From d539a0be457f61b65e90817e52be817f8283ef22 Mon Sep 17 00:00:00 2001 From: David Hotham Date: Sat, 8 Oct 2022 19:20:52 +0100 Subject: [PATCH] Package.satisfies() considers sources more carefully --- src/poetry/core/packages/package.py | 45 ++++++++++++++++++++++++----- tests/packages/test_package.py | 28 ++++++++++++++++++ 2 files changed, 66 insertions(+), 7 deletions(-) diff --git a/src/poetry/core/packages/package.py b/src/poetry/core/packages/package.py index 009780e78..1d2efa87c 100644 --- a/src/poetry/core/packages/package.py +++ b/src/poetry/core/packages/package.py @@ -556,16 +556,47 @@ def satisfies( """ Helper method to check if this package satisfies a given dependency. - This is determined by assessing if this instance provides the package and - features specified by the given dependency. Further, version and source - types are checked. + This is determined by assessing if this instance provides the package specified + by the given dependency. Further, version and source types are checked. """ - if not self.provides(dependency) or not dependency.constraint.allows( - self.version - ): + if self.name != dependency.name: + return False + + if not dependency.constraint.allows(self.version): return False - return ignore_source_type or self.is_same_source_as(dependency) + if not ignore_source_type and not self.source_satisfies(dependency): + return False + + return True + + def source_satisfies(self, dependency: Dependency) -> bool: + """Determine whether this package's source satisfies the given dependency.""" + if dependency.source_type is None: + if dependency.source_name is None: + # The dependency doesn't care about the source, so this package + # certainly satisfies it. + return True + + # The dependency specifies a source_name but not a type: it wants either + # pypi or a legacy repository. + # + # - If this package has no source type then it's from pypi, so it + # matches if and only if that's what the dependency wants + # - Else this package is a match if and only if it is from the desired + # repository + if self.source_type is None: + return dependency.source_name.lower() == "pypi" + + return ( + self.source_type == "legacy" + and self.source_reference is not None + and self.source_reference.lower() == dependency.source_name.lower() + ) + + # The dependency specifies a source: this package matches if and only if it is + # from that source. + return dependency.is_same_source_as(self) def __eq__(self, other: object) -> bool: if not isinstance(other, Package): diff --git a/tests/packages/test_package.py b/tests/packages/test_package.py index 77787ee64..91da5872f 100644 --- a/tests/packages/test_package.py +++ b/tests/packages/test_package.py @@ -562,6 +562,34 @@ def test_package_satisfies( assert package.satisfies(dependency, ignore_source_type) == result +@pytest.mark.parametrize( + ("package_repo", "dependency_repo", "result"), + [ + ("pypi", None, True), + ("private", None, True), + ("pypi", "pypi", True), + ("private", "private", True), + ("pypi", "private", False), + ("private", "pypi", False), + ], +) +def test_package_satisfies_on_repositories( + package_repo: str, + dependency_repo: str | None, + result: bool, +) -> None: + source_type = None if package_repo == "pypi" else "legacy" + source_reference = None if package_repo == "pypi" else package_repo + package = Package( + "foo", "0.1.0", source_type=source_type, source_reference=source_reference + ) + + dependency = Dependency("foo", ">=0.1.0") + dependency.source_name = dependency_repo + + assert package.satisfies(dependency) == result + + def test_package_pep592_default_not_yanked() -> None: package = Package("foo", "1.0")