mirror of
https://github.com/pypa/pip
synced 2023-12-13 21:30:23 +01:00
Merge pull request #11137 from sbidoul/download-info-sbi
Add download_info: DirectUrl to InstallRequirement
This commit is contained in:
commit
e58a8a591b
3
news/11137.feature.rst
Normal file
3
news/11137.feature.rst
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
Record in wheel cache entries the URL of the original artifiact that was downloaded
|
||||||
|
to build the cached wheels. The record is named ``origin.json`` and uses the PEP 610
|
||||||
|
Direct URL format.
|
|
@ -5,12 +5,14 @@ import hashlib
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
from pathlib import Path
|
||||||
from typing import Any, Dict, List, Optional, Set
|
from typing import Any, Dict, List, Optional, Set
|
||||||
|
|
||||||
from pip._vendor.packaging.tags import Tag, interpreter_name, interpreter_version
|
from pip._vendor.packaging.tags import Tag, interpreter_name, interpreter_version
|
||||||
from pip._vendor.packaging.utils import canonicalize_name
|
from pip._vendor.packaging.utils import canonicalize_name
|
||||||
|
|
||||||
from pip._internal.exceptions import InvalidWheelFilename
|
from pip._internal.exceptions import InvalidWheelFilename
|
||||||
|
from pip._internal.models.direct_url import DirectUrl
|
||||||
from pip._internal.models.format_control import FormatControl
|
from pip._internal.models.format_control import FormatControl
|
||||||
from pip._internal.models.link import Link
|
from pip._internal.models.link import Link
|
||||||
from pip._internal.models.wheel import Wheel
|
from pip._internal.models.wheel import Wheel
|
||||||
|
@ -19,6 +21,8 @@ from pip._internal.utils.urls import path_to_url
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
ORIGIN_JSON_NAME = "origin.json"
|
||||||
|
|
||||||
|
|
||||||
def _hash_dict(d: Dict[str, str]) -> str:
|
def _hash_dict(d: Dict[str, str]) -> str:
|
||||||
"""Return a stable sha224 of a dictionary."""
|
"""Return a stable sha224 of a dictionary."""
|
||||||
|
@ -204,6 +208,10 @@ class CacheEntry:
|
||||||
):
|
):
|
||||||
self.link = link
|
self.link = link
|
||||||
self.persistent = persistent
|
self.persistent = persistent
|
||||||
|
self.origin: Optional[DirectUrl] = None
|
||||||
|
origin_direct_url_path = Path(self.link.file_path).parent / ORIGIN_JSON_NAME
|
||||||
|
if origin_direct_url_path.exists():
|
||||||
|
self.origin = DirectUrl.from_json(origin_direct_url_path.read_text())
|
||||||
|
|
||||||
|
|
||||||
class WheelCache(Cache):
|
class WheelCache(Cache):
|
||||||
|
@ -262,3 +270,20 @@ class WheelCache(Cache):
|
||||||
return CacheEntry(retval, persistent=False)
|
return CacheEntry(retval, persistent=False)
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def record_download_origin(cache_dir: str, download_info: DirectUrl) -> None:
|
||||||
|
origin_path = Path(cache_dir) / ORIGIN_JSON_NAME
|
||||||
|
if origin_path.is_file():
|
||||||
|
origin = DirectUrl.from_json(origin_path.read_text())
|
||||||
|
# TODO: use DirectUrl.equivalent when https://github.com/pypa/pip/pull/10564
|
||||||
|
# is merged.
|
||||||
|
if origin.url != download_info.url:
|
||||||
|
logger.warning(
|
||||||
|
"Origin URL %s in cache entry %s does not match download URL %s. "
|
||||||
|
"This is likely a pip bug or a cache corruption issue.",
|
||||||
|
origin.url,
|
||||||
|
cache_dir,
|
||||||
|
download_info.url,
|
||||||
|
)
|
||||||
|
origin_path.write_text(download_info.to_json())
|
||||||
|
|
|
@ -25,6 +25,7 @@ from pip._internal.exceptions import (
|
||||||
)
|
)
|
||||||
from pip._internal.index.package_finder import PackageFinder
|
from pip._internal.index.package_finder import PackageFinder
|
||||||
from pip._internal.metadata import BaseDistribution
|
from pip._internal.metadata import BaseDistribution
|
||||||
|
from pip._internal.models.direct_url import ArchiveInfo
|
||||||
from pip._internal.models.link import Link
|
from pip._internal.models.link import Link
|
||||||
from pip._internal.models.wheel import Wheel
|
from pip._internal.models.wheel import Wheel
|
||||||
from pip._internal.network.download import BatchDownloader, Downloader
|
from pip._internal.network.download import BatchDownloader, Downloader
|
||||||
|
@ -35,9 +36,18 @@ from pip._internal.network.lazy_wheel import (
|
||||||
from pip._internal.network.session import PipSession
|
from pip._internal.network.session import PipSession
|
||||||
from pip._internal.operations.build.build_tracker import BuildTracker
|
from pip._internal.operations.build.build_tracker import BuildTracker
|
||||||
from pip._internal.req.req_install import InstallRequirement
|
from pip._internal.req.req_install import InstallRequirement
|
||||||
|
from pip._internal.utils.direct_url_helpers import (
|
||||||
|
direct_url_for_editable,
|
||||||
|
direct_url_from_link,
|
||||||
|
)
|
||||||
from pip._internal.utils.hashes import Hashes, MissingHashes
|
from pip._internal.utils.hashes import Hashes, MissingHashes
|
||||||
from pip._internal.utils.logging import indent_log
|
from pip._internal.utils.logging import indent_log
|
||||||
from pip._internal.utils.misc import display_path, hide_url, is_installable_dir
|
from pip._internal.utils.misc import (
|
||||||
|
display_path,
|
||||||
|
hash_file,
|
||||||
|
hide_url,
|
||||||
|
is_installable_dir,
|
||||||
|
)
|
||||||
from pip._internal.utils.temp_dir import TempDirectory
|
from pip._internal.utils.temp_dir import TempDirectory
|
||||||
from pip._internal.utils.unpacking import unpack_file
|
from pip._internal.utils.unpacking import unpack_file
|
||||||
from pip._internal.vcs import vcs
|
from pip._internal.vcs import vcs
|
||||||
|
@ -489,6 +499,23 @@ class RequirementPreparer:
|
||||||
hashes.check_against_path(file_path)
|
hashes.check_against_path(file_path)
|
||||||
local_file = File(file_path, content_type=None)
|
local_file = File(file_path, content_type=None)
|
||||||
|
|
||||||
|
# If download_info is set, we got it from the wheel cache.
|
||||||
|
if req.download_info is None:
|
||||||
|
# Editables don't go through this function (see
|
||||||
|
# prepare_editable_requirement).
|
||||||
|
assert not req.editable
|
||||||
|
req.download_info = direct_url_from_link(link, req.source_dir)
|
||||||
|
# Make sure we have a hash in download_info. If we got it as part of the
|
||||||
|
# URL, it will have been verified and we can rely on it. Otherwise we
|
||||||
|
# compute it from the downloaded file.
|
||||||
|
if (
|
||||||
|
isinstance(req.download_info.info, ArchiveInfo)
|
||||||
|
and not req.download_info.info.hash
|
||||||
|
and local_file
|
||||||
|
):
|
||||||
|
hash = hash_file(local_file.path)[0].hexdigest()
|
||||||
|
req.download_info.info.hash = f"sha256={hash}"
|
||||||
|
|
||||||
# For use in later processing,
|
# For use in later processing,
|
||||||
# preserve the file path on the requirement.
|
# preserve the file path on the requirement.
|
||||||
if local_file:
|
if local_file:
|
||||||
|
@ -547,6 +574,8 @@ class RequirementPreparer:
|
||||||
)
|
)
|
||||||
req.ensure_has_source_dir(self.src_dir)
|
req.ensure_has_source_dir(self.src_dir)
|
||||||
req.update_editable()
|
req.update_editable()
|
||||||
|
assert req.source_dir
|
||||||
|
req.download_info = direct_url_for_editable(req.unpacked_source_directory)
|
||||||
|
|
||||||
dist = _get_prepared_distribution(
|
dist = _get_prepared_distribution(
|
||||||
req,
|
req,
|
||||||
|
|
|
@ -26,6 +26,7 @@ from pip._internal.metadata import (
|
||||||
get_default_environment,
|
get_default_environment,
|
||||||
get_directory_distribution,
|
get_directory_distribution,
|
||||||
)
|
)
|
||||||
|
from pip._internal.models.direct_url import DirectUrl
|
||||||
from pip._internal.models.link import Link
|
from pip._internal.models.link import Link
|
||||||
from pip._internal.operations.build.metadata import generate_metadata
|
from pip._internal.operations.build.metadata import generate_metadata
|
||||||
from pip._internal.operations.build.metadata_editable import generate_editable_metadata
|
from pip._internal.operations.build.metadata_editable import generate_editable_metadata
|
||||||
|
@ -112,6 +113,10 @@ class InstallRequirement:
|
||||||
self.link = self.original_link = link
|
self.link = self.original_link = link
|
||||||
self.original_link_is_in_wheel_cache = False
|
self.original_link_is_in_wheel_cache = False
|
||||||
|
|
||||||
|
# Information about the location of the artifact that was downloaded . This
|
||||||
|
# property is guaranteed to be set in resolver results.
|
||||||
|
self.download_info: Optional[DirectUrl] = None
|
||||||
|
|
||||||
# Path to any downloaded or already-existing package.
|
# Path to any downloaded or already-existing package.
|
||||||
self.local_file_path: Optional[str] = None
|
self.local_file_path: Optional[str] = None
|
||||||
if self.link and self.link.is_file:
|
if self.link and self.link.is_file:
|
||||||
|
@ -762,6 +767,7 @@ class InstallRequirement:
|
||||||
if self.is_wheel:
|
if self.is_wheel:
|
||||||
assert self.local_file_path
|
assert self.local_file_path
|
||||||
direct_url = None
|
direct_url = None
|
||||||
|
# TODO this can be refactored to direct_url = self.download_info
|
||||||
if self.editable:
|
if self.editable:
|
||||||
direct_url = direct_url_for_editable(self.unpacked_source_directory)
|
direct_url = direct_url_for_editable(self.unpacked_source_directory)
|
||||||
elif self.original_link:
|
elif self.original_link:
|
||||||
|
|
|
@ -45,6 +45,7 @@ from pip._internal.req.req_set import RequirementSet
|
||||||
from pip._internal.resolution.base import BaseResolver, InstallRequirementProvider
|
from pip._internal.resolution.base import BaseResolver, InstallRequirementProvider
|
||||||
from pip._internal.utils import compatibility_tags
|
from pip._internal.utils import compatibility_tags
|
||||||
from pip._internal.utils.compatibility_tags import get_supported
|
from pip._internal.utils.compatibility_tags import get_supported
|
||||||
|
from pip._internal.utils.direct_url_helpers import direct_url_from_link
|
||||||
from pip._internal.utils.logging import indent_log
|
from pip._internal.utils.logging import indent_log
|
||||||
from pip._internal.utils.misc import normalize_version_info
|
from pip._internal.utils.misc import normalize_version_info
|
||||||
from pip._internal.utils.packaging import check_requires_python
|
from pip._internal.utils.packaging import check_requires_python
|
||||||
|
@ -431,6 +432,14 @@ class Resolver(BaseResolver):
|
||||||
logger.debug("Using cached wheel link: %s", cache_entry.link)
|
logger.debug("Using cached wheel link: %s", cache_entry.link)
|
||||||
if req.link is req.original_link and cache_entry.persistent:
|
if req.link is req.original_link and cache_entry.persistent:
|
||||||
req.original_link_is_in_wheel_cache = True
|
req.original_link_is_in_wheel_cache = True
|
||||||
|
if cache_entry.origin is not None:
|
||||||
|
req.download_info = cache_entry.origin
|
||||||
|
else:
|
||||||
|
# Legacy cache entry that does not have origin.json.
|
||||||
|
# download_info may miss the archive_info.hash field.
|
||||||
|
req.download_info = direct_url_from_link(
|
||||||
|
req.link, link_is_in_wheel_cache=cache_entry.persistent
|
||||||
|
)
|
||||||
req.link = cache_entry.link
|
req.link = cache_entry.link
|
||||||
|
|
||||||
def _get_dist_for(self, req: InstallRequirement) -> BaseDistribution:
|
def _get_dist_for(self, req: InstallRequirement) -> BaseDistribution:
|
||||||
|
|
|
@ -18,6 +18,7 @@ from pip._internal.req.constructors import (
|
||||||
install_req_from_line,
|
install_req_from_line,
|
||||||
)
|
)
|
||||||
from pip._internal.req.req_install import InstallRequirement
|
from pip._internal.req.req_install import InstallRequirement
|
||||||
|
from pip._internal.utils.direct_url_helpers import direct_url_from_link
|
||||||
from pip._internal.utils.misc import normalize_version_info
|
from pip._internal.utils.misc import normalize_version_info
|
||||||
|
|
||||||
from .base import Candidate, CandidateVersion, Requirement, format_name
|
from .base import Candidate, CandidateVersion, Requirement, format_name
|
||||||
|
@ -281,12 +282,17 @@ class LinkCandidate(_InstallRequirementBackedCandidate):
|
||||||
version, wheel_version, name
|
version, wheel_version, name
|
||||||
)
|
)
|
||||||
|
|
||||||
if (
|
if cache_entry is not None:
|
||||||
cache_entry is not None
|
if cache_entry.persistent and template.link is template.original_link:
|
||||||
and cache_entry.persistent
|
ireq.original_link_is_in_wheel_cache = True
|
||||||
and template.link is template.original_link
|
if cache_entry.origin is not None:
|
||||||
):
|
ireq.download_info = cache_entry.origin
|
||||||
ireq.original_link_is_in_wheel_cache = True
|
else:
|
||||||
|
# Legacy cache entry that does not have origin.json.
|
||||||
|
# download_info may miss the archive_info.hash field.
|
||||||
|
ireq.download_info = direct_url_from_link(
|
||||||
|
source_link, link_is_in_wheel_cache=cache_entry.persistent
|
||||||
|
)
|
||||||
|
|
||||||
super().__init__(
|
super().__init__(
|
||||||
link=link,
|
link=link,
|
||||||
|
|
|
@ -354,6 +354,12 @@ def build(
|
||||||
req.editable and req.permit_editable_wheels,
|
req.editable and req.permit_editable_wheels,
|
||||||
)
|
)
|
||||||
if wheel_file:
|
if wheel_file:
|
||||||
|
# Record the download origin in the cache
|
||||||
|
if req.download_info is not None:
|
||||||
|
# download_info is guaranteed to be set because when we build an
|
||||||
|
# InstallRequirement it has been through the preparer before, but
|
||||||
|
# let's be cautious.
|
||||||
|
wheel_cache.record_download_origin(cache_dir, req.download_info)
|
||||||
# Update the link for this.
|
# Update the link for this.
|
||||||
req.link = Link(path_to_url(wheel_file))
|
req.link = Link(path_to_url(wheel_file))
|
||||||
req.local_file_path = req.link.file_path
|
req.local_file_path = req.link.file_path
|
||||||
|
|
|
@ -1550,9 +1550,9 @@ def test_install_builds_wheels(script: PipTestEnvironment, data: TestData) -> No
|
||||||
)
|
)
|
||||||
# Must have installed it all
|
# Must have installed it all
|
||||||
assert expected in str(res), str(res)
|
assert expected in str(res), str(res)
|
||||||
wheels = []
|
wheels: List[str] = []
|
||||||
for _, _, files in os.walk(wheels_cache):
|
for _, _, files in os.walk(wheels_cache):
|
||||||
wheels.extend(files)
|
wheels.extend(f for f in files if f.endswith(".whl"))
|
||||||
# and built wheels for upper and wheelbroken
|
# and built wheels for upper and wheelbroken
|
||||||
assert "Building wheel for upper" in str(res), str(res)
|
assert "Building wheel for upper" in str(res), str(res)
|
||||||
assert "Building wheel for wheelb" in str(res), str(res)
|
assert "Building wheel for wheelb" in str(res), str(res)
|
||||||
|
|
|
@ -5,13 +5,14 @@ import shutil
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from typing import Iterator, Tuple, cast
|
from typing import Iterator, Optional, Tuple, cast
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from pip._vendor.packaging.markers import Marker
|
from pip._vendor.packaging.markers import Marker
|
||||||
from pip._vendor.packaging.requirements import Requirement
|
from pip._vendor.packaging.requirements import Requirement
|
||||||
|
|
||||||
|
from pip._internal.cache import WheelCache
|
||||||
from pip._internal.commands import create_command
|
from pip._internal.commands import create_command
|
||||||
from pip._internal.commands.install import InstallCommand
|
from pip._internal.commands.install import InstallCommand
|
||||||
from pip._internal.exceptions import (
|
from pip._internal.exceptions import (
|
||||||
|
@ -22,6 +23,9 @@ from pip._internal.exceptions import (
|
||||||
)
|
)
|
||||||
from pip._internal.index.package_finder import PackageFinder
|
from pip._internal.index.package_finder import PackageFinder
|
||||||
from pip._internal.metadata import select_backend
|
from pip._internal.metadata import select_backend
|
||||||
|
from pip._internal.models.direct_url import ArchiveInfo, DirectUrl, DirInfo, VcsInfo
|
||||||
|
from pip._internal.models.format_control import FormatControl
|
||||||
|
from pip._internal.models.link import Link
|
||||||
from pip._internal.network.session import PipSession
|
from pip._internal.network.session import PipSession
|
||||||
from pip._internal.operations.build.build_tracker import get_build_tracker
|
from pip._internal.operations.build.build_tracker import get_build_tracker
|
||||||
from pip._internal.operations.prepare import RequirementPreparer
|
from pip._internal.operations.prepare import RequirementPreparer
|
||||||
|
@ -42,7 +46,7 @@ from pip._internal.req.req_file import (
|
||||||
)
|
)
|
||||||
from pip._internal.resolution.legacy.resolver import Resolver
|
from pip._internal.resolution.legacy.resolver import Resolver
|
||||||
from pip._internal.utils.urls import path_to_url
|
from pip._internal.utils.urls import path_to_url
|
||||||
from tests.lib import TestData, make_test_finder, requirements_file
|
from tests.lib import TestData, make_test_finder, requirements_file, wheel
|
||||||
from tests.lib.path import Path
|
from tests.lib.path import Path
|
||||||
|
|
||||||
|
|
||||||
|
@ -76,7 +80,10 @@ class TestRequirementSet:
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def _basic_resolver(
|
def _basic_resolver(
|
||||||
self, finder: PackageFinder, require_hashes: bool = False
|
self,
|
||||||
|
finder: PackageFinder,
|
||||||
|
require_hashes: bool = False,
|
||||||
|
wheel_cache: Optional[WheelCache] = None,
|
||||||
) -> Iterator[Resolver]:
|
) -> Iterator[Resolver]:
|
||||||
make_install_req = partial(
|
make_install_req = partial(
|
||||||
install_req_from_req_string,
|
install_req_from_req_string,
|
||||||
|
@ -105,7 +112,7 @@ class TestRequirementSet:
|
||||||
preparer=preparer,
|
preparer=preparer,
|
||||||
make_install_req=make_install_req,
|
make_install_req=make_install_req,
|
||||||
finder=finder,
|
finder=finder,
|
||||||
wheel_cache=None,
|
wheel_cache=wheel_cache,
|
||||||
use_user_site=False,
|
use_user_site=False,
|
||||||
upgrade_strategy="to-satisfy-only",
|
upgrade_strategy="to-satisfy-only",
|
||||||
ignore_dependencies=False,
|
ignore_dependencies=False,
|
||||||
|
@ -342,6 +349,161 @@ class TestRequirementSet:
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_download_info_find_links(self, data: TestData) -> None:
|
||||||
|
"""Test that download_info is set for requirements via find_links."""
|
||||||
|
finder = make_test_finder(find_links=[data.find_links])
|
||||||
|
with self._basic_resolver(finder) as resolver:
|
||||||
|
ireq = get_processed_req_from_line("simple")
|
||||||
|
reqset = resolver.resolve([ireq], True)
|
||||||
|
assert len(reqset.all_requirements) == 1
|
||||||
|
req = reqset.all_requirements[0]
|
||||||
|
assert req.download_info
|
||||||
|
assert isinstance(req.download_info.info, ArchiveInfo)
|
||||||
|
assert req.download_info.info.hash
|
||||||
|
|
||||||
|
@pytest.mark.network
|
||||||
|
def test_download_info_index_url(self) -> None:
|
||||||
|
"""Test that download_info is set for requirements via index."""
|
||||||
|
finder = make_test_finder(index_urls=["https://pypi.org/simple"])
|
||||||
|
with self._basic_resolver(finder) as resolver:
|
||||||
|
ireq = get_processed_req_from_line("initools")
|
||||||
|
reqset = resolver.resolve([ireq], True)
|
||||||
|
assert len(reqset.all_requirements) == 1
|
||||||
|
req = reqset.all_requirements[0]
|
||||||
|
assert req.download_info
|
||||||
|
assert isinstance(req.download_info.info, ArchiveInfo)
|
||||||
|
|
||||||
|
@pytest.mark.network
|
||||||
|
def test_download_info_web_archive(self) -> None:
|
||||||
|
"""Test that download_info is set for requirements from a web archive."""
|
||||||
|
finder = make_test_finder()
|
||||||
|
with self._basic_resolver(finder) as resolver:
|
||||||
|
ireq = get_processed_req_from_line(
|
||||||
|
"pip-test-package @ "
|
||||||
|
"https://github.com/pypa/pip-test-package/tarball/0.1.1"
|
||||||
|
)
|
||||||
|
reqset = resolver.resolve([ireq], True)
|
||||||
|
assert len(reqset.all_requirements) == 1
|
||||||
|
req = reqset.all_requirements[0]
|
||||||
|
assert req.download_info
|
||||||
|
assert (
|
||||||
|
req.download_info.url
|
||||||
|
== "https://github.com/pypa/pip-test-package/tarball/0.1.1"
|
||||||
|
)
|
||||||
|
assert isinstance(req.download_info.info, ArchiveInfo)
|
||||||
|
assert (
|
||||||
|
req.download_info.info.hash == "sha256="
|
||||||
|
"ad977496000576e1b6c41f6449a9897087ce9da6db4f15b603fe8372af4bf3c6"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_download_info_archive_legacy_cache(
|
||||||
|
self, tmp_path: Path, shared_data: TestData
|
||||||
|
) -> None:
|
||||||
|
"""Test download_info hash is not set for an archive with legacy cache entry."""
|
||||||
|
url = path_to_url(shared_data.packages / "simple-1.0.tar.gz")
|
||||||
|
finder = make_test_finder()
|
||||||
|
wheel_cache = WheelCache(str(tmp_path / "cache"), FormatControl())
|
||||||
|
cache_entry_dir = wheel_cache.get_path_for_link(Link(url))
|
||||||
|
Path(cache_entry_dir).mkdir(parents=True)
|
||||||
|
wheel.make_wheel(name="simple", version="1.0").save_to_dir(cache_entry_dir)
|
||||||
|
with self._basic_resolver(finder, wheel_cache=wheel_cache) as resolver:
|
||||||
|
ireq = get_processed_req_from_line(f"simple @ {url}")
|
||||||
|
reqset = resolver.resolve([ireq], True)
|
||||||
|
assert len(reqset.all_requirements) == 1
|
||||||
|
req = reqset.all_requirements[0]
|
||||||
|
assert req.original_link_is_in_wheel_cache
|
||||||
|
assert req.download_info
|
||||||
|
assert req.download_info.url == url
|
||||||
|
assert isinstance(req.download_info.info, ArchiveInfo)
|
||||||
|
assert not req.download_info.info.hash
|
||||||
|
|
||||||
|
def test_download_info_archive_cache_with_origin(
|
||||||
|
self, tmp_path: Path, shared_data: TestData
|
||||||
|
) -> None:
|
||||||
|
"""Test download_info hash is set for a web archive with cache entry
|
||||||
|
that has origin.json."""
|
||||||
|
url = path_to_url(shared_data.packages / "simple-1.0.tar.gz")
|
||||||
|
hash = "sha256=ad977496000576e1b6c41f6449a9897087ce9da6db4f15b603fe8372af4bf3c6"
|
||||||
|
finder = make_test_finder()
|
||||||
|
wheel_cache = WheelCache(str(tmp_path / "cache"), FormatControl())
|
||||||
|
cache_entry_dir = wheel_cache.get_path_for_link(Link(url))
|
||||||
|
Path(cache_entry_dir).mkdir(parents=True)
|
||||||
|
Path(cache_entry_dir).joinpath("origin.json").write_text(
|
||||||
|
DirectUrl(url, ArchiveInfo(hash=hash)).to_json()
|
||||||
|
)
|
||||||
|
wheel.make_wheel(name="simple", version="1.0").save_to_dir(cache_entry_dir)
|
||||||
|
with self._basic_resolver(finder, wheel_cache=wheel_cache) as resolver:
|
||||||
|
ireq = get_processed_req_from_line(f"simple @ {url}")
|
||||||
|
reqset = resolver.resolve([ireq], True)
|
||||||
|
assert len(reqset.all_requirements) == 1
|
||||||
|
req = reqset.all_requirements[0]
|
||||||
|
assert req.original_link_is_in_wheel_cache
|
||||||
|
assert req.download_info
|
||||||
|
assert req.download_info.url == url
|
||||||
|
assert isinstance(req.download_info.info, ArchiveInfo)
|
||||||
|
assert req.download_info.info.hash == hash
|
||||||
|
|
||||||
|
def test_download_info_local_wheel(self, data: TestData) -> None:
|
||||||
|
"""Test that download_info is set for requirements from a local wheel."""
|
||||||
|
finder = make_test_finder()
|
||||||
|
with self._basic_resolver(finder) as resolver:
|
||||||
|
ireq = get_processed_req_from_line(
|
||||||
|
f"{data.packages}/simplewheel-1.0-py2.py3-none-any.whl"
|
||||||
|
)
|
||||||
|
reqset = resolver.resolve([ireq], True)
|
||||||
|
assert len(reqset.all_requirements) == 1
|
||||||
|
req = reqset.all_requirements[0]
|
||||||
|
assert req.download_info
|
||||||
|
assert req.download_info.url.startswith("file://")
|
||||||
|
assert isinstance(req.download_info.info, ArchiveInfo)
|
||||||
|
assert (
|
||||||
|
req.download_info.info.hash == "sha256="
|
||||||
|
"e63aa139caee941ec7f33f057a5b987708c2128238357cf905429846a2008718"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_download_info_local_dir(self, data: TestData) -> None:
|
||||||
|
"""Test that download_info is set for requirements from a local dir."""
|
||||||
|
finder = make_test_finder()
|
||||||
|
with self._basic_resolver(finder) as resolver:
|
||||||
|
ireq_url = path_to_url(data.packages / "FSPkg")
|
||||||
|
ireq = get_processed_req_from_line(f"FSPkg @ {ireq_url}")
|
||||||
|
reqset = resolver.resolve([ireq], True)
|
||||||
|
assert len(reqset.all_requirements) == 1
|
||||||
|
req = reqset.all_requirements[0]
|
||||||
|
assert req.download_info
|
||||||
|
assert req.download_info.url.startswith("file://")
|
||||||
|
assert isinstance(req.download_info.info, DirInfo)
|
||||||
|
|
||||||
|
def test_download_info_local_editable_dir(self, data: TestData) -> None:
|
||||||
|
"""Test that download_info is set for requirements from a local editable dir."""
|
||||||
|
finder = make_test_finder()
|
||||||
|
with self._basic_resolver(finder) as resolver:
|
||||||
|
ireq_url = path_to_url(data.packages / "FSPkg")
|
||||||
|
ireq = get_processed_req_from_line(f"-e {ireq_url}#egg=FSPkg")
|
||||||
|
reqset = resolver.resolve([ireq], True)
|
||||||
|
assert len(reqset.all_requirements) == 1
|
||||||
|
req = reqset.all_requirements[0]
|
||||||
|
assert req.download_info
|
||||||
|
assert req.download_info.url.startswith("file://")
|
||||||
|
assert isinstance(req.download_info.info, DirInfo)
|
||||||
|
assert req.download_info.info.editable
|
||||||
|
|
||||||
|
@pytest.mark.network
|
||||||
|
def test_download_info_vcs(self) -> None:
|
||||||
|
"""Test that download_info is set for requirements from git."""
|
||||||
|
finder = make_test_finder()
|
||||||
|
with self._basic_resolver(finder) as resolver:
|
||||||
|
ireq = get_processed_req_from_line(
|
||||||
|
"pip-test-package @ git+https://github.com/pypa/pip-test-package"
|
||||||
|
)
|
||||||
|
reqset = resolver.resolve([ireq], True)
|
||||||
|
assert len(reqset.all_requirements) == 1
|
||||||
|
req = reqset.all_requirements[0]
|
||||||
|
assert req.download_info
|
||||||
|
assert isinstance(req.download_info.info, VcsInfo)
|
||||||
|
assert req.download_info.url == "https://github.com/pypa/pip-test-package"
|
||||||
|
assert req.download_info.info.vcs == "git"
|
||||||
|
|
||||||
|
|
||||||
class TestInstallRequirement:
|
class TestInstallRequirement:
|
||||||
def setup(self) -> None:
|
def setup(self) -> None:
|
||||||
|
|
Loading…
Reference in a new issue