From 5eeb7c2bd5adefec59504def1d6277f0094c64ef Mon Sep 17 00:00:00 2001 From: "nicolas.fraison" Date: Thu, 21 Apr 2022 10:06:21 +0200 Subject: [PATCH] repository: do not call secondary repositories if package is available in primary ones --- src/poetry/factory.py | 5 +-- src/poetry/repositories/cached.py | 4 +-- src/poetry/repositories/http.py | 3 +- src/poetry/repositories/legacy_repository.py | 4 +-- src/poetry/repositories/pool.py | 2 ++ src/poetry/repositories/pypi_repository.py | 15 ++++---- src/poetry/repositories/repository.py | 26 +++++++------- tests/repositories/test_pool.py | 37 ++++++++++++++++++++ 8 files changed, 68 insertions(+), 28 deletions(-) diff --git a/src/poetry/factory.py b/src/poetry/factory.py index 5ff5be0b632..2a4a1e2194b 100644 --- a/src/poetry/factory.py +++ b/src/poetry/factory.py @@ -126,9 +126,9 @@ def configure_sources( cls, poetry: Poetry, sources: list[dict[str, str]], config: Config, io: IO ) -> None: for source in sources: - repository = cls.create_legacy_repository(source, config) is_default = bool(source.get("default", False)) is_secondary = bool(source.get("secondary", False)) + repository = cls.create_legacy_repository(source, config, is_secondary) if io.is_debug(): message = f"Adding repository {repository.name} ({repository.url})" if is_default: @@ -154,7 +154,7 @@ def configure_sources( @classmethod def create_legacy_repository( - cls, source: dict[str, str], auth_config: Config + cls, source: dict[str, str], auth_config: Config, secondary: bool ) -> LegacyRepository: from poetry.repositories.legacy_repository import LegacyRepository from poetry.utils.helpers import get_cert @@ -175,6 +175,7 @@ def create_legacy_repository( config=auth_config, cert=get_cert(auth_config, name), client_cert=get_client_cert(auth_config, name), + secondary=secondary, ) @classmethod diff --git a/src/poetry/repositories/cached.py b/src/poetry/repositories/cached.py index 12d57fbacf4..693a8ef22b9 100644 --- a/src/poetry/repositories/cached.py +++ b/src/poetry/repositories/cached.py @@ -21,8 +21,8 @@ class CachedRepository(Repository, ABC): CACHE_VERSION = parse_constraint("1.0.0") - def __init__(self, name: str, cache_group: str, disable_cache: bool = False): - super().__init__(name) + def __init__(self, name: str, cache_group: str, disable_cache: bool = False, secondary: bool = False): + super().__init__(name, secondary=secondary) self._disable_cache = disable_cache self._cache_dir = REPOSITORY_CACHE_DIR / name self._cache = CacheManager( diff --git a/src/poetry/repositories/http.py b/src/poetry/repositories/http.py index 4561e6fff02..32b2bf32056 100644 --- a/src/poetry/repositories/http.py +++ b/src/poetry/repositories/http.py @@ -43,8 +43,9 @@ def __init__( disable_cache: bool = False, cert: Path | None = None, client_cert: Path | None = None, + secondary: bool = False ) -> None: - super().__init__(name, "_http", disable_cache) + super().__init__(name, "_http", disable_cache, secondary) self._url = url self._client_cert = client_cert self._cert = cert diff --git a/src/poetry/repositories/legacy_repository.py b/src/poetry/repositories/legacy_repository.py index a065ab5bafc..4cf0bd25df7 100644 --- a/src/poetry/repositories/legacy_repository.py +++ b/src/poetry/repositories/legacy_repository.py @@ -30,12 +30,12 @@ def __init__( disable_cache: bool = False, cert: Path | None = None, client_cert: Path | None = None, + secondary: bool = False ) -> None: if name == "pypi": raise ValueError("The name [pypi] is reserved for repositories") - super().__init__( - name, url.rstrip("/"), config, disable_cache, cert, client_cert + name, url.rstrip("/"), config, disable_cache, cert, client_cert, secondary ) def find_packages(self, dependency: Dependency) -> list[Package]: diff --git a/src/poetry/repositories/pool.py b/src/poetry/repositories/pool.py index 40acfc4c026..caa07ea56cf 100644 --- a/src/poetry/repositories/pool.py +++ b/src/poetry/repositories/pool.py @@ -169,6 +169,8 @@ def find_packages(self, dependency: Dependency) -> list[Package]: packages = [] for repo in self._repositories: + if repo.secondary and packages: + continue packages += repo.find_packages(dependency) return packages diff --git a/src/poetry/repositories/pypi_repository.py b/src/poetry/repositories/pypi_repository.py index cb35e69c69b..3c80c38abd0 100644 --- a/src/poetry/repositories/pypi_repository.py +++ b/src/poetry/repositories/pypi_repository.py @@ -17,25 +17,24 @@ from poetry.repositories.http import HTTPRepository from poetry.utils._compat import to_str - cache_control_logger.setLevel(logging.ERROR) logger = logging.getLogger(__name__) - if TYPE_CHECKING: from poetry.core.packages.dependency import Dependency class PyPiRepository(HTTPRepository): def __init__( - self, - url: str = "https://pypi.org/", - disable_cache: bool = False, - fallback: bool = True, + self, + url: str = "https://pypi.org/", + disable_cache: bool = False, + fallback: bool = True, + secondary: bool = False, ) -> None: super().__init__( - "PyPI", url.rstrip("/") + "/simple/", disable_cache=disable_cache + "PyPI", url.rstrip("/") + "/simple/", disable_cache=disable_cache, secondary=secondary ) self._base_url = url @@ -162,7 +161,7 @@ def find_links_for_package(self, package: Package) -> list[Link]: return links def _get_release_info( - self, name: str, version: str + self, name: str, version: str ) -> dict[str, str | list[str] | None]: from poetry.inspection.info import PackageInfo diff --git a/src/poetry/repositories/repository.py b/src/poetry/repositories/repository.py index ed624ad02f6..9908c7f4f20 100644 --- a/src/poetry/repositories/repository.py +++ b/src/poetry/repositories/repository.py @@ -8,7 +8,6 @@ from poetry.core.semver.version_constraint import VersionConstraint from poetry.core.semver.version_range import VersionRange - if TYPE_CHECKING: from poetry.core.packages.dependency import Dependency from poetry.core.packages.package import Package @@ -17,9 +16,10 @@ class Repository: - def __init__(self, name: str = None, packages: list[Package] = None) -> None: + def __init__(self, name: str = None, packages: list[Package] = None, secondary: bool = False) -> None: self._name = name self._packages: list[Package] = [] + self.secondary = secondary for package in packages or []: self.add_package(package) @@ -42,9 +42,9 @@ def find_packages(self, dependency: Dependency) -> list[Package]: for package in self.packages: if dependency.name == package.name: if ( - package.is_prerelease() - and not allow_prereleases - and not package.source_type + package.is_prerelease() + and not allow_prereleases + and not package.source_type ): # If prereleases are not allowed and the package is a prerelease # and is a standard package then we skip it @@ -54,8 +54,8 @@ def find_packages(self, dependency: Dependency) -> list[Package]: continue if constraint.allows(package.version) or ( - package.is_prerelease() - and constraint.allows(package.version.next_patch()) + package.is_prerelease() + and constraint.allows(package.version.next_patch()) ): packages.append(package) @@ -93,7 +93,7 @@ def search(self, query: str) -> list[Package]: @staticmethod def _get_constraints_from_dependency( - dependency: Dependency, + dependency: Dependency, ) -> tuple[VersionTypes, bool]: constraint = dependency.constraint if constraint is None: @@ -104,10 +104,10 @@ def _get_constraints_from_dependency( allow_prereleases = dependency.allows_prereleases() if isinstance(constraint, VersionRange) and ( - constraint.max is not None - and constraint.max.is_unstable() - or constraint.min is not None - and constraint.min.is_unstable() + constraint.max is not None + and constraint.max.is_unstable() + or constraint.min is not None + and constraint.min.is_unstable() ): allow_prereleases = True @@ -125,7 +125,7 @@ def find_links_for_package(self, package: Package) -> list[Link]: return [] def package( - self, name: str, version: str, extras: list[str] | None = None + self, name: str, version: str, extras: list[str] | None = None ) -> Package: name = name.lower() diff --git a/tests/repositories/test_pool.py b/tests/repositories/test_pool.py index 9d2b77ac241..7ee96c17ebd 100644 --- a/tests/repositories/test_pool.py +++ b/tests/repositories/test_pool.py @@ -1,11 +1,14 @@ from __future__ import annotations +from unittest.mock import Mock + import pytest from poetry.repositories import Pool from poetry.repositories import Repository from poetry.repositories.exceptions import PackageNotFound from poetry.repositories.legacy_repository import LegacyRepository +from poetry.core.packages.dependency import Dependency def test_pool_raises_package_not_found_when_no_package_is_found(): @@ -71,3 +74,37 @@ def test_repository_with_normal_default_and_secondary_repositories(): assert pool.repository("foo") is repo1 assert pool.repository("bar") is repo2 assert pool.has_default() + + +def test_find_packages_do_not_call_secondary_if_primary_find_package(): + secondary = LegacyRepository("secondary", "https://secondary.com", secondary=True) + repo2 = LegacyRepository("bar", "https://bar.baz") + + pool = Pool() + pool.add_repository(secondary, secondary=True) + pool.add_repository(repo2) + + dependency = Dependency("test", "1.0.0") + + repo2.find_packages = Mock(return_value=["t"]) + secondary.find_packages = Mock(return_value=["t"]) + pool.find_packages(dependency) + repo2.find_packages.assert_called_once_with(dependency) + secondary.find_packages.assert_not_called() + + +def test_find_packages_call_secondary_if_primary_do_not_find_package(): + secondary = LegacyRepository("secondary", "https://secondary.com", secondary=True) + repo2 = LegacyRepository("bar", "https://bar.baz") + + pool = Pool() + pool.add_repository(secondary, secondary=True) + pool.add_repository(repo2) + + dependency = Dependency("test", "1.0.0") + + repo2.find_packages = Mock(return_value=[]) + secondary.find_packages = Mock(return_value=["t"]) + pool.find_packages(dependency) + repo2.find_packages.assert_called_once_with(dependency) + secondary.find_packages.assert_called_once_with(dependency)