diff --git a/src/pip/_internal/req/req_install.py b/src/pip/_internal/req/req_install.py index 6bbad88578a..f37baf55d60 100644 --- a/src/pip/_internal/req/req_install.py +++ b/src/pip/_internal/req/req_install.py @@ -31,6 +31,7 @@ from pip._internal.pyproject import load_pyproject_toml, make_pyproject_path from pip._internal.req.req_uninstall import UninstallPathSet from pip._internal.utils.deprecation import deprecated +from pip._internal.utils.direct_url_helpers import direct_url_from_link from pip._internal.utils.hashes import Hashes from pip._internal.utils.logging import indent_log from pip._internal.utils.marker_files import ( @@ -133,6 +134,7 @@ def __init__( # PEP 508 URL requirement link = Link(req.url) self.link = self.original_link = link + self.original_link_is_in_wheel_cache = False # Path to any downloaded or already-existing package. self.local_file_path = None # type: Optional[str] if self.link and self.link.is_file: @@ -249,13 +251,15 @@ def populate_link(self, finder, upgrade, require_hashes): if self._wheel_cache is not None and not require_hashes: old_link = self.link supported_tags = pep425tags.get_supported() - self.link = self._wheel_cache.get( + self.link, persistent_cache = self._wheel_cache.get2( link=self.link, package_name=self.name, supported_tags=supported_tags, ) if old_link != self.link: logger.debug('Using cached wheel link: %s', self.link) + if old_link is self.original_link and persistent_cache: + self.original_link_is_in_wheel_cache = True # Things that are valid for all kinds of requirements? @property @@ -804,6 +808,13 @@ def install( if self.is_wheel: assert self.local_file_path + direct_url = None + if self.original_link: + direct_url = direct_url_from_link( + self.original_link, + self.source_dir, + self.original_link_is_in_wheel_cache, + ) install_wheel( self.name, self.local_file_path, @@ -811,6 +822,7 @@ def install( req_description=str(self.req), pycompile=pycompile, warn_script_location=warn_script_location, + direct_url=direct_url, ) self.install_succeeded = True return diff --git a/tests/functional/test_install_direct_url.py b/tests/functional/test_install_direct_url.py new file mode 100644 index 00000000000..ec1e927ebf8 --- /dev/null +++ b/tests/functional/test_install_direct_url.py @@ -0,0 +1,48 @@ +import re + +from pip._internal.models.direct_url import DIRECT_URL_METADATA_NAME, DirectUrl +from tests.lib import _create_test_package, path_to_url + + +def _get_created_direct_url(result, pkg): + direct_url_metadata_re = re.compile( + pkg + r"-[\d\.]+\.dist-info." + DIRECT_URL_METADATA_NAME + r"$" + ) + for filename in result.files_created: + if direct_url_metadata_re.search(filename): + direct_url_path = result.test_env.base_path / filename + with open(direct_url_path) as f: + return DirectUrl.from_json(f.read()) + return None + + +def test_install_find_links_no_direct_url(script, with_wheel): + result = script.pip_install_local("simple") + assert not _get_created_direct_url(result, "simple") + + +def test_install_vcs_editable_no_direct_url(script, with_wheel): + pkg_path = _create_test_package(script, name="testpkg") + args = ["install", "-e", "git+%s#egg=testpkg" % path_to_url(pkg_path)] + result = script.pip(*args) + # legacy editable installs do not generate .dist-info, + # hence no direct_url.json + assert not _get_created_direct_url(result, "testpkg") + + +def test_install_vcs_non_editable_direct_url(script, with_wheel): + pkg_path = _create_test_package(script, name="testpkg") + url = path_to_url(pkg_path) + args = ["install", "git+{}#egg=testpkg".format(url)] + result = script.pip(*args) + direct_url = _get_created_direct_url(result, "testpkg") + assert direct_url + assert direct_url.url == url + assert direct_url.info.vcs == "git" + + +def test_install_archive_direct_url(script, data, with_wheel): + req = "simple @ " + path_to_url(data.packages / "simple-2.0.tar.gz") + assert req.startswith("simple @ file://") + result = script.pip("install", req) + assert _get_created_direct_url(result, "simple")