diff --git a/src/poetry/console/commands/init.py b/src/poetry/console/commands/init.py index 9204c487f38..95ff7e0a78d 100644 --- a/src/poetry/console/commands/init.py +++ b/src/poetry/console/commands/init.py @@ -434,14 +434,17 @@ def _parse_requirements(self, requirements: list[str]) -> list[dict[str, Any]]: try: cwd = self.poetry.file.parent + artifact_cache = self.poetry.pool.artifact_cache except (PyProjectException, RuntimeError): cwd = Path.cwd() + artifact_cache = None return [ parse_dependency_specification( requirement=requirement, env=self.env if isinstance(self, EnvCommand) else None, cwd=cwd, + artifact_cache=artifact_cache, ) for requirement in requirements ] diff --git a/src/poetry/console/commands/show.py b/src/poetry/console/commands/show.py index 1b8b03e718b..e5fe230b591 100644 --- a/src/poetry/console/commands/show.py +++ b/src/poetry/console/commands/show.py @@ -8,6 +8,7 @@ from poetry.console.commands.env_command import EnvCommand from poetry.console.commands.group_command import GroupCommand +from poetry.utils.cache import ArtifactCache if TYPE_CHECKING: @@ -213,7 +214,12 @@ def _display_packages_information( from poetry.utils.helpers import get_package_version_display_string locked_packages = locked_repository.packages - pool = RepositoryPool(ignore_repository_names=True) + pool = RepositoryPool( + ignore_repository_names=True, + artifact_cache=ArtifactCache( + cache_dir=self.poetry.config.artifacts_cache_directory + ), + ) pool.add_repository(locked_repository) solver = Solver( root, @@ -547,7 +553,11 @@ def find_latest_package( if package.is_direct_origin(): for dep in requires: if dep.name == package.name and dep.source_type == package.source_type: - provider = Provider(root, self.poetry.pool, NullIO()) + provider = Provider( + root, + self.poetry.pool, + NullIO(), + ) return provider.search_for_direct_origin_dependency(dep) allow_prereleases = False diff --git a/src/poetry/factory.py b/src/poetry/factory.py index a9a488d6bf7..24c814d5688 100644 --- a/src/poetry/factory.py +++ b/src/poetry/factory.py @@ -21,6 +21,7 @@ from poetry.plugins.plugin_manager import PluginManager from poetry.poetry import Poetry from poetry.toml.file import TOMLFile +from poetry.utils.cache import ArtifactCache if TYPE_CHECKING: @@ -118,7 +119,7 @@ def get_package(cls, name: str, version: str) -> ProjectPackage: @classmethod def create_pool( cls, - auth_config: Config, + config: Config, sources: Iterable[dict[str, Any]] = (), io: IO | None = None, disable_cache: bool = False, @@ -132,11 +133,13 @@ def create_pool( if disable_cache: logger.debug("Disabling source caches") - pool = RepositoryPool() + pool = RepositoryPool( + artifact_cache=ArtifactCache(cache_dir=config.artifacts_cache_directory) + ) for source in sources: repository = cls.create_package_source( - source, auth_config, disable_cache=disable_cache + source, config, disable_cache=disable_cache ) priority = Priority[source.get("priority", Priority.PRIMARY.name).upper()] if "default" in source or "secondary" in source: @@ -184,7 +187,7 @@ def create_pool( @classmethod def create_package_source( - cls, source: dict[str, str], auth_config: Config, disable_cache: bool = False + cls, source: dict[str, str], config: Config, disable_cache: bool = False ) -> LegacyRepository: from poetry.repositories.legacy_repository import LegacyRepository from poetry.repositories.single_page_repository import SinglePageRepository @@ -206,7 +209,7 @@ def create_package_source( return repository_class( name, url, - config=auth_config, + config=config, disable_cache=disable_cache, ) diff --git a/src/poetry/installation/installer.py b/src/poetry/installation/installer.py index c049f0da500..3a27f0ee16e 100644 --- a/src/poetry/installation/installer.py +++ b/src/poetry/installation/installer.py @@ -16,6 +16,7 @@ from poetry.repositories import RepositoryPool from poetry.repositories.installed_repository import InstalledRepository from poetry.repositories.lockfile_repository import LockfileRepository +from poetry.utils.cache import ArtifactCache from poetry.utils.extras import get_extra_package_names from poetry.utils.helpers import pluralize @@ -81,6 +82,7 @@ def __init__( installed = self._get_installed() self._installed_repository = installed + self._artifact_cache = ArtifactCache(cache_dir=config.artifacts_cache_directory) @property def executor(self) -> Executor: @@ -314,7 +316,9 @@ def _do_install(self) -> int: ) # We resolve again by only using the lock file - pool = RepositoryPool(ignore_repository_names=True) + pool = RepositoryPool( + ignore_repository_names=True, artifact_cache=self._artifact_cache + ) # Making a new repo containing the packages # newly resolved and the ones from the current lock file diff --git a/src/poetry/puzzle/provider.py b/src/poetry/puzzle/provider.py index 28558ab4607..89af086e272 100644 --- a/src/poetry/puzzle/provider.py +++ b/src/poetry/puzzle/provider.py @@ -17,6 +17,7 @@ from cleo.ui.progress_indicator import ProgressIndicator from poetry.core.constraints.version import EmptyConstraint from poetry.core.constraints.version import Version +from poetry.core.packages.utils.link import Link from poetry.core.packages.utils.utils import get_python_constraint_from_marker from poetry.core.version.markers import AnyMarker from poetry.core.version.markers import EmptyMarker @@ -56,6 +57,7 @@ from poetry.core.version.markers import BaseMarker from poetry.repositories import RepositoryPool + from poetry.utils.cache import ArtifactCache from poetry.utils.env import Env @@ -439,7 +441,7 @@ def get_package_from_directory(cls, directory: Path) -> Package: return PackageInfo.from_directory(path=directory).to_package(root_dir=directory) def _search_for_url(self, dependency: URLDependency) -> Package: - package = self.get_package_from_url(dependency.url) + package = self.get_package_from_url(dependency.url, self._pool.artifact_cache) self.validate_package_for_dependency(dependency=dependency, package=package) @@ -454,15 +456,33 @@ def _search_for_url(self, dependency: URLDependency) -> Package: return package @classmethod - def get_package_from_url(cls, url: str) -> Package: + def get_package_from_url( + cls, url: str, artifact_cache: ArtifactCache | None = None + ) -> Package: file_name = os.path.basename(urllib.parse.urlparse(url).path) - with tempfile.TemporaryDirectory() as temp_dir: - dest = Path(temp_dir) / file_name - download_file(url, dest) - package = cls.get_package_from_file(dest) + link = Link(url) + artifact = ( + artifact_cache.get_cached_archive_for_link(Link(url), strict=True) + if artifact_cache + else None + ) + + with tempfile.TemporaryDirectory() as tmp_dir: + if not artifact: + if artifact_cache: + artifact = ( + artifact_cache.get_cache_directory_for_link(link) / file_name + ) + artifact.parent.mkdir(parents=True, exist_ok=True) + else: + artifact = Path(tmp_dir) / file_name + + download_file(url, artifact) + + package = cls.get_package_from_file(artifact) package.files = [ - {"file": file_name, "hash": "sha256:" + get_file_hash(dest)} + {"file": file_name, "hash": "sha256:" + get_file_hash(artifact)} ] package._source_type = "url" diff --git a/src/poetry/repositories/repository_pool.py b/src/poetry/repositories/repository_pool.py index 304f7e9ed33..b2a5bf06c84 100644 --- a/src/poetry/repositories/repository_pool.py +++ b/src/poetry/repositories/repository_pool.py @@ -18,6 +18,7 @@ from poetry.core.packages.package import Package from poetry.repositories.repository import Repository + from poetry.utils.cache import ArtifactCache class Priority(IntEnum): @@ -40,6 +41,7 @@ def __init__( self, repositories: list[Repository] | None = None, ignore_repository_names: bool = False, + artifact_cache: ArtifactCache | None = None, ) -> None: super().__init__("poetry-repository-pool") self._repositories: OrderedDict[str, PrioritizedRepository] = OrderedDict() @@ -50,6 +52,8 @@ def __init__( for repository in repositories: self.add_repository(repository) + self._artifact_cache = artifact_cache + @property def repositories(self) -> list[Repository]: """ @@ -77,6 +81,10 @@ def _sorted_repositories(self) -> list[PrioritizedRepository]: self._repositories.values(), key=lambda prio_repo: prio_repo.priority ) + @property + def artifact_cache(self) -> ArtifactCache | None: + return self._artifact_cache + def has_default(self) -> bool: return self._contains_priority(Priority.DEFAULT) diff --git a/src/poetry/utils/dependency_specification.py b/src/poetry/utils/dependency_specification.py index adcfd3d55ab..2219660d0ed 100644 --- a/src/poetry/utils/dependency_specification.py +++ b/src/poetry/utils/dependency_specification.py @@ -22,6 +22,7 @@ if TYPE_CHECKING: from poetry.core.packages.vcs_dependency import VCSDependency + from poetry.utils.cache import ArtifactCache from poetry.utils.env import Env @@ -58,7 +59,9 @@ def _parse_dependency_specification_git_url( def _parse_dependency_specification_url( - requirement: str, env: Env | None = None + requirement: str, + env: Env | None = None, + artifact_cache: ArtifactCache | None = None, ) -> DependencySpec | None: url_parsed = urllib.parse.urlparse(requirement) if not (url_parsed.scheme and url_parsed.netloc): @@ -68,7 +71,7 @@ def _parse_dependency_specification_url( return _parse_dependency_specification_git_url(requirement, env) if url_parsed.scheme in ["http", "https"]: - package = Provider.get_package_from_url(requirement) + package = Provider.get_package_from_url(requirement, artifact_cache) assert package.source_url is not None return {"name": package.name, "url": package.source_url} @@ -196,7 +199,10 @@ def pep508_to_dependency_specification(requirement: str) -> DependencySpec | Non def parse_dependency_specification( - requirement: str, env: Env | None = None, cwd: Path | None = None + requirement: str, + env: Env | None = None, + cwd: Path | None = None, + artifact_cache: ArtifactCache | None = None, ) -> DependencySpec: requirement = requirement.strip() cwd = cwd or Path.cwd() @@ -213,7 +219,9 @@ def parse_dependency_specification( requirement, _ = requirement.split("[") specification = ( - _parse_dependency_specification_url(requirement, env=env) + _parse_dependency_specification_url( + requirement, env=env, artifact_cache=artifact_cache + ) or _parse_dependency_specification_path(requirement, cwd=cwd) or _parse_dependency_specification_simple(requirement) )