diff --git a/src/poetry/mixology/term.py b/src/poetry/mixology/term.py index 260567b1b7f..89580d8a541 100644 --- a/src/poetry/mixology/term.py +++ b/src/poetry/mixology/term.py @@ -127,17 +127,17 @@ def intersect(self, other: Term) -> Term | None: negative = other if self.is_positive() else self return self._non_empty_term( - positive.constraint.difference(negative.constraint), True + positive.constraint.difference(negative.constraint), True, other ) elif self.is_positive(): # foo ^1.0.0 ∩ foo >=1.5.0 <3.0.0 → foo ^1.5.0 return self._non_empty_term( - self.constraint.intersect(other.constraint), True + self.constraint.intersect(other.constraint), True, other ) else: # not foo ^1.0.0 ∩ not foo >=1.5.0 <3.0.0 → not foo >=1.0.0 <3.0.0 return self._non_empty_term( - self.constraint.union(other.constraint), False + self.constraint.union(other.constraint), False, other ) elif self.is_positive() != other.is_positive(): return self if self.is_positive() else other @@ -151,21 +151,45 @@ def difference(self, other: Term) -> Term | None: """ return self.intersect(other.inverse) + @staticmethod + def _is_direct_reference(dependency: Dependency) -> bool: + return dependency.source_type in ["directory", "file", "url", "git"] + def _compatible_dependency(self, other: Dependency) -> bool: compatible: bool = ( self.dependency.is_root or other.is_root or other.is_same_package_as(self.dependency) ) + + if ( + not compatible + and not self._is_direct_reference(self.dependency) + and self._is_direct_reference(other) + ): + # we do this here to indicate direct reference dependencies are compatible + # with NVR dependencies + return ( + self.dependency.complete_name == other.complete_name + and self.dependency.constraint.allows_all(other.constraint) + ) + return compatible def _non_empty_term( - self, constraint: VersionConstraint, is_positive: bool + self, constraint: VersionConstraint, is_positive: bool, other: Term ) -> Term | None: if constraint.is_empty(): return None - return Term(self.dependency.with_constraint(constraint), is_positive) + # when creating a new term prefer direct-reference dependencies + dependency = ( + other.dependency + if not self._is_direct_reference(self.dependency) + and self._is_direct_reference(other.dependency) + else self.dependency + ) + return Term(dependency.with_constraint(constraint), is_positive) def __str__(self) -> str: prefix = "not " if not self.is_positive() else "" diff --git a/tests/puzzle/test_solver.py b/tests/puzzle/test_solver.py index fe47e8bd220..2ca281be0e6 100644 --- a/tests/puzzle/test_solver.py +++ b/tests/puzzle/test_solver.py @@ -22,6 +22,7 @@ from poetry.repositories.pool import Pool from poetry.repositories.repository import Repository from poetry.utils.env import MockEnv +from tests.compat import is_poetry_core_1_1_0a7_compat from tests.helpers import get_dependency from tests.helpers import get_package from tests.repositories.test_legacy_repository import ( @@ -1383,7 +1384,10 @@ def test_solver_duplicate_dependencies_different_sources_types_are_preserved( assert len(complete_package.all_requires) == 2 - pypi, git = complete_package.all_requires + if is_poetry_core_1_1_0a7_compat: + pypi, git = complete_package.all_requires + else: + git, pypi = complete_package.all_requires assert isinstance(pypi, Dependency) assert pypi == dependency_pypi