Commit 3d6ffced by David Hotham Committed by GitHub

more typechecking (#5485)

clean up errors revealed by having a py.typed in poetry-core and running mypy in a proper environment

* more typechecking

* remove remaining `FooTypes` type annotations

just use the appropriate base class, no need for the extra complexity

* typecheck a couple more files

* more typechecking fixes

* more typechecking

* more typechecking

* tweak typing to appease pre-commit

* further mixology fixes

* consistency in repository package-finding

signal failure with `PackageNotFound`, rather than by returning None

* typechecking vcs.git.backend
parent 4d0b710d
...@@ -30,7 +30,8 @@ class FileConfigSource(ConfigSource): ...@@ -30,7 +30,8 @@ class FileConfigSource(ConfigSource):
return self._file return self._file
def add_property(self, key: str, value: Any) -> None: def add_property(self, key: str, value: Any) -> None:
with self.secure() as config: with self.secure() as toml:
config: dict[str, Any] = toml
keys = key.split(".") keys = key.split(".")
for i, key in enumerate(keys): for i, key in enumerate(keys):
...@@ -44,7 +45,8 @@ class FileConfigSource(ConfigSource): ...@@ -44,7 +45,8 @@ class FileConfigSource(ConfigSource):
config = config[key] config = config[key]
def remove_property(self, key: str) -> None: def remove_property(self, key: str) -> None:
with self.secure() as config: with self.secure() as toml:
config: dict[str, Any] = toml
keys = key.split(".") keys = key.split(".")
current_config = config current_config = config
......
...@@ -2,10 +2,12 @@ from __future__ import annotations ...@@ -2,10 +2,12 @@ from __future__ import annotations
import contextlib import contextlib
from typing import Any
from typing import cast from typing import cast
from cleo.helpers import argument from cleo.helpers import argument
from cleo.helpers import option from cleo.helpers import option
from tomlkit.toml_document import TOMLDocument
try: try:
...@@ -114,7 +116,9 @@ You can specify a package in the following forms: ...@@ -114,7 +116,9 @@ You can specify a package in the following forms:
"You can only specify one package when using the --extras option" "You can only specify one package when using the --extras option"
) )
content = self.poetry.file.read() # tomlkit types are awkward to work with, treat content as a mostly untyped
# dictionary.
content: dict[str, Any] = self.poetry.file.read()
poetry_content = content["tool"]["poetry"] poetry_content = content["tool"]["poetry"]
if group == MAIN_GROUP: if group == MAIN_GROUP:
...@@ -130,9 +134,10 @@ You can specify a package in the following forms: ...@@ -130,9 +134,10 @@ You can specify a package in the following forms:
groups = poetry_content["group"] groups = poetry_content["group"]
if group not in groups: if group not in groups:
group_table = parse_toml( dependencies_toml: dict[str, Any] = parse_toml(
f"[tool.poetry.group.{group}.dependencies]\n\n" f"[tool.poetry.group.{group}.dependencies]\n\n"
)["tool"]["poetry"]["group"][group] )
group_table = dependencies_toml["tool"]["poetry"]["group"][group]
poetry_content["group"][group] = group_table poetry_content["group"][group] = group_table
if "dependencies" not in poetry_content["group"][group]: if "dependencies" not in poetry_content["group"][group]:
...@@ -158,11 +163,13 @@ You can specify a package in the following forms: ...@@ -158,11 +163,13 @@ You can specify a package in the following forms:
) )
for _constraint in requirements: for _constraint in requirements:
if "version" in _constraint: version = _constraint.get("version")
if version is not None:
# Validate version constraint # Validate version constraint
parse_constraint(_constraint["version"]) assert isinstance(version, str)
parse_constraint(version)
constraint = inline_table() constraint: dict[str, Any] = inline_table()
for name, value in _constraint.items(): for name, value in _constraint.items():
if name == "name": if name == "name":
continue continue
...@@ -210,16 +217,18 @@ You can specify a package in the following forms: ...@@ -210,16 +217,18 @@ You can specify a package in the following forms:
if len(constraint) == 1 and "version" in constraint: if len(constraint) == 1 and "version" in constraint:
constraint = constraint["version"] constraint = constraint["version"]
section[_constraint["name"]] = constraint constraint_name = _constraint["name"]
assert isinstance(constraint_name, str)
section[constraint_name] = constraint
with contextlib.suppress(ValueError): with contextlib.suppress(ValueError):
self.poetry.package.dependency_group(group).remove_dependency( self.poetry.package.dependency_group(group).remove_dependency(
_constraint["name"] constraint_name
) )
self.poetry.package.add_dependency( self.poetry.package.add_dependency(
Factory.create_dependency( Factory.create_dependency(
_constraint["name"], constraint_name,
constraint, constraint,
groups=[group], groups=[group],
root_dir=self.poetry.file.parent, root_dir=self.poetry.file.parent,
...@@ -247,6 +256,7 @@ You can specify a package in the following forms: ...@@ -247,6 +256,7 @@ You can specify a package in the following forms:
status = self._installer.run() status = self._installer.run()
if status == 0 and not self.option("dry-run"): if status == 0 and not self.option("dry-run"):
assert isinstance(content, TOMLDocument)
self.poetry.file.write(content) self.poetry.file.write(content)
return status return status
......
...@@ -68,6 +68,7 @@ class DebugResolveCommand(InitCommand): ...@@ -68,6 +68,7 @@ class DebugResolveCommand(InitCommand):
for constraint in requirements: for constraint in requirements:
name = constraint.pop("name") name = constraint.pop("name")
assert isinstance(name, str)
extras = [] extras = []
for extra in self.option("extras"): for extra in self.option("extras"):
if " " in extra: if " " in extra:
......
...@@ -15,8 +15,7 @@ from poetry.console.commands.env_command import EnvCommand ...@@ -15,8 +15,7 @@ from poetry.console.commands.env_command import EnvCommand
if TYPE_CHECKING: if TYPE_CHECKING:
from cleo.io.inputs.option import Option from cleo.io.inputs.option import Option
from poetry.core.packages.project_package import ProjectPackage
from poetry.packages.project_package import ProjectPackage
class GroupCommand(EnvCommand): class GroupCommand(EnvCommand):
......
...@@ -2,6 +2,7 @@ from __future__ import annotations ...@@ -2,6 +2,7 @@ from __future__ import annotations
import os import os
from typing import Any
from typing import cast from typing import cast
from cleo.helpers import argument from cleo.helpers import argument
...@@ -78,7 +79,7 @@ You can specify a package in the following forms: ...@@ -78,7 +79,7 @@ You can specify a package in the following forms:
# We check for the plugins existence first. # We check for the plugins existence first.
if env_dir.joinpath("pyproject.toml").exists(): if env_dir.joinpath("pyproject.toml").exists():
pyproject = tomlkit.loads( pyproject: dict[str, Any] = tomlkit.loads(
env_dir.joinpath("pyproject.toml").read_text(encoding="utf-8") env_dir.joinpath("pyproject.toml").read_text(encoding="utf-8")
) )
poetry_content = pyproject["tool"]["poetry"] poetry_content = pyproject["tool"]["poetry"]
...@@ -128,8 +129,8 @@ You can specify a package in the following forms: ...@@ -128,8 +129,8 @@ You can specify a package in the following forms:
# We add the plugins to the dependencies section of the previously # We add the plugins to the dependencies section of the previously
# created `pyproject.toml` file # created `pyproject.toml` file
pyproject = PyProjectTOML(env_dir.joinpath("pyproject.toml")) pyproject_toml = PyProjectTOML(env_dir.joinpath("pyproject.toml"))
poetry_content = pyproject.poetry_config poetry_content = pyproject_toml.poetry_config
poetry_dependency_section = poetry_content["dependencies"] poetry_dependency_section = poetry_content["dependencies"]
plugin_names = [] plugin_names = []
for plugin in plugins: for plugin in plugins:
...@@ -137,7 +138,7 @@ You can specify a package in the following forms: ...@@ -137,7 +138,7 @@ You can specify a package in the following forms:
# Validate version constraint # Validate version constraint
parse_constraint(plugin["version"]) parse_constraint(plugin["version"])
constraint = tomlkit.inline_table() constraint: dict[str, Any] = tomlkit.inline_table()
for name, value in plugin.items(): for name, value in plugin.items():
if name == "name": if name == "name":
continue continue
...@@ -150,7 +151,7 @@ You can specify a package in the following forms: ...@@ -150,7 +151,7 @@ You can specify a package in the following forms:
poetry_dependency_section[plugin["name"]] = constraint poetry_dependency_section[plugin["name"]] = constraint
plugin_names.append(plugin["name"]) plugin_names.append(plugin["name"])
pyproject.save() pyproject_toml.save()
# From this point forward, all the logic will be deferred to # From this point forward, all the logic will be deferred to
# the update command, by using the previously created `pyproject.toml` # the update command, by using the previously created `pyproject.toml`
......
from __future__ import annotations from __future__ import annotations
from collections import defaultdict from collections import defaultdict
from typing import TYPE_CHECKING from typing import Any
from typing import DefaultDict
from poetry.console.commands.command import Command from poetry.console.commands.command import Command
if TYPE_CHECKING:
from poetry.core.packages.package import Package
class PluginShowCommand(Command): class PluginShowCommand(Command):
name = "plugin show" name = "plugin show"
...@@ -26,7 +21,7 @@ class PluginShowCommand(Command): ...@@ -26,7 +21,7 @@ class PluginShowCommand(Command):
from poetry.utils.helpers import canonicalize_name from poetry.utils.helpers import canonicalize_name
from poetry.utils.helpers import pluralize from poetry.utils.helpers import pluralize
plugins: DefaultDict[str, dict[str, Package | list[str]]] = defaultdict( plugins: dict[str, dict[str, Any]] = defaultdict(
lambda: { lambda: {
"package": None, "package": None,
"plugins": [], "plugins": [],
......
...@@ -4,6 +4,7 @@ from typing import Any ...@@ -4,6 +4,7 @@ from typing import Any
from cleo.helpers import argument from cleo.helpers import argument
from cleo.helpers import option from cleo.helpers import option
from tomlkit.toml_document import TOMLDocument
try: try:
...@@ -50,7 +51,7 @@ list of installed packages ...@@ -50,7 +51,7 @@ list of installed packages
else: else:
group = self.option("group", self.default_group) group = self.option("group", self.default_group)
content = self.poetry.file.read() content: dict[str, Any] = self.poetry.file.read()
poetry_content = content["tool"]["poetry"] poetry_content = content["tool"]["poetry"]
if group is None: if group is None:
...@@ -114,6 +115,7 @@ list of installed packages ...@@ -114,6 +115,7 @@ list of installed packages
status = self._installer.run() status = self._installer.run()
if not self.option("dry-run") and status == 0: if not self.option("dry-run") and status == 0:
assert isinstance(content, TOMLDocument)
self.poetry.file.write(content) self.poetry.file.write(content)
return status return status
......
from __future__ import annotations from __future__ import annotations
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from typing import cast
from cleo.helpers import argument from cleo.helpers import argument
from cleo.helpers import option from cleo.helpers import option
from poetry.core.packages.directory_dependency import DirectoryDependency
from poetry.core.packages.file_dependency import FileDependency
from poetry.core.packages.vcs_dependency import VCSDependency
from poetry.console.commands.group_command import GroupCommand from poetry.console.commands.group_command import GroupCommand
...@@ -12,8 +16,8 @@ if TYPE_CHECKING: ...@@ -12,8 +16,8 @@ if TYPE_CHECKING:
from cleo.io.io import IO from cleo.io.io import IO
from poetry.core.packages.dependency import Dependency from poetry.core.packages.dependency import Dependency
from poetry.core.packages.package import Package from poetry.core.packages.package import Package
from poetry.core.packages.project_package import ProjectPackage
from poetry.packages.project_package import ProjectPackage
from poetry.repositories.installed_repository import InstalledRepository from poetry.repositories.installed_repository import InstalledRepository
from poetry.repositories.repository import Repository from poetry.repositories.repository import Repository
...@@ -82,10 +86,10 @@ lists all packages available.""" ...@@ -82,10 +86,10 @@ lists all packages available."""
if self.option("tree") and not package: if self.option("tree") and not package:
requires = root.all_requires requires = root.all_requires
packages = locked_repo.packages packages = locked_repo.packages
for pkg in packages: for p in packages:
for require in requires: for require in requires:
if pkg.name == require.name: if p.name == require.name:
self.display_package_tree(self._io, pkg, locked_repo) self.display_package_tree(self._io, p, locked_repo)
break break
return 0 return 0
...@@ -383,7 +387,7 @@ lists all packages available.""" ...@@ -383,7 +387,7 @@ lists all packages available."""
def find_latest_package( def find_latest_package(
self, package: Package, root: ProjectPackage self, package: Package, root: ProjectPackage
) -> Package | bool: ) -> Package | None:
from cleo.io.null_io import NullIO from cleo.io.null_io import NullIO
from poetry.puzzle.provider import Provider from poetry.puzzle.provider import Provider
...@@ -398,10 +402,13 @@ lists all packages available.""" ...@@ -398,10 +402,13 @@ lists all packages available."""
provider = Provider(root, self.poetry.pool, NullIO()) provider = Provider(root, self.poetry.pool, NullIO())
if dep.is_vcs(): if dep.is_vcs():
dep = cast(VCSDependency, dep)
return provider.search_for_vcs(dep)[0] return provider.search_for_vcs(dep)[0]
if dep.is_file(): if dep.is_file():
dep = cast(FileDependency, dep)
return provider.search_for_file(dep)[0] return provider.search_for_file(dep)[0]
if dep.is_directory(): if dep.is_directory():
dep = cast(DirectoryDependency, dep)
return provider.search_for_directory(dep)[0] return provider.search_for_directory(dep)[0]
name = package.name name = package.name
......
from __future__ import annotations from __future__ import annotations
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from typing import Any
from cleo.helpers import argument from cleo.helpers import argument
from cleo.helpers import option from cleo.helpers import option
from tomlkit.toml_document import TOMLDocument
from poetry.console.commands.command import Command from poetry.console.commands.command import Command
...@@ -64,10 +66,11 @@ patch, minor, major, prepatch, preminor, premajor, prerelease. ...@@ -64,10 +66,11 @@ patch, minor, major, prepatch, preminor, premajor, prerelease.
f" to <fg=green>{version}</>" f" to <fg=green>{version}</>"
) )
content = self.poetry.file.read() content: dict[str, Any] = self.poetry.file.read()
poetry_content = content["tool"]["poetry"] poetry_content = content["tool"]["poetry"]
poetry_content["version"] = version.text poetry_content["version"] = version.text
assert isinstance(content, TOMLDocument)
self.poetry.file.write(content) self.poetry.file.write(content)
else: else:
if self.option("short"): if self.option("short"):
...@@ -100,7 +103,9 @@ patch, minor, major, prepatch, preminor, premajor, prerelease. ...@@ -100,7 +103,9 @@ patch, minor, major, prepatch, preminor, premajor, prerelease.
new = new.first_prerelease() new = new.first_prerelease()
elif rule == "prerelease": elif rule == "prerelease":
if parsed.is_unstable(): if parsed.is_unstable():
new = Version(parsed.epoch, parsed.release, parsed.pre.next()) pre = parsed.pre
assert pre is not None
new = Version(parsed.epoch, parsed.release, pre.next())
else: else:
new = parsed.next_patch().first_prerelease() new = parsed.next_patch().first_prerelease()
else: else:
......
...@@ -2,10 +2,14 @@ from __future__ import annotations ...@@ -2,10 +2,14 @@ from __future__ import annotations
from pathlib import Path from pathlib import Path
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from typing import Any
from typing import cast
from cleo.io.null_io import NullIO from cleo.io.null_io import NullIO
from poetry.core.factory import Factory as BaseFactory from poetry.core.factory import Factory as BaseFactory
from poetry.core.packages.vcs_dependency import VCSDependency
from poetry.core.toml.file import TOMLFile from poetry.core.toml.file import TOMLFile
from tomlkit.toml_document import TOMLDocument
from poetry.config.config import Config from poetry.config.config import Config
from poetry.config.file_config_source import FileConfigSource from poetry.config.file_config_source import FileConfigSource
...@@ -183,7 +187,7 @@ class Factory(BaseFactory): ...@@ -183,7 +187,7 @@ class Factory(BaseFactory):
from poetry.layouts.layout import POETRY_DEFAULT from poetry.layouts.layout import POETRY_DEFAULT
pyproject = tomlkit.loads(POETRY_DEFAULT) pyproject: dict[str, Any] = tomlkit.loads(POETRY_DEFAULT)
content = pyproject["tool"]["poetry"] content = pyproject["tool"]["poetry"]
content["name"] = package.name content["name"] = package.name
...@@ -195,8 +199,9 @@ class Factory(BaseFactory): ...@@ -195,8 +199,9 @@ class Factory(BaseFactory):
dependency_section["python"] = package.python_versions dependency_section["python"] = package.python_versions
for dep in package.requires: for dep in package.requires:
constraint = tomlkit.inline_table() constraint: dict[str, Any] = tomlkit.inline_table()
if dep.is_vcs(): if dep.is_vcs():
dep = cast(VCSDependency, dep)
constraint[dep.vcs] = dep.source_url constraint[dep.vcs] = dep.source_url
if dep.reference: if dep.reference:
...@@ -217,6 +222,7 @@ class Factory(BaseFactory): ...@@ -217,6 +222,7 @@ class Factory(BaseFactory):
dependency_section[dep.name] = constraint dependency_section[dep.name] = constraint
assert isinstance(pyproject, TOMLDocument)
path.joinpath("pyproject.toml").write_text( path.joinpath("pyproject.toml").write_text(
pyproject.as_string(), encoding="utf-8" pyproject.as_string(), encoding="utf-8"
) )
...@@ -34,11 +34,10 @@ if TYPE_CHECKING: ...@@ -34,11 +34,10 @@ if TYPE_CHECKING:
from poetry.core.packages.package import Package from poetry.core.packages.package import Package
from poetry.config.config import Config from poetry.config.config import Config
from poetry.installation.operations import OperationTypes from poetry.installation.operations import Install
from poetry.installation.operations.install import Install from poetry.installation.operations import Uninstall
from poetry.installation.operations import Update
from poetry.installation.operations.operation import Operation from poetry.installation.operations.operation import Operation
from poetry.installation.operations.uninstall import Uninstall
from poetry.installation.operations.update import Update
from poetry.repositories import Pool from poetry.repositories import Pool
from poetry.utils.env import Env from poetry.utils.env import Env
...@@ -127,7 +126,7 @@ class Executor: ...@@ -127,7 +126,7 @@ class Executor:
return 0 return 0
def execute(self, operations: list[OperationTypes]) -> int: def execute(self, operations: list[Operation]) -> int:
self._total_operations = len(operations) self._total_operations = len(operations)
for job_type in self._executed: for job_type in self._executed:
self._executed[job_type] = 0 self._executed[job_type] = 0
...@@ -195,7 +194,7 @@ class Executor: ...@@ -195,7 +194,7 @@ class Executor:
return default_max_workers return default_max_workers
return min(default_max_workers, desired_max_workers) return min(default_max_workers, desired_max_workers)
def _write(self, operation: OperationTypes, line: str) -> None: def _write(self, operation: Operation, line: str) -> None:
if not self.supports_fancy_output() or not self._should_write_operation( if not self.supports_fancy_output() or not self._should_write_operation(
operation operation
): ):
...@@ -213,7 +212,7 @@ class Executor: ...@@ -213,7 +212,7 @@ class Executor:
section.clear() section.clear()
section.write(line) section.write(line)
def _execute_operation(self, operation: OperationTypes) -> None: def _execute_operation(self, operation: Operation) -> None:
try: try:
op_message = self.get_operation_message(operation) op_message = self.get_operation_message(operation)
if self.supports_fancy_output(): if self.supports_fancy_output():
...@@ -290,7 +289,7 @@ class Executor: ...@@ -290,7 +289,7 @@ class Executor:
with self._lock: with self._lock:
self._shutdown = True self._shutdown = True
def _do_execute_operation(self, operation: OperationTypes) -> int: def _do_execute_operation(self, operation: Operation) -> int:
method = operation.job_type method = operation.job_type
operation_message = self.get_operation_message(operation) operation_message = self.get_operation_message(operation)
...@@ -326,9 +325,7 @@ class Executor: ...@@ -326,9 +325,7 @@ class Executor:
return result return result
def _increment_operations_count( def _increment_operations_count(self, operation: Operation, executed: bool) -> None:
self, operation: OperationTypes, executed: bool
) -> None:
with self._lock: with self._lock:
if executed: if executed:
self._executed_operations += 1 self._executed_operations += 1
...@@ -353,7 +350,7 @@ class Executor: ...@@ -353,7 +350,7 @@ class Executor:
def get_operation_message( def get_operation_message(
self, self,
operation: OperationTypes, operation: Operation,
done: bool = False, done: bool = False,
error: bool = False, error: bool = False,
warning: bool = False, warning: bool = False,
...@@ -401,7 +398,7 @@ class Executor: ...@@ -401,7 +398,7 @@ class Executor:
) )
return "" return ""
def _display_summary(self, operations: list[OperationTypes]) -> None: def _display_summary(self, operations: list[Operation]) -> None:
installs = 0 installs = 0
updates = 0 updates = 0
uninstalls = 0 uninstalls = 0
...@@ -707,7 +704,7 @@ class Executor: ...@@ -707,7 +704,7 @@ class Executor:
def _should_write_operation(self, operation: Operation) -> bool: def _should_write_operation(self, operation: Operation) -> bool:
return not operation.skipped or self._dry_run or self._verbose return not operation.skipped or self._dry_run or self._verbose
def _save_url_reference(self, operation: OperationTypes) -> None: def _save_url_reference(self, operation: Operation) -> None:
""" """
Create and store a PEP-610 `direct_url.json` file, if needed. Create and store a PEP-610 `direct_url.json` file, if needed.
""" """
......
...@@ -25,7 +25,6 @@ if TYPE_CHECKING: ...@@ -25,7 +25,6 @@ if TYPE_CHECKING:
from poetry.config.config import Config from poetry.config.config import Config
from poetry.installation.base_installer import BaseInstaller from poetry.installation.base_installer import BaseInstaller
from poetry.installation.operations import OperationTypes
from poetry.installation.operations.operation import Operation from poetry.installation.operations.operation import Operation
from poetry.packages import Locker from poetry.packages import Locker
from poetry.utils.env import Env from poetry.utils.env import Env
...@@ -345,7 +344,7 @@ class Installer: ...@@ -345,7 +344,7 @@ class Installer:
self._io.write_line("") self._io.write_line("")
self._io.write_line("<info>Writing lock file</>") self._io.write_line("<info>Writing lock file</>")
def _execute(self, operations: list[OperationTypes]) -> int: def _execute(self, operations: list[Operation]) -> int:
if self._use_executor: if self._use_executor:
return self._executor.execute(operations) return self._executor.execute(operations)
......
...@@ -12,7 +12,7 @@ if TYPE_CHECKING: ...@@ -12,7 +12,7 @@ if TYPE_CHECKING:
class NoopInstaller(BaseInstaller): class NoopInstaller(BaseInstaller):
def __init__(self) -> None: def __init__(self) -> None:
self._installs: list[Package] = [] self._installs: list[Package] = []
self._updates: list[Package] = [] self._updates: list[tuple[Package, Package]] = []
self._removals: list[Package] = [] self._removals: list[Package] = []
@property @property
...@@ -20,7 +20,7 @@ class NoopInstaller(BaseInstaller): ...@@ -20,7 +20,7 @@ class NoopInstaller(BaseInstaller):
return self._installs return self._installs
@property @property
def updates(self) -> list[Package]: def updates(self) -> list[tuple[Package, Package]]:
return self._updates return self._updates
@property @property
......
...@@ -7,4 +7,4 @@ from poetry.installation.operations.uninstall import Uninstall ...@@ -7,4 +7,4 @@ from poetry.installation.operations.uninstall import Uninstall
from poetry.installation.operations.update import Update from poetry.installation.operations.update import Update
OperationTypes = Union[Install, Uninstall, Update] __all__ = ["Install", "Uninstall", "Update"]
...@@ -2,11 +2,13 @@ from __future__ import annotations ...@@ -2,11 +2,13 @@ from __future__ import annotations
from pathlib import Path from pathlib import Path
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from typing import Any
from tomlkit import dumps from tomlkit import dumps
from tomlkit import inline_table from tomlkit import inline_table
from tomlkit import loads from tomlkit import loads
from tomlkit import table from tomlkit import table
from tomlkit.toml_document import TOMLDocument
from poetry.utils.helpers import canonicalize_name from poetry.utils.helpers import canonicalize_name
from poetry.utils.helpers import module_name from poetry.utils.helpers import module_name
...@@ -115,7 +117,7 @@ class Layout: ...@@ -115,7 +117,7 @@ class Layout:
def generate_poetry_content(self, original: PyProjectTOML | None = None) -> str: def generate_poetry_content(self, original: PyProjectTOML | None = None) -> str:
template = POETRY_DEFAULT template = POETRY_DEFAULT
content = loads(template) content: dict[str, Any] = loads(template)
poetry_content = content["tool"]["poetry"] poetry_content = content["tool"]["poetry"]
poetry_content["name"] = self._project poetry_content["name"] = self._project
...@@ -162,14 +164,15 @@ class Layout: ...@@ -162,14 +164,15 @@ class Layout:
build_system.add("requires", ["poetry-core" + build_system_version]) build_system.add("requires", ["poetry-core" + build_system_version])
build_system.add("build-backend", "poetry.core.masonry.api") build_system.add("build-backend", "poetry.core.masonry.api")
assert isinstance(content, TOMLDocument)
content.add("build-system", build_system) content.add("build-system", build_system)
content = dumps(content) text = dumps(content)
if original and original.file.exists(): if original and original.file.exists():
content = dumps(original.data) + "\n" + content text = dumps(original.data) + "\n" + text
return content return text
def _create_default(self, path: Path, src: bool = True) -> None: def _create_default(self, path: Path, src: bool = True) -> None:
package_path = path / self.package_path package_path = path / self.package_path
......
...@@ -74,7 +74,7 @@ class EditableBuilder(Builder): ...@@ -74,7 +74,7 @@ class EditableBuilder(Builder):
added_files += self._add_scripts() added_files += self._add_scripts()
self._add_dist_info(added_files) self._add_dist_info(added_files)
def _run_build_script(self, build_script: Path) -> None: def _run_build_script(self, build_script: str) -> None:
self._debug(f" - Executing build script: <b>{build_script}</b>") self._debug(f" - Executing build script: <b>{build_script}</b>")
self._env.run("python", str(self._path.joinpath(build_script)), call=True) self._env.run("python", str(self._path.joinpath(build_script)), call=True)
......
...@@ -16,8 +16,8 @@ if TYPE_CHECKING: ...@@ -16,8 +16,8 @@ if TYPE_CHECKING:
def resolve_version( def resolve_version(
root: ProjectPackage, root: ProjectPackage,
provider: Provider, provider: Provider,
locked: dict[str, list[DependencyPackage]] = None, locked: dict[str, list[DependencyPackage]] | None = None,
use_latest: list[str] = None, use_latest: list[str] | None = None,
) -> SolverResult: ) -> SolverResult:
solver = VersionSolver(root, provider, locked=locked, use_latest=use_latest) solver = VersionSolver(root, provider, locked=locked, use_latest=use_latest)
......
...@@ -9,7 +9,7 @@ from poetry.mixology.set_relation import SetRelation ...@@ -9,7 +9,7 @@ from poetry.mixology.set_relation import SetRelation
if TYPE_CHECKING: if TYPE_CHECKING:
from poetry.core.packages.dependency import Dependency from poetry.core.packages.dependency import Dependency
from poetry.core.semver.helpers import VersionTypes from poetry.core.semver.version_constraint import VersionConstraint
class Term: class Term:
...@@ -33,7 +33,7 @@ class Term: ...@@ -33,7 +33,7 @@ class Term:
return self._dependency return self._dependency
@property @property
def constraint(self) -> VersionTypes: def constraint(self) -> VersionConstraint:
return self._dependency.constraint return self._dependency.constraint
def is_positive(self) -> bool: def is_positive(self) -> bool:
...@@ -159,7 +159,7 @@ class Term: ...@@ -159,7 +159,7 @@ class Term:
) )
def _non_empty_term( def _non_empty_term(
self, constraint: VersionTypes, is_positive: bool self, constraint: VersionConstraint, is_positive: bool
) -> Term | None: ) -> Term | None:
if constraint.is_empty(): if constraint.is_empty():
return None return None
......
...@@ -22,7 +22,6 @@ from poetry.packages import DependencyPackage ...@@ -22,7 +22,6 @@ from poetry.packages import DependencyPackage
if TYPE_CHECKING: if TYPE_CHECKING:
from poetry.core.packages.package import Package
from poetry.core.packages.project_package import ProjectPackage from poetry.core.packages.project_package import ProjectPackage
from poetry.puzzle.provider import Provider from poetry.puzzle.provider import Provider
...@@ -42,7 +41,7 @@ class DependencyCache: ...@@ -42,7 +41,7 @@ class DependencyCache:
def __init__(self, provider: Provider): def __init__(self, provider: Provider):
self.provider = provider self.provider = provider
self.cache: dict[str, list[Package]] = {} self.cache: dict[str, list[DependencyPackage]] = {}
@functools.lru_cache(maxsize=128) @functools.lru_cache(maxsize=128)
def search_for(self, dependency: Dependency) -> list[DependencyPackage]: def search_for(self, dependency: Dependency) -> list[DependencyPackage]:
...@@ -74,8 +73,8 @@ class VersionSolver: ...@@ -74,8 +73,8 @@ class VersionSolver:
self, self,
root: ProjectPackage, root: ProjectPackage,
provider: Provider, provider: Provider,
locked: dict[str, list[Package]] = None, locked: dict[str, list[DependencyPackage]] | None = None,
use_latest: list[str] = None, use_latest: list[str] | None = None,
): ):
self._root = root self._root = root
self._provider = provider self._provider = provider
...@@ -109,7 +108,7 @@ class VersionSolver: ...@@ -109,7 +108,7 @@ class VersionSolver:
) )
try: try:
next = self._root.name next: str | None = self._root.name
while next is not None: while next is not None:
self._propagate(next) self._propagate(next)
next = self._choose_package_version() next = self._choose_package_version()
...@@ -450,7 +449,7 @@ class VersionSolver: ...@@ -450,7 +449,7 @@ class VersionSolver:
) )
if not conflict: if not conflict:
self._solution.decide(package) self._solution.decide(package.package)
self._log( self._log(
f"selecting {package.complete_name} ({package.full_pretty_version})" f"selecting {package.complete_name} ({package.full_pretty_version})"
) )
...@@ -494,7 +493,7 @@ class VersionSolver: ...@@ -494,7 +493,7 @@ class VersionSolver:
locked = self._locked.get(dependency.name, []) locked = self._locked.get(dependency.name, [])
for package in locked: for package in locked:
if (allow_similar or dependency.is_same_package_as(package)) and ( if (allow_similar or dependency.is_same_package_as(package.package)) and (
dependency.constraint.allows(package.version) dependency.constraint.allows(package.version)
or package.is_prerelease() or package.is_prerelease()
and dependency.constraint.allows(package.version.next_patch()) and dependency.constraint.allows(package.version.next_patch())
......
...@@ -3,3 +3,6 @@ from __future__ import annotations ...@@ -3,3 +3,6 @@ from __future__ import annotations
from poetry.packages.dependency_package import DependencyPackage from poetry.packages.dependency_package import DependencyPackage
from poetry.packages.locker import Locker from poetry.packages.locker import Locker
from poetry.packages.package_collection import PackageCollection from poetry.packages.package_collection import PackageCollection
__all__ = ["DependencyPackage", "Locker", "PackageCollection"]
...@@ -49,7 +49,7 @@ class DependencyPackage: ...@@ -49,7 +49,7 @@ class DependencyPackage:
def __hash__(self) -> int: def __hash__(self) -> int:
return hash(self._package) return hash(self._package)
def __eq__(self, other: Package | DependencyPackage) -> bool: def __eq__(self, other: object) -> bool:
if isinstance(other, DependencyPackage): if isinstance(other, DependencyPackage):
other = other.package other = other.package
......
...@@ -9,18 +9,18 @@ from copy import deepcopy ...@@ -9,18 +9,18 @@ from copy import deepcopy
from hashlib import sha256 from hashlib import sha256
from pathlib import Path from pathlib import Path
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from typing import Any
from typing import Iterable from typing import Iterable
from typing import Iterator from typing import Iterator
from typing import Sequence from typing import Sequence
from typing import cast
from poetry.core.packages.dependency import Dependency from poetry.core.packages.dependency import Dependency
from poetry.core.packages.directory_dependency import DirectoryDependency
from poetry.core.packages.file_dependency import FileDependency
try:
from poetry.core.packages.dependency_group import MAIN_GROUP
except ImportError:
MAIN_GROUP = "default"
from poetry.core.packages.package import Package from poetry.core.packages.package import Package
from poetry.core.packages.url_dependency import URLDependency
from poetry.core.packages.vcs_dependency import VCSDependency
from poetry.core.semver.helpers import parse_constraint from poetry.core.semver.helpers import parse_constraint
from poetry.core.semver.version import Version from poetry.core.semver.version import Version
from poetry.core.toml.file import TOMLFile from poetry.core.toml.file import TOMLFile
...@@ -31,7 +31,9 @@ from tomlkit import document ...@@ -31,7 +31,9 @@ from tomlkit import document
from tomlkit import inline_table from tomlkit import inline_table
from tomlkit import item from tomlkit import item
from tomlkit import table from tomlkit import table
from tomlkit.container import Table
from tomlkit.exceptions import TOMLKitError from tomlkit.exceptions import TOMLKitError
from tomlkit.items import Array
from poetry.packages import DependencyPackage from poetry.packages import DependencyPackage
from poetry.utils.extras import get_extra_package_names from poetry.utils.extras import get_extra_package_names
...@@ -39,7 +41,6 @@ from poetry.utils.extras import get_extra_package_names ...@@ -39,7 +41,6 @@ from poetry.utils.extras import get_extra_package_names
if TYPE_CHECKING: if TYPE_CHECKING:
from poetry.core.version.markers import BaseMarker from poetry.core.version.markers import BaseMarker
from tomlkit.items import InlineTable
from tomlkit.toml_document import TOMLDocument from tomlkit.toml_document import TOMLDocument
from poetry.repositories import Repository from poetry.repositories import Repository
...@@ -57,7 +58,7 @@ class Locker: ...@@ -57,7 +58,7 @@ class Locker:
def __init__(self, lock: str | Path, local_config: dict) -> None: def __init__(self, lock: str | Path, local_config: dict) -> None:
self._lock = TOMLFile(lock) self._lock = TOMLFile(lock)
self._local_config = local_config self._local_config = local_config
self._lock_data = None self._lock_data: TOMLDocument | None = None
self._content_hash = self._get_content_hash() self._content_hash = self._get_content_hash()
@property @property
...@@ -88,7 +89,7 @@ class Locker: ...@@ -88,7 +89,7 @@ class Locker:
metadata = lock.get("metadata", {}) metadata = lock.get("metadata", {})
if "content-hash" in metadata: if "content-hash" in metadata:
return self._content_hash == lock["metadata"]["content-hash"] return self._content_hash == metadata["content-hash"]
return False return False
...@@ -104,7 +105,7 @@ class Locker: ...@@ -104,7 +105,7 @@ class Locker:
lock_data = self.lock_data lock_data = self.lock_data
packages = Repository() packages = Repository()
locked_packages = lock_data["package"] locked_packages = cast("list[dict[str, Any]]", lock_data["package"])
if not locked_packages: if not locked_packages:
return packages return packages
...@@ -127,16 +128,16 @@ class Locker: ...@@ -127,16 +128,16 @@ class Locker:
) )
package.description = info.get("description", "") package.description = info.get("description", "")
package.category = info.get("category", "main") package.category = info.get("category", "main")
package.groups = info.get("groups", [MAIN_GROUP])
package.optional = info["optional"] package.optional = info["optional"]
if "hashes" in lock_data["metadata"]: metadata = cast("dict[str, Any]", lock_data["metadata"])
name = info["name"]
if "hashes" in metadata:
# Old lock so we create dummy files from the hashes # Old lock so we create dummy files from the hashes
package.files = [ hashes = cast("dict[str, Any]", metadata["hashes"])
{"name": h, "hash": h} package.files = [{"name": h, "hash": h} for h in hashes[name]]
for h in lock_data["metadata"]["hashes"][info["name"]]
]
else: else:
package.files = lock_data["metadata"]["files"][info["name"]] files = metadata["files"][name]
package.files = files
package.python_versions = info["python-versions"] package.python_versions = info["python-versions"]
extras = info.get("extras", {}) extras = info.get("extras", {})
...@@ -182,6 +183,7 @@ class Locker: ...@@ -182,6 +183,7 @@ class Locker:
if package.source_type == "directory": if package.source_type == "directory":
# root dir should be the source of the package relative to the lock # root dir should be the source of the package relative to the lock
# path # path
assert package.source_url is not None
root_dir = Path(package.source_url) root_dir = Path(package.source_url)
if isinstance(constraint, list): if isinstance(constraint, list):
...@@ -368,10 +370,10 @@ class Locker: ...@@ -368,10 +370,10 @@ class Locker:
yield DependencyPackage(dependency=dependency, package=package) yield DependencyPackage(dependency=dependency, package=package)
def set_lock_data(self, root: Package, packages: list[Package]) -> bool: def set_lock_data(self, root: Package, packages: list[Package]) -> bool:
files = table() files: dict[str, Any] = table()
packages = self._lock_packages(packages) package_specs = self._lock_packages(packages)
# Retrieving hashes # Retrieving hashes
for package in packages: for package in package_specs:
if package["name"] not in files: if package["name"] not in files:
files[package["name"]] = [] files[package["name"]] = []
...@@ -383,12 +385,14 @@ class Locker: ...@@ -383,12 +385,14 @@ class Locker:
files[package["name"]].append(file_metadata) files[package["name"]].append(file_metadata)
if files[package["name"]]: if files[package["name"]]:
files[package["name"]] = item(files[package["name"]]).multiline(True) package_files = item(files[package["name"]])
assert isinstance(package_files, Array)
files[package["name"]] = package_files.multiline(True)
del package["files"] del package["files"]
lock = document() lock = document()
lock["package"] = packages lock["package"] = package_specs
if root.extras: if root.extras:
lock["extras"] = { lock["extras"] = {
...@@ -445,7 +449,8 @@ class Locker: ...@@ -445,7 +449,8 @@ class Locker:
except TOMLKitError as e: except TOMLKitError as e:
raise RuntimeError(f"Unable to read the lock file ({e}).") raise RuntimeError(f"Unable to read the lock file ({e}).")
lock_version = Version.parse(lock_data["metadata"].get("lock-version", "1.0")) metadata = cast(Table, lock_data["metadata"])
lock_version = Version.parse(metadata.get("lock-version", "1.0"))
current_version = Version.parse(self._VERSION) current_version = Version.parse(self._VERSION)
# We expect the locker to be able to read lock files # We expect the locker to be able to read lock files
# from the same semantic versioning range # from the same semantic versioning range
...@@ -469,7 +474,7 @@ class Locker: ...@@ -469,7 +474,7 @@ class Locker:
return lock_data return lock_data
def _lock_packages(self, packages: list[Package]) -> list: def _lock_packages(self, packages: list[Package]) -> list[dict[str, Any]]:
locked = [] locked = []
for package in sorted(packages, key=lambda x: (x.name, x.version)): for package in sorted(packages, key=lambda x: (x.name, x.version)):
...@@ -479,22 +484,31 @@ class Locker: ...@@ -479,22 +484,31 @@ class Locker:
return locked return locked
def _dump_package(self, package: Package) -> dict: def _dump_package(self, package: Package) -> dict[str, Any]:
dependencies: dict[str, list[InlineTable]] = {} dependencies: dict[str, list[Any]] = {}
for dependency in sorted(package.requires, key=lambda d: d.name): for dependency in sorted(package.requires, key=lambda d: d.name):
if dependency.pretty_name not in dependencies: if dependency.pretty_name not in dependencies:
dependencies[dependency.pretty_name] = [] dependencies[dependency.pretty_name] = []
constraint = inline_table() constraint = inline_table()
if dependency.is_directory() or dependency.is_file(): if dependency.is_directory():
dependency = cast(DirectoryDependency, dependency)
constraint["path"] = dependency.path.as_posix() constraint["path"] = dependency.path.as_posix()
if dependency.is_directory() and dependency.develop: if dependency.develop:
constraint["develop"] = True constraint["develop"] = True
elif dependency.is_file():
dependency = cast(FileDependency, dependency)
constraint["path"] = dependency.path.as_posix()
elif dependency.is_url(): elif dependency.is_url():
dependency = cast(URLDependency, dependency)
constraint["url"] = dependency.url constraint["url"] = dependency.url
elif dependency.is_vcs(): elif dependency.is_vcs():
dependency = cast(VCSDependency, dependency)
constraint[dependency.vcs] = dependency.source constraint[dependency.vcs] = dependency.source
if dependency.branch: if dependency.branch:
...@@ -519,16 +533,16 @@ class Locker: ...@@ -519,16 +533,16 @@ class Locker:
# All the constraints should have the same type, # All the constraints should have the same type,
# but we want to simplify them if it's possible # but we want to simplify them if it's possible
for dependency, constraints in tuple(dependencies.items()): for dependency_name, constraints in dependencies.items():
if all( if all(
len(constraint) == 1 and "version" in constraint len(constraint) == 1 and "version" in constraint
for constraint in constraints for constraint in constraints
): ):
dependencies[dependency] = [ dependencies[dependency_name] = [
constraint["version"] for constraint in constraints constraint["version"] for constraint in constraints
] ]
data = { data: dict[str, Any] = {
"name": package.pretty_name, "name": package.pretty_name,
"version": package.pretty_version, "version": package.pretty_version,
"description": package.description or "", "description": package.description or "",
......
from __future__ import annotations from __future__ import annotations
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from typing import Sequence
from poetry.packages.dependency_package import DependencyPackage from poetry.packages.dependency_package import DependencyPackage
...@@ -14,7 +15,7 @@ class PackageCollection(list): ...@@ -14,7 +15,7 @@ class PackageCollection(list):
def __init__( def __init__(
self, self,
dependency: Dependency, dependency: Dependency,
packages: list[Package | DependencyPackage] = None, packages: Sequence[Package | DependencyPackage] | None = None,
) -> None: ) -> None:
self._dependency = dependency self._dependency = dependency
......
...@@ -15,10 +15,10 @@ from poetry.core.utils.helpers import normalize_version ...@@ -15,10 +15,10 @@ from poetry.core.utils.helpers import normalize_version
from requests import adapters from requests import adapters
from requests.exceptions import ConnectionError from requests.exceptions import ConnectionError
from requests.exceptions import HTTPError from requests.exceptions import HTTPError
from requests.packages.urllib3 import util
from requests_toolbelt import user_agent from requests_toolbelt import user_agent
from requests_toolbelt.multipart import MultipartEncoder from requests_toolbelt.multipart import MultipartEncoder
from requests_toolbelt.multipart import MultipartEncoderMonitor from requests_toolbelt.multipart import MultipartEncoderMonitor
from urllib3 import util
from poetry.__version__ import __version__ from poetry.__version__ import __version__
from poetry.utils.patterns import wheel_file_re from poetry.utils.patterns import wheel_file_re
...@@ -327,7 +327,7 @@ class Uploader: ...@@ -327,7 +327,7 @@ class Uploader:
return resp return resp
def _prepare_data(self, data: dict) -> list[tuple[str, str]]: def _prepare_data(self, data: dict[str, Any]) -> list[tuple[str, str]]:
data_to_send = [] data_to_send = []
for key, value in data.items(): for key, value in data.items():
if not isinstance(value, (list, tuple)): if not isinstance(value, (list, tuple)):
......
...@@ -4,7 +4,10 @@ from typing import TYPE_CHECKING ...@@ -4,7 +4,10 @@ from typing import TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from poetry.core.packages.dependency import Dependency
from poetry.mixology.failure import SolveFailure from poetry.mixology.failure import SolveFailure
from poetry.packages import DependencyPackage
class SolverProblemError(Exception): class SolverProblemError(Exception):
...@@ -19,9 +22,11 @@ class SolverProblemError(Exception): ...@@ -19,9 +22,11 @@ class SolverProblemError(Exception):
class OverrideNeeded(Exception): class OverrideNeeded(Exception):
def __init__(self, *overrides: dict) -> None: def __init__(
self, *overrides: dict[DependencyPackage, dict[str, Dependency]]
) -> None:
self._overrides = overrides self._overrides = overrides
@property @property
def overrides(self) -> tuple[dict, ...]: def overrides(self) -> tuple[dict[DependencyPackage, dict[str, Dependency]], ...]:
return self._overrides return self._overrides
...@@ -15,9 +15,14 @@ from typing import TYPE_CHECKING ...@@ -15,9 +15,14 @@ from typing import TYPE_CHECKING
from typing import Any from typing import Any
from typing import Iterable from typing import Iterable
from typing import Iterator from typing import Iterator
from typing import cast
from cleo.ui.progress_indicator import ProgressIndicator from cleo.ui.progress_indicator import ProgressIndicator
from poetry.core.packages.directory_dependency import DirectoryDependency
from poetry.core.packages.file_dependency import FileDependency
from poetry.core.packages.url_dependency import URLDependency
from poetry.core.packages.utils.utils import get_python_constraint_from_marker from poetry.core.packages.utils.utils import get_python_constraint_from_marker
from poetry.core.packages.vcs_dependency import VCSDependency
from poetry.core.semver.empty_constraint import EmptyConstraint from poetry.core.semver.empty_constraint import EmptyConstraint
from poetry.core.semver.version import Version from poetry.core.semver.version import Version
from poetry.core.version.markers import AnyMarker from poetry.core.version.markers import AnyMarker
...@@ -38,11 +43,7 @@ from poetry.vcs.git import Git ...@@ -38,11 +43,7 @@ from poetry.vcs.git import Git
if TYPE_CHECKING: if TYPE_CHECKING:
from poetry.core.packages.dependency import Dependency from poetry.core.packages.dependency import Dependency
from poetry.core.packages.directory_dependency import DirectoryDependency
from poetry.core.packages.file_dependency import FileDependency
from poetry.core.packages.package import Package from poetry.core.packages.package import Package
from poetry.core.packages.url_dependency import URLDependency
from poetry.core.packages.vcs_dependency import VCSDependency
from poetry.core.semver.version_constraint import VersionConstraint from poetry.core.semver.version_constraint import VersionConstraint
from poetry.core.version.markers import BaseMarker from poetry.core.version.markers import BaseMarker
...@@ -117,7 +118,9 @@ class Provider: ...@@ -117,7 +118,9 @@ class Provider:
def is_debugging(self) -> bool: def is_debugging(self) -> bool:
return self._is_debugging return self._is_debugging
def set_overrides(self, overrides: dict) -> None: def set_overrides(
self, overrides: dict[DependencyPackage, dict[str, Dependency]]
) -> None:
self._overrides = overrides self._overrides = overrides
def load_deferred(self, load_deferred: bool) -> None: def load_deferred(self, load_deferred: bool) -> None:
...@@ -176,12 +179,16 @@ class Provider: ...@@ -176,12 +179,16 @@ class Provider:
return PackageCollection(dependency, [self._package]) return PackageCollection(dependency, [self._package])
if dependency.is_vcs(): if dependency.is_vcs():
dependency = cast(VCSDependency, dependency)
packages = self.search_for_vcs(dependency) packages = self.search_for_vcs(dependency)
elif dependency.is_file(): elif dependency.is_file():
dependency = cast(FileDependency, dependency)
packages = self.search_for_file(dependency) packages = self.search_for_file(dependency)
elif dependency.is_directory(): elif dependency.is_directory():
dependency = cast(DirectoryDependency, dependency)
packages = self.search_for_directory(dependency) packages = self.search_for_directory(dependency)
elif dependency.is_url(): elif dependency.is_url():
dependency = cast(URLDependency, dependency)
packages = self.search_for_url(dependency) packages = self.search_for_url(dependency)
else: else:
packages = self._pool.find_packages(dependency) packages = self._pool.find_packages(dependency)
...@@ -249,7 +256,7 @@ class Provider: ...@@ -249,7 +256,7 @@ class Provider:
def search_for_file(self, dependency: FileDependency) -> list[Package]: def search_for_file(self, dependency: FileDependency) -> list[Package]:
if dependency in self._deferred_cache: if dependency in self._deferred_cache:
dependency, _package = self._deferred_cache[dependency] _package = self._deferred_cache[dependency]
package = _package.clone() package = _package.clone()
else: else:
...@@ -258,7 +265,7 @@ class Provider: ...@@ -258,7 +265,7 @@ class Provider:
dependency._constraint = package.version dependency._constraint = package.version
dependency._pretty_constraint = package.version.text dependency._pretty_constraint = package.version.text
self._deferred_cache[dependency] = (dependency, package) self._deferred_cache[dependency] = package
self.validate_package_for_dependency(dependency=dependency, package=package) self.validate_package_for_dependency(dependency=dependency, package=package)
...@@ -286,7 +293,7 @@ class Provider: ...@@ -286,7 +293,7 @@ class Provider:
def search_for_directory(self, dependency: DirectoryDependency) -> list[Package]: def search_for_directory(self, dependency: DirectoryDependency) -> list[Package]:
if dependency in self._deferred_cache: if dependency in self._deferred_cache:
dependency, _package = self._deferred_cache[dependency] _package = self._deferred_cache[dependency]
package = _package.clone() package = _package.clone()
else: else:
...@@ -295,7 +302,7 @@ class Provider: ...@@ -295,7 +302,7 @@ class Provider:
dependency._constraint = package.version dependency._constraint = package.version
dependency._pretty_constraint = package.version.text dependency._pretty_constraint = package.version.text
self._deferred_cache[dependency] = (dependency, package) self._deferred_cache[dependency] = package
self.validate_package_for_dependency(dependency=dependency, package=package) self.validate_package_for_dependency(dependency=dependency, package=package)
...@@ -665,7 +672,7 @@ class Provider: ...@@ -665,7 +672,7 @@ class Provider:
_deps.append(inverted_marker_dep) _deps.append(inverted_marker_dep)
overrides = [] overrides = []
overrides_marker_intersection = AnyMarker() overrides_marker_intersection: BaseMarker = AnyMarker()
for dep_overrides in self._overrides.values(): for dep_overrides in self._overrides.values():
for _dep in dep_overrides.values(): for _dep in dep_overrides.values():
overrides_marker_intersection = ( overrides_marker_intersection = (
......
...@@ -5,10 +5,10 @@ import time ...@@ -5,10 +5,10 @@ import time
from collections import defaultdict from collections import defaultdict
from contextlib import contextmanager from contextlib import contextmanager
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from typing import Callable
from typing import FrozenSet from typing import FrozenSet
from typing import Iterator from typing import Iterator
from typing import Tuple from typing import Tuple
from typing import TypeVar
try: try:
...@@ -60,7 +60,7 @@ class Solver: ...@@ -60,7 +60,7 @@ class Solver:
provider = Provider(self._package, self._pool, self._io) provider = Provider(self._package, self._pool, self._io)
self._provider = provider self._provider = provider
self._overrides: list[dict] = [] self._overrides: list[dict[DependencyPackage, dict[str, Dependency]]] = []
@property @property
def provider(self) -> Provider: def provider(self) -> Provider:
...@@ -71,7 +71,7 @@ class Solver: ...@@ -71,7 +71,7 @@ class Solver:
with self.provider.use_environment(env): with self.provider.use_environment(env):
yield yield
def solve(self, use_latest: list[str] = None) -> Transaction: def solve(self, use_latest: list[str] | None = None) -> Transaction:
from poetry.puzzle.transaction import Transaction from poetry.puzzle.transaction import Transaction
with self._provider.progress(): with self._provider.progress():
...@@ -97,7 +97,9 @@ class Solver: ...@@ -97,7 +97,9 @@ class Solver:
) )
def solve_in_compatibility_mode( def solve_in_compatibility_mode(
self, overrides: tuple[dict, ...], use_latest: list[str] = None self,
overrides: tuple[dict[DependencyPackage, dict[str, Dependency]], ...],
use_latest: list[str] | None = None,
) -> tuple[list[Package], list[int]]: ) -> tuple[list[Package], list[int]]:
packages = [] packages = []
...@@ -125,17 +127,21 @@ class Solver: ...@@ -125,17 +127,21 @@ class Solver:
return packages, depths return packages, depths
def _solve(self, use_latest: list[str] = None) -> tuple[list[Package], list[int]]: def _solve(
self, use_latest: list[str] | None = None
) -> tuple[list[Package], list[int]]:
if self._provider._overrides: if self._provider._overrides:
self._overrides.append(self._provider._overrides) self._overrides.append(self._provider._overrides)
locked = defaultdict(list) locked: dict[str, list[DependencyPackage]] = defaultdict(list)
for package in self._locked.packages: for package in self._locked.packages:
locked[package.name].append( locked[package.name].append(
DependencyPackage(package.to_dependency(), package) DependencyPackage(package.to_dependency(), package)
) )
for packages in locked.values(): for dependency_packages in locked.values():
packages.sort(key=lambda package: package.version, reverse=True) dependency_packages.sort(
key=lambda package: package.package.version, reverse=True
)
try: try:
result = resolve_version( result = resolve_version(
...@@ -148,11 +154,8 @@ class Solver: ...@@ -148,11 +154,8 @@ class Solver:
except SolveFailure as e: except SolveFailure as e:
raise SolverProblemError(e) raise SolverProblemError(e)
results = dict( combined_nodes = depth_first_search(PackageNode(self._package, packages))
depth_first_search( results = dict(aggregate_package_nodes(nodes) for nodes in combined_nodes)
PackageNode(self._package, packages), aggregate_package_nodes
)
)
# Merging feature packages with base packages # Merging feature packages with base packages
final_packages = [] final_packages = []
...@@ -183,6 +186,8 @@ class Solver: ...@@ -183,6 +186,8 @@ class Solver:
DFSNodeID = Tuple[str, FrozenSet[str], bool] DFSNodeID = Tuple[str, FrozenSet[str], bool]
T = TypeVar("T", bound="DFSNode")
class DFSNode: class DFSNode:
def __init__(self, id: DFSNodeID, name: str, base_name: str) -> None: def __init__(self, id: DFSNodeID, name: str, base_name: str) -> None:
...@@ -190,7 +195,7 @@ class DFSNode: ...@@ -190,7 +195,7 @@ class DFSNode:
self.name = name self.name = name
self.base_name = base_name self.base_name = base_name
def reachable(self) -> list: def reachable(self: T) -> list[T]:
return [] return []
def visit(self, parents: list[PackageNode]) -> None: def visit(self, parents: list[PackageNode]) -> None:
...@@ -200,9 +205,7 @@ class DFSNode: ...@@ -200,9 +205,7 @@ class DFSNode:
return str(self.id) return str(self.id)
def depth_first_search( def depth_first_search(source: PackageNode) -> list[list[PackageNode]]:
source: PackageNode, aggregator: Callable
) -> list[tuple[Package, int]]:
back_edges: dict[DFSNodeID, list[PackageNode]] = defaultdict(list) back_edges: dict[DFSNodeID, list[PackageNode]] = defaultdict(list)
visited: set[DFSNodeID] = set() visited: set[DFSNodeID] = set()
topo_sorted_nodes: list[PackageNode] = [] topo_sorted_nodes: list[PackageNode] = []
...@@ -210,18 +213,18 @@ def depth_first_search( ...@@ -210,18 +213,18 @@ def depth_first_search(
dfs_visit(source, back_edges, visited, topo_sorted_nodes) dfs_visit(source, back_edges, visited, topo_sorted_nodes)
# Combine the nodes by name # Combine the nodes by name
combined_nodes = defaultdict(list) combined_nodes: dict[str, list[PackageNode]] = defaultdict(list)
for node in topo_sorted_nodes: for node in topo_sorted_nodes:
node.visit(back_edges[node.id]) node.visit(back_edges[node.id])
combined_nodes[node.name].append(node) combined_nodes[node.name].append(node)
combined_topo_sorted_nodes = [ combined_topo_sorted_nodes: list[list[PackageNode]] = [
combined_nodes.pop(node.name) combined_nodes.pop(node.name)
for node in topo_sorted_nodes for node in topo_sorted_nodes
if node.name in combined_nodes if node.name in combined_nodes
] ]
return [aggregator(nodes) for nodes in combined_topo_sorted_nodes] return combined_topo_sorted_nodes
def dfs_visit( def dfs_visit(
......
...@@ -6,7 +6,7 @@ from typing import TYPE_CHECKING ...@@ -6,7 +6,7 @@ from typing import TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from poetry.core.packages.package import Package from poetry.core.packages.package import Package
from poetry.installation.operations import OperationTypes from poetry.installation.operations.operation import Operation
class Transaction: class Transaction:
...@@ -28,12 +28,12 @@ class Transaction: ...@@ -28,12 +28,12 @@ class Transaction:
def calculate_operations( def calculate_operations(
self, with_uninstalls: bool = True, synchronize: bool = False self, with_uninstalls: bool = True, synchronize: bool = False
) -> list[OperationTypes]: ) -> list[Operation]:
from poetry.installation.operations.install import Install from poetry.installation.operations import Install
from poetry.installation.operations.uninstall import Uninstall from poetry.installation.operations import Uninstall
from poetry.installation.operations.update import Update from poetry.installation.operations import Update
operations: list[OperationTypes] = [] operations: list[Operation] = []
for result_package, priority in self._result_packages: for result_package, priority in self._result_packages:
installed = False installed = False
......
...@@ -2,3 +2,6 @@ from __future__ import annotations ...@@ -2,3 +2,6 @@ from __future__ import annotations
from poetry.repositories.pool import Pool from poetry.repositories.pool import Pool
from poetry.repositories.repository import Repository from poetry.repositories.repository import Repository
__all__ = ["Pool", "Repository"]
...@@ -9,6 +9,7 @@ from abc import ABC ...@@ -9,6 +9,7 @@ from abc import ABC
from collections import defaultdict from collections import defaultdict
from pathlib import Path from pathlib import Path
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from typing import Any
from urllib.parse import quote from urllib.parse import quote
import requests import requests
...@@ -232,7 +233,7 @@ class HTTPRepository(CachedRepository, ABC): ...@@ -232,7 +233,7 @@ class HTTPRepository(CachedRepository, ABC):
f' "{data.version}"' f' "{data.version}"'
) )
urls = defaultdict(list) urls = defaultdict(list)
files = [] files: list[dict[str, Any]] = []
for link in links: for link in links:
if link.is_wheel: if link.is_wheel:
urls["bdist_wheel"].append(link.url) urls["bdist_wheel"].append(link.url)
...@@ -244,7 +245,8 @@ class HTTPRepository(CachedRepository, ABC): ...@@ -244,7 +245,8 @@ class HTTPRepository(CachedRepository, ABC):
file_hash = f"{link.hash_name}:{link.hash}" if link.hash else None file_hash = f"{link.hash_name}:{link.hash}" if link.hash else None
if not link.hash or ( if not link.hash or (
link.hash_name not in ("sha256", "sha384", "sha512") link.hash_name is not None
and link.hash_name not in ("sha256", "sha384", "sha512")
and hasattr(hashlib, link.hash_name) and hasattr(hashlib, link.hash_name)
): ):
with temporary_directory() as temp_dir: with temporary_directory() as temp_dir:
......
...@@ -69,15 +69,15 @@ class LinkSource: ...@@ -69,15 +69,15 @@ class LinkSource:
if m: if m:
name = canonicalize_name(m.group("name")) name = canonicalize_name(m.group("name"))
version = m.group("ver") version_string = m.group("ver")
else: else:
info, ext = link.splitext() info, ext = link.splitext()
match = self.VERSION_REGEX.match(info) match = self.VERSION_REGEX.match(info)
if match: if match:
version = match.group(2) version_string = match.group(2)
with contextlib.suppress(ValueError): with contextlib.suppress(ValueError):
version = Version.parse(version) version = Version.parse(version_string)
return Package(name, version, source_url=link.url) return Package(name, version, source_url=link.url)
......
from __future__ import annotations from __future__ import annotations
from contextlib import suppress
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from poetry.repositories.exceptions import PackageNotFound from poetry.repositories.exceptions import PackageNotFound
...@@ -136,18 +135,14 @@ class Pool(Repository): ...@@ -136,18 +135,14 @@ class Pool(Repository):
raise ValueError(f'Repository "{repository}" does not exist.') raise ValueError(f'Repository "{repository}" does not exist.')
if repository is not None and not self._ignore_repository_names: if repository is not None and not self._ignore_repository_names:
with suppress(PackageNotFound):
return self.repository(repository).package(name, version, extras=extras) return self.repository(repository).package(name, version, extras=extras)
else:
for repo in self._repositories: for repo in self._repositories:
try: try:
package = repo.package(name, version, extras=extras) package = repo.package(name, version, extras=extras)
except PackageNotFound: except PackageNotFound:
continue continue
if package:
self._packages.append(package)
return package return package
raise PackageNotFound(f"Package {name} ({version}) not found.") raise PackageNotFound(f"Package {name} ({version}) not found.")
......
...@@ -8,16 +8,19 @@ from poetry.core.semver.helpers import parse_constraint ...@@ -8,16 +8,19 @@ from poetry.core.semver.helpers import parse_constraint
from poetry.core.semver.version_constraint import VersionConstraint from poetry.core.semver.version_constraint import VersionConstraint
from poetry.core.semver.version_range import VersionRange from poetry.core.semver.version_range import VersionRange
from poetry.repositories.exceptions import PackageNotFound
if TYPE_CHECKING: if TYPE_CHECKING:
from poetry.core.packages.dependency import Dependency from poetry.core.packages.dependency import Dependency
from poetry.core.packages.package import Package from poetry.core.packages.package import Package
from poetry.core.packages.utils.link import Link from poetry.core.packages.utils.link import Link
from poetry.core.semver.helpers import VersionTypes
class Repository: class Repository:
def __init__(self, name: str = None, packages: list[Package] = None) -> None: def __init__(
self, name: str | None = None, packages: list[Package] | None = None
) -> None:
self._name = name self._name = name
self._packages: list[Package] = [] self._packages: list[Package] = []
...@@ -94,7 +97,7 @@ class Repository: ...@@ -94,7 +97,7 @@ class Repository:
@staticmethod @staticmethod
def _get_constraints_from_dependency( def _get_constraints_from_dependency(
dependency: Dependency, dependency: Dependency,
) -> tuple[VersionTypes, bool]: ) -> tuple[VersionConstraint, bool]:
constraint = dependency.constraint constraint = dependency.constraint
if constraint is None: if constraint is None:
constraint = "*" constraint = "*"
...@@ -131,3 +134,5 @@ class Repository: ...@@ -131,3 +134,5 @@ class Repository:
for package in self.packages: for package in self.packages:
if name == package.name and package.version.text == version: if name == package.name and package.version.text == version:
return package.clone() return package.clone()
raise PackageNotFound(f"Package {name} ({version}) not found.")
...@@ -14,7 +14,7 @@ else: ...@@ -14,7 +14,7 @@ else:
WINDOWS = sys.platform == "win32" WINDOWS = sys.platform == "win32"
def decode(string: str, encodings: list[str] | None = None) -> str: def decode(string: bytes | str, encodings: list[str] | None = None) -> str:
if not isinstance(string, bytes): if not isinstance(string, bytes):
return string return string
......
...@@ -114,6 +114,7 @@ def get_package_version_display_string( ...@@ -114,6 +114,7 @@ def get_package_version_display_string(
package: Package, root: Path | None = None package: Package, root: Path | None = None
) -> str: ) -> str:
if package.source_type in ["file", "directory"] and root: if package.source_type in ["file", "directory"] and root:
assert package.source_url is not None
path = Path(os.path.relpath(package.source_url, root.as_posix())).as_posix() path = Path(os.path.relpath(package.source_url, root.as_posix())).as_posix()
return f"{package.version} {path}" return f"{package.version} {path}"
......
...@@ -367,7 +367,11 @@ class Git: ...@@ -367,7 +367,11 @@ class Git:
if not is_revision_sha(revision=current_sha): if not is_revision_sha(revision=current_sha):
# head is not a sha, this will cause issues later, lets reset # head is not a sha, this will cause issues later, lets reset
remove_directory(target, force=True) remove_directory(target, force=True)
elif refspec.is_sha and current_sha.startswith(refspec.revision): elif (
refspec.is_sha
and refspec.revision is not None
and current_sha.startswith(refspec.revision)
):
# if revision is used short-circuit remote fetch head matches # if revision is used short-circuit remote fetch head matches
return current_repo return current_repo
......
...@@ -21,7 +21,7 @@ class VersionSelector: ...@@ -21,7 +21,7 @@ class VersionSelector:
target_package_version: str | None = None, target_package_version: str | None = None,
allow_prereleases: bool = False, allow_prereleases: bool = False,
source: str | None = None, source: str | None = None,
) -> Package | bool: ) -> Package | None:
""" """
Given a package name and optional version, Given a package name and optional version,
returns the latest Package that matches returns the latest Package that matches
...@@ -40,7 +40,7 @@ class VersionSelector: ...@@ -40,7 +40,7 @@ class VersionSelector:
only_prereleases = all(c.version.is_unstable() for c in candidates) only_prereleases = all(c.version.is_unstable() for c in candidates)
if not candidates: if not candidates:
return False return None
package = None package = None
for candidate in candidates: for candidate in candidates:
...@@ -55,8 +55,6 @@ class VersionSelector: ...@@ -55,8 +55,6 @@ class VersionSelector:
if package is None or package.version < candidate.version: if package is None or package.version < candidate.version:
package = candidate package = candidate
if package is None:
return False
return package return package
def find_recommended_require_version(self, package: Package) -> str: def find_recommended_require_version(self, package: Package) -> str:
......
...@@ -26,11 +26,10 @@ from poetry.repositories.exceptions import PackageNotFound ...@@ -26,11 +26,10 @@ from poetry.repositories.exceptions import PackageNotFound
if TYPE_CHECKING: if TYPE_CHECKING:
from poetry.core.packages.dependency import Dependency from poetry.core.packages.dependency import Dependency
from poetry.core.packages.types import DependencyTypes
from poetry.core.semver.version import Version from poetry.core.semver.version import Version
from tomlkit.toml_document import TOMLDocument from tomlkit.toml_document import TOMLDocument
from poetry.installation.operations import OperationTypes from poetry.installation.operations.operation import Operation
from poetry.poetry import Poetry from poetry.poetry import Poetry
FIXTURE_PATH = Path(__file__).parent / "fixtures" FIXTURE_PATH = Path(__file__).parent / "fixtures"
...@@ -46,7 +45,7 @@ def get_dependency( ...@@ -46,7 +45,7 @@ def get_dependency(
groups: list[str] | None = None, groups: list[str] | None = None,
optional: bool = False, optional: bool = False,
allows_prereleases: bool = False, allows_prereleases: bool = False,
) -> DependencyTypes: ) -> Dependency:
if constraint is None: if constraint is None:
constraint = "*" constraint = "*"
...@@ -134,19 +133,19 @@ class TestExecutor(Executor): ...@@ -134,19 +133,19 @@ class TestExecutor(Executor):
def removals(self) -> list[Package]: def removals(self) -> list[Package]:
return self._uninstalls return self._uninstalls
def _do_execute_operation(self, operation: OperationTypes) -> None: def _do_execute_operation(self, operation: Operation) -> None:
super()._do_execute_operation(operation) super()._do_execute_operation(operation)
if not operation.skipped: if not operation.skipped:
getattr(self, f"_{operation.job_type}s").append(operation.package) getattr(self, f"_{operation.job_type}s").append(operation.package)
def _execute_install(self, operation: OperationTypes) -> int: def _execute_install(self, operation: Operation) -> int:
return 0 return 0
def _execute_update(self, operation: OperationTypes) -> int: def _execute_update(self, operation: Operation) -> int:
return 0 return 0
def _execute_remove(self, operation: OperationTypes) -> int: def _execute_remove(self, operation: Operation) -> int:
return 0 return 0
......
...@@ -47,7 +47,7 @@ from tests.repositories.test_pypi_repository import MockRepository ...@@ -47,7 +47,7 @@ from tests.repositories.test_pypi_repository import MockRepository
if TYPE_CHECKING: if TYPE_CHECKING:
from pytest_mock import MockerFixture from pytest_mock import MockerFixture
from poetry.installation.operations import OperationTypes from poetry.installation.operations.operation import Operation
from poetry.packages import DependencyPackage from poetry.packages import DependencyPackage
from poetry.utils.env import Env from poetry.utils.env import Env
from tests.conftest import Config from tests.conftest import Config
...@@ -81,19 +81,19 @@ class Executor(BaseExecutor): ...@@ -81,19 +81,19 @@ class Executor(BaseExecutor):
def removals(self) -> list[DependencyPackage]: def removals(self) -> list[DependencyPackage]:
return self._uninstalls return self._uninstalls
def _do_execute_operation(self, operation: OperationTypes) -> None: def _do_execute_operation(self, operation: Operation) -> None:
super()._do_execute_operation(operation) super()._do_execute_operation(operation)
if not operation.skipped: if not operation.skipped:
getattr(self, f"_{operation.job_type}s").append(operation.package) getattr(self, f"_{operation.job_type}s").append(operation.package)
def _execute_install(self, operation: OperationTypes) -> int: def _execute_install(self, operation: Operation) -> int:
return 0 return 0
def _execute_update(self, operation: OperationTypes) -> int: def _execute_update(self, operation: Operation) -> int:
return 0 return 0
def _execute_uninstall(self, operation: OperationTypes) -> int: def _execute_uninstall(self, operation: Operation) -> int:
return 0 return 0
......
...@@ -32,7 +32,7 @@ from tests.repositories.test_pypi_repository import MockRepository as MockPyPIRe ...@@ -32,7 +32,7 @@ from tests.repositories.test_pypi_repository import MockRepository as MockPyPIRe
if TYPE_CHECKING: if TYPE_CHECKING:
import httpretty import httpretty
from poetry.installation.operations import OperationTypes from poetry.installation.operation.operation import Operation
from poetry.puzzle.transaction import Transaction from poetry.puzzle.transaction import Transaction
DEFAULT_SOURCE_REF = ( DEFAULT_SOURCE_REF = (
...@@ -94,7 +94,7 @@ def check_solver_result( ...@@ -94,7 +94,7 @@ def check_solver_result(
transaction: Transaction, transaction: Transaction,
expected: list[dict[str, Any]], expected: list[dict[str, Any]],
synchronize: bool = False, synchronize: bool = False,
) -> list[OperationTypes]: ) -> list[Operation]:
for e in expected: for e in expected:
if "skipped" not in e: if "skipped" not in e:
e["skipped"] = False e["skipped"] = False
......
...@@ -9,10 +9,10 @@ from poetry.puzzle.transaction import Transaction ...@@ -9,10 +9,10 @@ from poetry.puzzle.transaction import Transaction
if TYPE_CHECKING: if TYPE_CHECKING:
from poetry.installation.operations import OperationTypes from poetry.installation.operations.operation import Operation
def check_operations(ops: list[OperationTypes], expected: list[dict[str, Any]]) -> None: def check_operations(ops: list[Operation], expected: list[dict[str, Any]]) -> None:
for e in expected: for e in expected:
if "skipped" not in e: if "skipped" not in e:
e["skipped"] = False e["skipped"] = False
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment