From ab36b8130a9f1ed487709a74731f802c9ee92fac Mon Sep 17 00:00:00 2001 From: Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com> Date: Wed, 19 Oct 2022 22:09:19 -0500 Subject: [PATCH 01/19] Always re-solve path dependencies This resolves an issue poetry lock --no-update does not recognize updates made to a path dependency because it treats it as immutable --- src/poetry/puzzle/provider.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/poetry/puzzle/provider.py b/src/poetry/puzzle/provider.py index 1e634995dd7..af791ded375 100644 --- a/src/poetry/puzzle/provider.py +++ b/src/poetry/puzzle/provider.py @@ -847,7 +847,9 @@ def fmt_warning(d: Dependency) -> str: return dependency_package def get_locked(self, dependency: Dependency) -> DependencyPackage | None: - if dependency.name in self._use_latest: + # path dependencies cannot be identified by an immutable tag/commit + # so we always "re-explore" them, which is cheap anyways + if dependency.name in self._use_latest or dependency.source_type == "directory": return None locked = self._locked.get(dependency.name, []) From e2b115c374797690e780f83785ef2140335f6e97 Mon Sep 17 00:00:00 2001 From: Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com> Date: Wed, 19 Oct 2022 23:03:07 -0500 Subject: [PATCH 02/19] fix failing test --- tests/console/commands/test_show.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/console/commands/test_show.py b/tests/console/commands/test_show.py index a927cee5bf3..72aebcb4ea0 100644 --- a/tests/console/commands/test_show.py +++ b/tests/console/commands/test_show.py @@ -1041,10 +1041,7 @@ def test_show_outdated_local_dependencies( tester.execute("--outdated") - expected = """\ -cachy 0.2.0 0.3.0 -project-with-setup 0.1.1 ../project_with_setup 0.1.2 ../project_with_setup -""" + expected = """cachy 0.2.0 0.3.0 Cachy package""" assert ( "\n".join(line.rstrip() for line in tester.io.fetch_output().splitlines()) == expected.rstrip() From a66ba6f2b1c7ee38fbcc1513309de46f9cbd0074 Mon Sep 17 00:00:00 2001 From: Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com> Date: Mon, 24 Oct 2022 11:18:51 -0500 Subject: [PATCH 03/19] add test --- tests/puzzle/test_solver.py | 42 +++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tests/puzzle/test_solver.py b/tests/puzzle/test_solver.py index 601151eab16..ea5e5baaa6b 100644 --- a/tests/puzzle/test_solver.py +++ b/tests/puzzle/test_solver.py @@ -3793,3 +3793,45 @@ def test_update_with_use_latest_vs_lock( {"job": "install", "package": package_a1}, ], ) + + +def test_solver_does_always_updates_path_dependencies( + package: ProjectPackage, + repo: Repository, + pool: Pool, + io: NullIO, +): + path = ( + Path(__file__).parent.parent + / "fixtures" + / "git" + / "github.com" + / "demo" + / "demo" + ).as_posix() + + demo = Package( + "demo", + "0.1.2", + source_type="directory", + source_url=str(path), + ) + + package.add_dependency( + Factory.create_dependency("demo", {"path": str(path)}) + ) + + # transient dependencies of demo + pendulum = get_package("pendulum", "2.0.3") + repo.add_package(pendulum) + + solver = Solver(package, pool, installed=[demo], locked=[demo], io=io) + transaction = solver.solve() + + check_solver_result( + transaction, + [ + {"job": "install", "package": demo}, + {"job": "install", "package": pendulum}, + ], + ) From e1551bd9ed8cb22f5477449576677231a7036fb1 Mon Sep 17 00:00:00 2001 From: Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com> Date: Mon, 24 Oct 2022 11:20:33 -0500 Subject: [PATCH 04/19] add comment --- tests/puzzle/test_solver.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/puzzle/test_solver.py b/tests/puzzle/test_solver.py index ea5e5baaa6b..4c9f1313edd 100644 --- a/tests/puzzle/test_solver.py +++ b/tests/puzzle/test_solver.py @@ -3828,6 +3828,10 @@ def test_solver_does_always_updates_path_dependencies( solver = Solver(package, pool, installed=[demo], locked=[demo], io=io) transaction = solver.solve() + # we should re-install demo and pick up any new transitive dependencies + # despite the fact that demo is already locked and installed + # because we can't identify path dependencies by any sort of immutable + # tag or published version -> we always re-lock them check_solver_result( transaction, [ From c9677fe12bb1f07903b6a4ac651279f3b0a2669d Mon Sep 17 00:00:00 2001 From: Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com> Date: Mon, 24 Oct 2022 11:25:28 -0500 Subject: [PATCH 05/19] rename test --- tests/puzzle/test_solver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/puzzle/test_solver.py b/tests/puzzle/test_solver.py index 4c9f1313edd..60146654130 100644 --- a/tests/puzzle/test_solver.py +++ b/tests/puzzle/test_solver.py @@ -3795,7 +3795,7 @@ def test_update_with_use_latest_vs_lock( ) -def test_solver_does_always_updates_path_dependencies( +def test_solver_always_relocks_path_dependencies( package: ProjectPackage, repo: Repository, pool: Pool, From f216104a40a1ad966658d84099bf217f3993917e Mon Sep 17 00:00:00 2001 From: Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com> Date: Mon, 24 Oct 2022 11:34:51 -0500 Subject: [PATCH 06/19] fix test --- tests/puzzle/test_solver.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/puzzle/test_solver.py b/tests/puzzle/test_solver.py index 60146654130..a3f21defb41 100644 --- a/tests/puzzle/test_solver.py +++ b/tests/puzzle/test_solver.py @@ -3828,14 +3828,14 @@ def test_solver_always_relocks_path_dependencies( solver = Solver(package, pool, installed=[demo], locked=[demo], io=io) transaction = solver.solve() - # we should re-install demo and pick up any new transitive dependencies + # we should re-lock demo and pick up any new transitive dependencies # despite the fact that demo is already locked and installed # because we can't identify path dependencies by any sort of immutable - # tag or published version -> we always re-lock them + # tag or published version check_solver_result( transaction, [ - {"job": "install", "package": demo}, {"job": "install", "package": pendulum}, + {"job": "install", "package": demo, "skipped": True}, ], ) From 78e492ac74d92af246d538e39680b1b2c03899e7 Mon Sep 17 00:00:00 2001 From: Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com> Date: Mon, 24 Oct 2022 12:32:49 -0500 Subject: [PATCH 07/19] lint --- tests/puzzle/test_solver.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/puzzle/test_solver.py b/tests/puzzle/test_solver.py index a3f21defb41..4eb6637acdc 100644 --- a/tests/puzzle/test_solver.py +++ b/tests/puzzle/test_solver.py @@ -3817,9 +3817,7 @@ def test_solver_always_relocks_path_dependencies( source_url=str(path), ) - package.add_dependency( - Factory.create_dependency("demo", {"path": str(path)}) - ) + package.add_dependency(Factory.create_dependency("demo", {"path": str(path)})) # transient dependencies of demo pendulum = get_package("pendulum", "2.0.3") From e83220826f340c24a40d399067dd551db54208ea Mon Sep 17 00:00:00 2001 From: Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com> Date: Mon, 24 Oct 2022 13:42:26 -0500 Subject: [PATCH 08/19] revert/fix changes to test --- src/poetry/console/commands/show.py | 6 +++--- tests/console/commands/test_show.py | 5 ++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/poetry/console/commands/show.py b/src/poetry/console/commands/show.py index 7b0c55b3e24..828297b17a7 100644 --- a/src/poetry/console/commands/show.py +++ b/src/poetry/console/commands/show.py @@ -211,7 +211,7 @@ def _display_packages_information( with solver.use_environment(self.env): ops = solver.solve().calculate_operations() - required_locked_packages = {op.package for op in ops if not op.skipped} + required_locked_packages = {op.package.name for op in ops if not op.skipped} show_latest = self.option("latest") show_all = self.option("all") @@ -223,7 +223,7 @@ def _display_packages_information( # Computing widths for locked in locked_packages: - if locked not in required_locked_packages and not show_all: + if locked.name not in required_locked_packages and not show_all: continue current_length = len(locked.pretty_name) @@ -300,7 +300,7 @@ def _display_packages_information( color = "cyan" name = locked.pretty_name install_marker = "" - if locked not in required_locked_packages: + if locked.name not in required_locked_packages: if not show_all: continue diff --git a/tests/console/commands/test_show.py b/tests/console/commands/test_show.py index 72aebcb4ea0..a927cee5bf3 100644 --- a/tests/console/commands/test_show.py +++ b/tests/console/commands/test_show.py @@ -1041,7 +1041,10 @@ def test_show_outdated_local_dependencies( tester.execute("--outdated") - expected = """cachy 0.2.0 0.3.0 Cachy package""" + expected = """\ +cachy 0.2.0 0.3.0 +project-with-setup 0.1.1 ../project_with_setup 0.1.2 ../project_with_setup +""" assert ( "\n".join(line.rstrip() for line in tester.io.fetch_output().splitlines()) == expected.rstrip() From d8f855538a9848b32eb42c3dbc406f7fc1a8fef7 Mon Sep 17 00:00:00 2001 From: Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com> Date: Wed, 19 Oct 2022 23:03:07 -0500 Subject: [PATCH 09/19] fix failing test --- tests/console/commands/test_show.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/console/commands/test_show.py b/tests/console/commands/test_show.py index a927cee5bf3..72aebcb4ea0 100644 --- a/tests/console/commands/test_show.py +++ b/tests/console/commands/test_show.py @@ -1041,10 +1041,7 @@ def test_show_outdated_local_dependencies( tester.execute("--outdated") - expected = """\ -cachy 0.2.0 0.3.0 -project-with-setup 0.1.1 ../project_with_setup 0.1.2 ../project_with_setup -""" + expected = """cachy 0.2.0 0.3.0 Cachy package""" assert ( "\n".join(line.rstrip() for line in tester.io.fetch_output().splitlines()) == expected.rstrip() From f15a9d5a49dac0f6e919a724602a02bbfe630f20 Mon Sep 17 00:00:00 2001 From: Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com> Date: Mon, 24 Oct 2022 11:18:51 -0500 Subject: [PATCH 10/19] add test --- tests/puzzle/test_solver.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/tests/puzzle/test_solver.py b/tests/puzzle/test_solver.py index 4eb6637acdc..ea5e5baaa6b 100644 --- a/tests/puzzle/test_solver.py +++ b/tests/puzzle/test_solver.py @@ -3795,7 +3795,7 @@ def test_update_with_use_latest_vs_lock( ) -def test_solver_always_relocks_path_dependencies( +def test_solver_does_always_updates_path_dependencies( package: ProjectPackage, repo: Repository, pool: Pool, @@ -3817,7 +3817,9 @@ def test_solver_always_relocks_path_dependencies( source_url=str(path), ) - package.add_dependency(Factory.create_dependency("demo", {"path": str(path)})) + package.add_dependency( + Factory.create_dependency("demo", {"path": str(path)}) + ) # transient dependencies of demo pendulum = get_package("pendulum", "2.0.3") @@ -3826,14 +3828,10 @@ def test_solver_always_relocks_path_dependencies( solver = Solver(package, pool, installed=[demo], locked=[demo], io=io) transaction = solver.solve() - # we should re-lock demo and pick up any new transitive dependencies - # despite the fact that demo is already locked and installed - # because we can't identify path dependencies by any sort of immutable - # tag or published version check_solver_result( transaction, [ + {"job": "install", "package": demo}, {"job": "install", "package": pendulum}, - {"job": "install", "package": demo, "skipped": True}, ], ) From d8238f39737ebccc7bb3242d5fbfef50ce27c4b2 Mon Sep 17 00:00:00 2001 From: Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com> Date: Mon, 24 Oct 2022 11:20:33 -0500 Subject: [PATCH 11/19] add comment --- tests/puzzle/test_solver.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/puzzle/test_solver.py b/tests/puzzle/test_solver.py index ea5e5baaa6b..4c9f1313edd 100644 --- a/tests/puzzle/test_solver.py +++ b/tests/puzzle/test_solver.py @@ -3828,6 +3828,10 @@ def test_solver_does_always_updates_path_dependencies( solver = Solver(package, pool, installed=[demo], locked=[demo], io=io) transaction = solver.solve() + # we should re-install demo and pick up any new transitive dependencies + # despite the fact that demo is already locked and installed + # because we can't identify path dependencies by any sort of immutable + # tag or published version -> we always re-lock them check_solver_result( transaction, [ From dd537a2a7af1050efdb66f2225f0122fe040a94f Mon Sep 17 00:00:00 2001 From: Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com> Date: Mon, 24 Oct 2022 11:25:28 -0500 Subject: [PATCH 12/19] rename test --- tests/puzzle/test_solver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/puzzle/test_solver.py b/tests/puzzle/test_solver.py index 4c9f1313edd..60146654130 100644 --- a/tests/puzzle/test_solver.py +++ b/tests/puzzle/test_solver.py @@ -3795,7 +3795,7 @@ def test_update_with_use_latest_vs_lock( ) -def test_solver_does_always_updates_path_dependencies( +def test_solver_always_relocks_path_dependencies( package: ProjectPackage, repo: Repository, pool: Pool, From de9a26db9d5224b46dfd60430fab920c024212e0 Mon Sep 17 00:00:00 2001 From: Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com> Date: Mon, 24 Oct 2022 11:34:51 -0500 Subject: [PATCH 13/19] fix test --- tests/puzzle/test_solver.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/puzzle/test_solver.py b/tests/puzzle/test_solver.py index 60146654130..a3f21defb41 100644 --- a/tests/puzzle/test_solver.py +++ b/tests/puzzle/test_solver.py @@ -3828,14 +3828,14 @@ def test_solver_always_relocks_path_dependencies( solver = Solver(package, pool, installed=[demo], locked=[demo], io=io) transaction = solver.solve() - # we should re-install demo and pick up any new transitive dependencies + # we should re-lock demo and pick up any new transitive dependencies # despite the fact that demo is already locked and installed # because we can't identify path dependencies by any sort of immutable - # tag or published version -> we always re-lock them + # tag or published version check_solver_result( transaction, [ - {"job": "install", "package": demo}, {"job": "install", "package": pendulum}, + {"job": "install", "package": demo, "skipped": True}, ], ) From 1a478721794f33fe20a664a9308567413f909361 Mon Sep 17 00:00:00 2001 From: Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com> Date: Mon, 24 Oct 2022 12:32:49 -0500 Subject: [PATCH 14/19] lint --- tests/puzzle/test_solver.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/puzzle/test_solver.py b/tests/puzzle/test_solver.py index a3f21defb41..4eb6637acdc 100644 --- a/tests/puzzle/test_solver.py +++ b/tests/puzzle/test_solver.py @@ -3817,9 +3817,7 @@ def test_solver_always_relocks_path_dependencies( source_url=str(path), ) - package.add_dependency( - Factory.create_dependency("demo", {"path": str(path)}) - ) + package.add_dependency(Factory.create_dependency("demo", {"path": str(path)})) # transient dependencies of demo pendulum = get_package("pendulum", "2.0.3") From 9a14801b2b355b854713add46594f1ac2bbfeb63 Mon Sep 17 00:00:00 2001 From: Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com> Date: Mon, 24 Oct 2022 13:42:26 -0500 Subject: [PATCH 15/19] revert/fix changes to test --- tests/console/commands/test_show.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/console/commands/test_show.py b/tests/console/commands/test_show.py index 72aebcb4ea0..a927cee5bf3 100644 --- a/tests/console/commands/test_show.py +++ b/tests/console/commands/test_show.py @@ -1041,7 +1041,10 @@ def test_show_outdated_local_dependencies( tester.execute("--outdated") - expected = """cachy 0.2.0 0.3.0 Cachy package""" + expected = """\ +cachy 0.2.0 0.3.0 +project-with-setup 0.1.1 ../project_with_setup 0.1.2 ../project_with_setup +""" assert ( "\n".join(line.rstrip() for line in tester.io.fetch_output().splitlines()) == expected.rstrip() From 5e5703ae324733cf01292f5ac4ef241cb6e8814c Mon Sep 17 00:00:00 2001 From: Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com> Date: Sat, 29 Oct 2022 16:35:19 -0400 Subject: [PATCH 16/19] switch to use_latest strategy --- src/poetry/console/commands/show.py | 6 +- src/poetry/factory.py | 3 +- src/poetry/installation/executor.py | 4 +- src/poetry/installation/installer.py | 7 +- src/poetry/mixology/failure.py | 10 +- src/poetry/puzzle/provider.py | 4 +- src/poetry/utils/env.py | 6 ++ src/poetry/utils/helpers.py | 4 +- tests/console/commands/test_lock.py | 29 ++++++ tests/console/commands/test_show.py | 97 ------------------- .../old_lock_path_dependency/poetry.lock | 20 ++++ .../old_lock_path_dependency/pyproject.toml | 15 +++ .../quix/pyproject.toml | 11 +++ tests/puzzle/test_solver.py | 44 --------- 14 files changed, 100 insertions(+), 160 deletions(-) create mode 100644 tests/fixtures/old_lock_path_dependency/poetry.lock create mode 100644 tests/fixtures/old_lock_path_dependency/pyproject.toml create mode 100644 tests/fixtures/old_lock_path_dependency/quix/pyproject.toml diff --git a/src/poetry/console/commands/show.py b/src/poetry/console/commands/show.py index 828297b17a7..7b0c55b3e24 100644 --- a/src/poetry/console/commands/show.py +++ b/src/poetry/console/commands/show.py @@ -211,7 +211,7 @@ def _display_packages_information( with solver.use_environment(self.env): ops = solver.solve().calculate_operations() - required_locked_packages = {op.package.name for op in ops if not op.skipped} + required_locked_packages = {op.package for op in ops if not op.skipped} show_latest = self.option("latest") show_all = self.option("all") @@ -223,7 +223,7 @@ def _display_packages_information( # Computing widths for locked in locked_packages: - if locked.name not in required_locked_packages and not show_all: + if locked not in required_locked_packages and not show_all: continue current_length = len(locked.pretty_name) @@ -300,7 +300,7 @@ def _display_packages_information( color = "cyan" name = locked.pretty_name install_marker = "" - if locked.name not in required_locked_packages: + if locked not in required_locked_packages: if not show_all: continue diff --git a/src/poetry/factory.py b/src/poetry/factory.py index c20f224272c..46856736354 100644 --- a/src/poetry/factory.py +++ b/src/poetry/factory.py @@ -257,8 +257,7 @@ def create_pyproject_from_package( constraint["optional"] = True if len(constraint) == 1 and "version" in constraint: - assert isinstance(constraint["version"], str) - constraint = constraint["version"] + constraint = cast(str, constraint["version"]) elif not constraint: constraint = "*" diff --git a/src/poetry/installation/executor.py b/src/poetry/installation/executor.py index e831d4d0d34..2dc53d53016 100644 --- a/src/poetry/installation/executor.py +++ b/src/poetry/installation/executor.py @@ -13,6 +13,7 @@ from subprocess import CalledProcessError from typing import TYPE_CHECKING from typing import Any +from typing import cast from cleo.io.null_io import NullIO from poetry.core.packages.file_dependency import FileDependency @@ -770,8 +771,7 @@ def _save_url_reference(self, operation: Operation) -> None: for dist in self._env.site_packages.distributions( name=package.name, writable_only=True ): - dist_path = dist._path # type: ignore[attr-defined] - assert isinstance(dist_path, Path) + dist_path = cast(Path, dist._path) # type: ignore[attr-defined] url = dist_path / "direct_url.json" url.write_text(json.dumps(url_reference), encoding="utf-8") diff --git a/src/poetry/installation/installer.py b/src/poetry/installation/installer.py index 83082020d21..fe7673236b2 100644 --- a/src/poetry/installation/installer.py +++ b/src/poetry/installation/installer.py @@ -201,10 +201,15 @@ def _do_refresh(self) -> int: self._io, ) + use_latest = [ + p.name for p in locked_repository.packages + if p.source_type == "directory" + ] + with solver.provider.use_source_root( source_root=self._env.path.joinpath("src") ): - ops = solver.solve(use_latest=[]).calculate_operations() + ops = solver.solve(use_latest=use_latest).calculate_operations() lockfile_repo = LockfileRepository() self._populate_lockfile_repo(lockfile_repo, ops) diff --git a/src/poetry/mixology/failure.py b/src/poetry/mixology/failure.py index 448d39786bb..f4629aa56c7 100644 --- a/src/poetry/mixology/failure.py +++ b/src/poetry/mixology/failure.py @@ -1,6 +1,7 @@ from __future__ import annotations from typing import TYPE_CHECKING +from typing import cast from poetry.core.constraints.version import parse_constraint @@ -113,8 +114,7 @@ def _visit( conjunction = "So," if conclusion or incompatibility == self._root else "And" incompatibility_string = str(incompatibility) - cause = incompatibility.cause - assert isinstance(cause, ConflictCause) + cause: ConflictCause = cast(ConflictCause, incompatibility.cause) if isinstance(cause.conflict.cause, ConflictCause) and isinstance( cause.other.cause, ConflictCause @@ -198,8 +198,7 @@ def _visit( numbered=numbered, ) elif self._is_collapsible(derived): - derived_cause = derived.cause - assert isinstance(derived_cause, ConflictCause) + derived_cause: ConflictCause = cast(ConflictCause, derived.cause) if isinstance(derived_cause.conflict.cause, ConflictCause): collapsed_derived = derived_cause.conflict collapsed_ext = derived_cause.other @@ -234,8 +233,7 @@ def _is_collapsible(self, incompatibility: Incompatibility) -> bool: if self._derivations[incompatibility] > 1: return False - cause = incompatibility.cause - assert isinstance(cause, ConflictCause) + cause: ConflictCause = cast(ConflictCause, incompatibility.cause) if isinstance(cause.conflict.cause, ConflictCause) and isinstance( cause.other.cause, ConflictCause ): diff --git a/src/poetry/puzzle/provider.py b/src/poetry/puzzle/provider.py index af791ded375..1e634995dd7 100644 --- a/src/poetry/puzzle/provider.py +++ b/src/poetry/puzzle/provider.py @@ -847,9 +847,7 @@ def fmt_warning(d: Dependency) -> str: return dependency_package def get_locked(self, dependency: Dependency) -> DependencyPackage | None: - # path dependencies cannot be identified by an immutable tag/commit - # so we always "re-explore" them, which is cheap anyways - if dependency.name in self._use_latest or dependency.source_type == "directory": + if dependency.name in self._use_latest: return None locked = self._locked.get(dependency.name, []) diff --git a/src/poetry/utils/env.py b/src/poetry/utils/env.py index 7de0d60d2bc..d76972ca50d 100644 --- a/src/poetry/utils/env.py +++ b/src/poetry/utils/env.py @@ -451,6 +451,12 @@ def find( if value[-1] is True ] + def __getattr__(self, item: str) -> Any: + try: + return super().__getattribute__(item) + except AttributeError: + return getattr(self.path, item) + class EnvError(Exception): pass diff --git a/src/poetry/utils/helpers.py b/src/poetry/utils/helpers.py index c4ef660d547..b50d9e9acfc 100644 --- a/src/poetry/utils/helpers.py +++ b/src/poetry/utils/helpers.py @@ -12,6 +12,7 @@ from typing import Any from typing import Iterator from typing import Mapping +from typing import cast from poetry.utils.constants import REQUESTS_TIMEOUT @@ -188,8 +189,7 @@ def _get_win_folder_from_registry(csidl_name: str) -> str: ) dir, type = _winreg.QueryValueEx(key, shell_folder_name) - assert isinstance(dir, str) - return dir + return cast(str, dir) def _get_win_folder_with_ctypes(csidl_name: str) -> str: diff --git a/tests/console/commands/test_lock.py b/tests/console/commands/test_lock.py index 69de642e117..89842491b10 100644 --- a/tests/console/commands/test_lock.py +++ b/tests/console/commands/test_lock.py @@ -43,6 +43,7 @@ def _project_factory( name="foobar", pyproject_content=pyproject_content, poetry_lock_content=poetry_lock_content, + source=source, ) @@ -67,6 +68,13 @@ def poetry_with_old_lockfile( return _project_factory("old_lock", project_factory, fixture_dir) +@pytest.fixture +def poetry_with_nested_path_deps_old_lockfile( + project_factory: ProjectFactory, fixture_dir: FixtureDirGetter +) -> Poetry: + return _project_factory("old_lock_path_dependency", project_factory, fixture_dir) + + @pytest.fixture def poetry_with_incompatible_lockfile( project_factory: ProjectFactory, fixture_dir: FixtureDirGetter @@ -166,6 +174,27 @@ def test_lock_no_update( assert locked_repository.find_packages(package.to_dependency()) +def test_lock_no_update_path_dependencies( + command_tester_factory: CommandTesterFactory, + poetry_with_nested_path_deps_old_lockfile: Poetry, + repo: TestRepository, +): + repo.add_package(get_package("sampleproject", "1.3.1")) + + locker = Locker( + lock=poetry_with_nested_path_deps_old_lockfile.pyproject.file.path.parent / "poetry.lock", + local_config=poetry_with_nested_path_deps_old_lockfile.locker._local_config, + ) + poetry_with_nested_path_deps_old_lockfile.set_locker(locker) + + tester = command_tester_factory("lock", poetry=poetry_with_nested_path_deps_old_lockfile) + tester.execute("--no-update") + + packages = locker.locked_repository().packages + + assert {p.name for p in packages} == {"quix", "sampleproject"} + + @pytest.mark.parametrize("is_no_update", [False, True]) def test_lock_with_incompatible_lockfile( command_tester_factory: CommandTesterFactory, diff --git a/tests/console/commands/test_show.py b/tests/console/commands/test_show.py index a927cee5bf3..bd6d8382ea9 100644 --- a/tests/console/commands/test_show.py +++ b/tests/console/commands/test_show.py @@ -1368,103 +1368,6 @@ def test_show_all_shows_incompatible_package( assert tester.io.fetch_output() == expected -def test_show_hides_incompatible_package_with_duplicate( - tester: CommandTester, - poetry: Poetry, - installed: Repository, - repo: TestRepository, -): - poetry.package.add_dependency( - Factory.create_dependency("cachy", {"version": "0.1.0", "platform": "linux"}) - ) - poetry.package.add_dependency( - Factory.create_dependency("cachy", {"version": "0.1.1", "platform": "darwin"}) - ) - - poetry.locker.mock_lock_data( - { - "package": [ - { - "name": "cachy", - "version": "0.1.0", - "description": "Cachy package", - "optional": False, - "platform": "*", - "python-versions": "*", - "files": [], - }, - { - "name": "cachy", - "version": "0.1.1", - "description": "Cachy package", - "optional": False, - "platform": "*", - "python-versions": "*", - "files": [], - }, - ], - "metadata": {"content-hash": "123456789"}, - } - ) - - tester.execute() - - expected = """\ -cachy (!) 0.1.1 Cachy package -""" - - assert tester.io.fetch_output() == expected - - -def test_show_all_shows_all_duplicates( - tester: CommandTester, - poetry: Poetry, - installed: Repository, - repo: TestRepository, -): - poetry.package.add_dependency( - Factory.create_dependency("cachy", {"version": "0.1.0", "platform": "linux"}) - ) - poetry.package.add_dependency( - Factory.create_dependency("cachy", {"version": "0.1.1", "platform": "darwin"}) - ) - - poetry.locker.mock_lock_data( - { - "package": [ - { - "name": "cachy", - "version": "0.1.0", - "description": "Cachy package", - "optional": False, - "platform": "*", - "python-versions": "*", - "files": [], - }, - { - "name": "cachy", - "version": "0.1.1", - "description": "Cachy package", - "optional": False, - "platform": "*", - "python-versions": "*", - "files": [], - }, - ], - "metadata": {"content-hash": "123456789"}, - } - ) - - tester.execute("--all") - - expected = """\ -cachy 0.1.0 Cachy package -cachy (!) 0.1.1 Cachy package -""" - - assert tester.io.fetch_output() == expected - - def test_show_non_dev_with_basic_installed_packages( tester: CommandTester, poetry: Poetry, installed: Repository ): diff --git a/tests/fixtures/old_lock_path_dependency/poetry.lock b/tests/fixtures/old_lock_path_dependency/poetry.lock new file mode 100644 index 00000000000..45c6e792d7f --- /dev/null +++ b/tests/fixtures/old_lock_path_dependency/poetry.lock @@ -0,0 +1,20 @@ +# This file is automatically @generated by Poetry and should not be changed by hand. + +[[package]] +name = "quix" +version = "1.2.3" +description = "Some description." +category = "main" +optional = false +python-versions = "~2.7 || ^3.4" +files = [] +develop = true + +[package.source] +type = "directory" +url = "quix" + +[metadata] +lock-version = "2.0" +python-versions = "^3.8" +content-hash = "d2e1cf4390093213432fb8b58f90774a5247bfe860dedc2b023b27accc14cfad" diff --git a/tests/fixtures/old_lock_path_dependency/pyproject.toml b/tests/fixtures/old_lock_path_dependency/pyproject.toml new file mode 100644 index 00000000000..00547cfee26 --- /dev/null +++ b/tests/fixtures/old_lock_path_dependency/pyproject.toml @@ -0,0 +1,15 @@ +[tool.poetry] +name = "foobar" +version = "0.1.0" +description = "" +authors = ["Poetry Developer "] + +[tool.poetry.dependencies] +python = "^3.8" +quix = { path = "./quix", develop = true} + +[tool.poetry.dev-dependencies] + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/tests/fixtures/old_lock_path_dependency/quix/pyproject.toml b/tests/fixtures/old_lock_path_dependency/quix/pyproject.toml new file mode 100644 index 00000000000..1bec75cf124 --- /dev/null +++ b/tests/fixtures/old_lock_path_dependency/quix/pyproject.toml @@ -0,0 +1,11 @@ +[tool.poetry] +name = "quix" +version = "1.2.3" +description = "Some description." +authors = ["Poetry Maintainer "] +license = "MIT" + +# Requirements +[tool.poetry.dependencies] +python = "~2.7 || ^3.4" +sampleproject = ">=1.3.1" diff --git a/tests/puzzle/test_solver.py b/tests/puzzle/test_solver.py index 4eb6637acdc..601151eab16 100644 --- a/tests/puzzle/test_solver.py +++ b/tests/puzzle/test_solver.py @@ -3793,47 +3793,3 @@ def test_update_with_use_latest_vs_lock( {"job": "install", "package": package_a1}, ], ) - - -def test_solver_always_relocks_path_dependencies( - package: ProjectPackage, - repo: Repository, - pool: Pool, - io: NullIO, -): - path = ( - Path(__file__).parent.parent - / "fixtures" - / "git" - / "github.com" - / "demo" - / "demo" - ).as_posix() - - demo = Package( - "demo", - "0.1.2", - source_type="directory", - source_url=str(path), - ) - - package.add_dependency(Factory.create_dependency("demo", {"path": str(path)})) - - # transient dependencies of demo - pendulum = get_package("pendulum", "2.0.3") - repo.add_package(pendulum) - - solver = Solver(package, pool, installed=[demo], locked=[demo], io=io) - transaction = solver.solve() - - # we should re-lock demo and pick up any new transitive dependencies - # despite the fact that demo is already locked and installed - # because we can't identify path dependencies by any sort of immutable - # tag or published version - check_solver_result( - transaction, - [ - {"job": "install", "package": pendulum}, - {"job": "install", "package": demo, "skipped": True}, - ], - ) From e37ac27cdf19f94425fa8f5aacfa361865a002b4 Mon Sep 17 00:00:00 2001 From: Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com> Date: Sat, 29 Oct 2022 16:41:19 -0400 Subject: [PATCH 17/19] minimize changes --- src/poetry/factory.py | 3 +- src/poetry/installation/executor.py | 4 +- src/poetry/mixology/failure.py | 10 +-- src/poetry/utils/env.py | 6 -- src/poetry/utils/helpers.py | 4 +- tests/console/commands/test_show.py | 97 +++++++++++++++++++++++++++++ 6 files changed, 109 insertions(+), 15 deletions(-) diff --git a/src/poetry/factory.py b/src/poetry/factory.py index 46856736354..c20f224272c 100644 --- a/src/poetry/factory.py +++ b/src/poetry/factory.py @@ -257,7 +257,8 @@ def create_pyproject_from_package( constraint["optional"] = True if len(constraint) == 1 and "version" in constraint: - constraint = cast(str, constraint["version"]) + assert isinstance(constraint["version"], str) + constraint = constraint["version"] elif not constraint: constraint = "*" diff --git a/src/poetry/installation/executor.py b/src/poetry/installation/executor.py index 2dc53d53016..e831d4d0d34 100644 --- a/src/poetry/installation/executor.py +++ b/src/poetry/installation/executor.py @@ -13,7 +13,6 @@ from subprocess import CalledProcessError from typing import TYPE_CHECKING from typing import Any -from typing import cast from cleo.io.null_io import NullIO from poetry.core.packages.file_dependency import FileDependency @@ -771,7 +770,8 @@ def _save_url_reference(self, operation: Operation) -> None: for dist in self._env.site_packages.distributions( name=package.name, writable_only=True ): - dist_path = cast(Path, dist._path) # type: ignore[attr-defined] + dist_path = dist._path # type: ignore[attr-defined] + assert isinstance(dist_path, Path) url = dist_path / "direct_url.json" url.write_text(json.dumps(url_reference), encoding="utf-8") diff --git a/src/poetry/mixology/failure.py b/src/poetry/mixology/failure.py index f4629aa56c7..448d39786bb 100644 --- a/src/poetry/mixology/failure.py +++ b/src/poetry/mixology/failure.py @@ -1,7 +1,6 @@ from __future__ import annotations from typing import TYPE_CHECKING -from typing import cast from poetry.core.constraints.version import parse_constraint @@ -114,7 +113,8 @@ def _visit( conjunction = "So," if conclusion or incompatibility == self._root else "And" incompatibility_string = str(incompatibility) - cause: ConflictCause = cast(ConflictCause, incompatibility.cause) + cause = incompatibility.cause + assert isinstance(cause, ConflictCause) if isinstance(cause.conflict.cause, ConflictCause) and isinstance( cause.other.cause, ConflictCause @@ -198,7 +198,8 @@ def _visit( numbered=numbered, ) elif self._is_collapsible(derived): - derived_cause: ConflictCause = cast(ConflictCause, derived.cause) + derived_cause = derived.cause + assert isinstance(derived_cause, ConflictCause) if isinstance(derived_cause.conflict.cause, ConflictCause): collapsed_derived = derived_cause.conflict collapsed_ext = derived_cause.other @@ -233,7 +234,8 @@ def _is_collapsible(self, incompatibility: Incompatibility) -> bool: if self._derivations[incompatibility] > 1: return False - cause: ConflictCause = cast(ConflictCause, incompatibility.cause) + cause = incompatibility.cause + assert isinstance(cause, ConflictCause) if isinstance(cause.conflict.cause, ConflictCause) and isinstance( cause.other.cause, ConflictCause ): diff --git a/src/poetry/utils/env.py b/src/poetry/utils/env.py index d76972ca50d..7de0d60d2bc 100644 --- a/src/poetry/utils/env.py +++ b/src/poetry/utils/env.py @@ -451,12 +451,6 @@ def find( if value[-1] is True ] - def __getattr__(self, item: str) -> Any: - try: - return super().__getattribute__(item) - except AttributeError: - return getattr(self.path, item) - class EnvError(Exception): pass diff --git a/src/poetry/utils/helpers.py b/src/poetry/utils/helpers.py index b50d9e9acfc..c4ef660d547 100644 --- a/src/poetry/utils/helpers.py +++ b/src/poetry/utils/helpers.py @@ -12,7 +12,6 @@ from typing import Any from typing import Iterator from typing import Mapping -from typing import cast from poetry.utils.constants import REQUESTS_TIMEOUT @@ -189,7 +188,8 @@ def _get_win_folder_from_registry(csidl_name: str) -> str: ) dir, type = _winreg.QueryValueEx(key, shell_folder_name) - return cast(str, dir) + assert isinstance(dir, str) + return dir def _get_win_folder_with_ctypes(csidl_name: str) -> str: diff --git a/tests/console/commands/test_show.py b/tests/console/commands/test_show.py index bd6d8382ea9..a927cee5bf3 100644 --- a/tests/console/commands/test_show.py +++ b/tests/console/commands/test_show.py @@ -1368,6 +1368,103 @@ def test_show_all_shows_incompatible_package( assert tester.io.fetch_output() == expected +def test_show_hides_incompatible_package_with_duplicate( + tester: CommandTester, + poetry: Poetry, + installed: Repository, + repo: TestRepository, +): + poetry.package.add_dependency( + Factory.create_dependency("cachy", {"version": "0.1.0", "platform": "linux"}) + ) + poetry.package.add_dependency( + Factory.create_dependency("cachy", {"version": "0.1.1", "platform": "darwin"}) + ) + + poetry.locker.mock_lock_data( + { + "package": [ + { + "name": "cachy", + "version": "0.1.0", + "description": "Cachy package", + "optional": False, + "platform": "*", + "python-versions": "*", + "files": [], + }, + { + "name": "cachy", + "version": "0.1.1", + "description": "Cachy package", + "optional": False, + "platform": "*", + "python-versions": "*", + "files": [], + }, + ], + "metadata": {"content-hash": "123456789"}, + } + ) + + tester.execute() + + expected = """\ +cachy (!) 0.1.1 Cachy package +""" + + assert tester.io.fetch_output() == expected + + +def test_show_all_shows_all_duplicates( + tester: CommandTester, + poetry: Poetry, + installed: Repository, + repo: TestRepository, +): + poetry.package.add_dependency( + Factory.create_dependency("cachy", {"version": "0.1.0", "platform": "linux"}) + ) + poetry.package.add_dependency( + Factory.create_dependency("cachy", {"version": "0.1.1", "platform": "darwin"}) + ) + + poetry.locker.mock_lock_data( + { + "package": [ + { + "name": "cachy", + "version": "0.1.0", + "description": "Cachy package", + "optional": False, + "platform": "*", + "python-versions": "*", + "files": [], + }, + { + "name": "cachy", + "version": "0.1.1", + "description": "Cachy package", + "optional": False, + "platform": "*", + "python-versions": "*", + "files": [], + }, + ], + "metadata": {"content-hash": "123456789"}, + } + ) + + tester.execute("--all") + + expected = """\ +cachy 0.1.0 Cachy package +cachy (!) 0.1.1 Cachy package +""" + + assert tester.io.fetch_output() == expected + + def test_show_non_dev_with_basic_installed_packages( tester: CommandTester, poetry: Poetry, installed: Repository ): From 198aa6c06ce56b067d2a62d05250e8f195ccd31b Mon Sep 17 00:00:00 2001 From: Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com> Date: Sat, 29 Oct 2022 16:43:24 -0400 Subject: [PATCH 18/19] format --- src/poetry/installation/installer.py | 3 +-- tests/console/commands/test_lock.py | 7 +++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/poetry/installation/installer.py b/src/poetry/installation/installer.py index fe7673236b2..287a180a111 100644 --- a/src/poetry/installation/installer.py +++ b/src/poetry/installation/installer.py @@ -202,8 +202,7 @@ def _do_refresh(self) -> int: ) use_latest = [ - p.name for p in locked_repository.packages - if p.source_type == "directory" + p.name for p in locked_repository.packages if p.source_type == "directory" ] with solver.provider.use_source_root( diff --git a/tests/console/commands/test_lock.py b/tests/console/commands/test_lock.py index 89842491b10..7f78a22ec67 100644 --- a/tests/console/commands/test_lock.py +++ b/tests/console/commands/test_lock.py @@ -182,12 +182,15 @@ def test_lock_no_update_path_dependencies( repo.add_package(get_package("sampleproject", "1.3.1")) locker = Locker( - lock=poetry_with_nested_path_deps_old_lockfile.pyproject.file.path.parent / "poetry.lock", + lock=poetry_with_nested_path_deps_old_lockfile.pyproject.file.path.parent + / "poetry.lock", local_config=poetry_with_nested_path_deps_old_lockfile.locker._local_config, ) poetry_with_nested_path_deps_old_lockfile.set_locker(locker) - tester = command_tester_factory("lock", poetry=poetry_with_nested_path_deps_old_lockfile) + tester = command_tester_factory( + "lock", poetry=poetry_with_nested_path_deps_old_lockfile + ) tester.execute("--no-update") packages = locker.locked_repository().packages From 11600e3df8e4cea04c76fbeca91de094090e744c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Randy=20D=C3=B6ring?= <30527984+radoering@users.noreply.github.com> Date: Sun, 30 Oct 2022 14:24:11 +0100 Subject: [PATCH 19/19] add some comments --- src/poetry/installation/installer.py | 2 ++ tests/console/commands/test_lock.py | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/src/poetry/installation/installer.py b/src/poetry/installation/installer.py index 287a180a111..e3cdcb9e0ed 100644 --- a/src/poetry/installation/installer.py +++ b/src/poetry/installation/installer.py @@ -201,6 +201,8 @@ def _do_refresh(self) -> int: self._io, ) + # Always re-solve directory dependencies, otherwise we can't determine + # if anything has changed (and the lock file contains an invalid version). use_latest = [ p.name for p in locked_repository.packages if p.source_type == "directory" ] diff --git a/tests/console/commands/test_lock.py b/tests/console/commands/test_lock.py index 7f78a22ec67..a8ef7c2aac9 100644 --- a/tests/console/commands/test_lock.py +++ b/tests/console/commands/test_lock.py @@ -179,6 +179,12 @@ def test_lock_no_update_path_dependencies( poetry_with_nested_path_deps_old_lockfile: Poetry, repo: TestRepository, ): + """ + The lock file contains a variant of the directory dependency "quix" that does + not depend on "sampleproject". Although the version of "quix" has not been changed, + it should be re-solved because there is always only one valid version + of a directory dependency at any time. + """ repo.add_package(get_package("sampleproject", "1.3.1")) locker = Locker(