From 2ca6e08429b3c586112a68a478339759bf189752 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Randy=20D=C3=B6ring?= <30527984+radoering@users.noreply.github.com> Date: Sat, 4 Jun 2022 17:24:51 +0200 Subject: [PATCH] solver: fix special case where a direct origin dependency without extras is requested by the project and the same dependency with extras is requested by another dependency --- src/poetry/mixology/partial_solution.py | 11 +++++- tests/puzzle/test_solver.py | 52 +++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/src/poetry/mixology/partial_solution.py b/src/poetry/mixology/partial_solution.py index b078cc78549..3acb92ff78c 100644 --- a/src/poetry/mixology/partial_solution.py +++ b/src/poetry/mixology/partial_solution.py @@ -4,6 +4,7 @@ from poetry.mixology.assignment import Assignment from poetry.mixology.set_relation import SetRelation +from poetry.mixology.term import Term if TYPE_CHECKING: @@ -11,7 +12,6 @@ from poetry.core.packages.package import Package from poetry.mixology.incompatibility import Incompatibility - from poetry.mixology.term import Term class PartialSolution: @@ -146,6 +146,15 @@ def _register(self, assignment: Assignment) -> None: """ name = assignment.dependency.complete_name old_positive = self._positive.get(name) + if old_positive is None and assignment.dependency.features: + old_positive_without_features = self._positive.get( + assignment.dependency.name + ) + if old_positive_without_features is not None: + dep = old_positive_without_features.dependency.with_features( + assignment.dependency.features + ) + old_positive = Term(dep, is_positive=True) if old_positive is not None: value = old_positive.intersect(assignment) assert value is not None diff --git a/tests/puzzle/test_solver.py b/tests/puzzle/test_solver.py index 1efd5304564..52db060a7af 100644 --- a/tests/puzzle/test_solver.py +++ b/tests/puzzle/test_solver.py @@ -3539,3 +3539,55 @@ def test_solver_keeps_multiple_locked_dependencies_for_same_package( {"job": "install", "package": a12}, ], ) + + +def test_solver_direct_origin_dependency_with_extras_requested_by_other_package( + solver: Solver, repo: Repository, package: ProjectPackage +): + """ + Another package requires the same dependency with extras that is required + by the project as direct origin dependency without any extras. + """ + pendulum = get_package("pendulum", "2.0.3") # required by demo + cleo = get_package("cleo", "1.0.0") # required by demo[foo] + demo_foo = get_package("demo-foo", "1.2.3") + demo_foo.add_dependency( + Factory.create_dependency("demo", {"version": ">=0.1", "extras": ["foo"]}) + ) + repo.add_package(demo_foo) + repo.add_package(pendulum) + repo.add_package(cleo) + + path = ( + Path(__file__).parent.parent + / "fixtures" + / "git" + / "github.com" + / "demo" + / "demo" + ).as_posix() + + # project requires path dependency of demo while demo-foo requires demo[foo] + package.add_dependency(Factory.create_dependency("demo", {"path": path})) + package.add_dependency(Factory.create_dependency("demo-foo", "^1.2.3")) + + transaction = solver.solve() + + demo = Package("demo", "0.1.2", source_type="directory", source_url=path) + + ops = check_solver_result( + transaction, + [ + {"job": "install", "package": cleo}, + {"job": "install", "package": pendulum}, + {"job": "install", "package": demo}, + {"job": "install", "package": demo_foo}, + ], + ) + + op = ops[2] + + assert op.package.name == "demo" + assert op.package.version.text == "0.1.2" + assert op.package.source_type == "directory" + assert op.package.source_url == path