def test_std_install_with_direct_url(self, data, tmpdir): """Test that install_wheel creates direct_url.json metadata when provided with a direct_url argument. Also test that the RECORDS file contains an entry for direct_url.json in that case. Note direct_url.url is intentionally different from wheelpath, because wheelpath is typically the result of a local build. """ self.prep(data, tmpdir) direct_url = DirectUrl( url="file:///home/user/archive.tgz", info=ArchiveInfo(), ) wheel.install_wheel( self.name, self.wheelpath, scheme=self.scheme, req_description=str(self.req), direct_url=direct_url, ) direct_url_path = os.path.join( self.dest_dist_info, DIRECT_URL_METADATA_NAME ) self.assert_permission(direct_url_path, 0o644) with open(direct_url_path, 'rb') as f: expected_direct_url_json = direct_url.to_json() direct_url_json = f.read().decode("utf-8") assert direct_url_json == expected_direct_url_json # check that the direc_url file is part of RECORDS with open(os.path.join(self.dest_dist_info, "RECORD")) as f: assert DIRECT_URL_METADATA_NAME in f.read()
def test_to_json(): direct_url = DirectUrl( url="file:///home/user/archive.tgz", info=ArchiveInfo(), ) direct_url.validate() assert direct_url.to_json() == ( '{"archive_info": {}, "url": "file:///home/user/archive.tgz"}')
def test_as_pep440_requirement_dir() -> None: direct_url = DirectUrl( url="file:///home/user/project", info=DirInfo(editable=False), ) direct_url.validate() assert (direct_url_as_pep440_direct_reference( direct_url, "pkg") == "pkg @ file:///home/user/project")
def test_as_pep440_requirement_editable_dir() -> None: # direct_url_as_pep440_direct_reference behaves the same # irrespective of the editable flag. It's the responsibility of # callers to render it as editable direct_url = DirectUrl( url="file:///home/user/project", info=DirInfo(editable=True), ) direct_url.validate() assert (direct_url_as_pep440_direct_reference( direct_url, "pkg") == "pkg @ file:///home/user/project")
def direct_url_from_link(link: Link, source_dir: Optional[str] = None, link_is_in_wheel_cache: bool = False) -> DirectUrl: if link.is_vcs: vcs_backend = vcs.get_backend_for_scheme(link.scheme) assert vcs_backend url, requested_revision, _ = vcs_backend.get_url_rev_and_auth( link.url_without_fragment) # For VCS links, we need to find out and add commit_id. if link_is_in_wheel_cache: # If the requested VCS link corresponds to a cached # wheel, it means the requested revision was an # immutable commit hash, otherwise it would not have # been cached. In that case we don't have a source_dir # with the VCS checkout. assert requested_revision commit_id = requested_revision else: # If the wheel was not in cache, it means we have # had to checkout from VCS to build and we have a source_dir # which we can inspect to find out the commit id. assert source_dir commit_id = vcs_backend.get_revision(source_dir) return DirectUrl( url=url, info=VcsInfo( vcs=vcs_backend.name, commit_id=commit_id, requested_revision=requested_revision, ), subdirectory=link.subdirectory_fragment, ) elif link.is_existing_dir(): return DirectUrl( url=link.url_without_fragment, info=DirInfo(), subdirectory=link.subdirectory_fragment, ) else: hash = None hash_name = link.hash_name if hash_name: hash = f"{hash_name}={link.hash}" return DirectUrl( url=link.url_without_fragment, info=ArchiveInfo(hash=hash), subdirectory=link.subdirectory_fragment, )
def get_created_direct_url(result: TestPipResult, pkg: str) -> Optional[DirectUrl]: direct_url_path = get_created_direct_url_path(result, pkg) if direct_url_path: with open(direct_url_path) as f: return DirectUrl.from_json(f.read()) return None
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(), encoding="utf-8")
def test_dir_info(): direct_url_dict = { "url": "file:///home/user/project", "dir_info": { "editable": True }, } direct_url = DirectUrl.from_dict(direct_url_dict) assert isinstance(direct_url.info, DirInfo) assert direct_url.url == direct_url_dict["url"] assert direct_url.info.editable is True assert direct_url.to_dict() == direct_url_dict # test editable default to False direct_url_dict = {"url": "file:///home/user/project", "dir_info": {}} direct_url = DirectUrl.from_dict(direct_url_dict) assert direct_url.info.editable is False
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 direct_url_as_pep440_direct_reference(direct_url: DirectUrl, name: str) -> str: """Convert a DirectUrl to a pip requirement string.""" direct_url.validate() # if invalid, this is a pip bug requirement = name + " @ " fragments = [] if isinstance(direct_url.info, VcsInfo): requirement += "{}+{}@{}".format(direct_url.info.vcs, direct_url.url, direct_url.info.commit_id) elif isinstance(direct_url.info, ArchiveInfo): requirement += direct_url.url if direct_url.info.hash: fragments.append(direct_url.info.hash) else: assert isinstance(direct_url.info, DirInfo) requirement += direct_url.url if direct_url.subdirectory: fragments.append("subdirectory=" + direct_url.subdirectory) if fragments: requirement += "#" + "&".join(fragments) return requirement
def test_archive_info(): direct_url_dict = { "url": "file:///home/user/archive.tgz", "archive_info": { "hash": "sha1=1b8c5bc61a86f377fea47b4276c8c8a5842d2220" }, } direct_url = DirectUrl.from_dict(direct_url_dict) assert isinstance(direct_url.info, ArchiveInfo) assert direct_url.url == direct_url_dict["url"] assert direct_url.info.hash == direct_url_dict["archive_info"]["hash"] assert direct_url.to_dict() == direct_url_dict
def __init__( self, link: Link, persistent: bool, ): self.link = link 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())
def test_as_pep440_requirement_archive(): direct_url = DirectUrl( url="file:///home/user/archive.tgz", info=ArchiveInfo(), ) direct_url.validate() assert (direct_url_as_pep440_direct_reference( direct_url, "pkg") == "pkg @ file:///home/user/archive.tgz") direct_url.subdirectory = "subdir" direct_url.validate() assert (direct_url_as_pep440_direct_reference( direct_url, "pkg") == "pkg @ file:///home/user/archive.tgz#subdirectory=subdir") direct_url.info.hash = "sha1=1b8c5bc61a86f377fea47b4276c8c8a5842d2220" direct_url.validate() assert ( direct_url_as_pep440_direct_reference( direct_url, "pkg") == "pkg @ file:///home/user/archive.tgz" "#sha1=1b8c5bc61a86f377fea47b4276c8c8a5842d2220&subdirectory=subdir")
def test_vcs_info() -> None: direct_url_dict = { "url": "https:///g.c/u/p.git", "vcs_info": { "vcs": "git", "requested_revision": "master", "commit_id": "1b8c5bc61a86f377fea47b4276c8c8a5842d2220", }, } direct_url = DirectUrl.from_dict(direct_url_dict) assert isinstance(direct_url.info, VcsInfo) assert direct_url.url == direct_url_dict["url"] assert direct_url.info.vcs == "git" assert direct_url.info.requested_revision == "master" assert direct_url.info.commit_id == "1b8c5bc61a86f377fea47b4276c8c8a5842d2220" assert direct_url.to_dict() == direct_url_dict
def test_as_pep440_requirement_vcs(): direct_url = DirectUrl( url="https:///g.c/u/p.git", info=VcsInfo(vcs="git", commit_id="1b8c5bc61a86f377fea47b4276c8c8a5842d2220")) direct_url.validate() assert (direct_url_as_pep440_direct_reference( direct_url, "pkg") == "pkg @ git+https:///g.c/u/p.git" "@1b8c5bc61a86f377fea47b4276c8c8a5842d2220") direct_url.subdirectory = "subdir" direct_url.validate() assert (direct_url_as_pep440_direct_reference( direct_url, "pkg") == "pkg @ git+https:///g.c/u/p.git" "@1b8c5bc61a86f377fea47b4276c8c8a5842d2220#subdirectory=subdir")
def test_freeze_pep610_editable(script: PipTestEnvironment) -> None: """ Test that a package installed with a direct_url.json with editable=true is correctly frozeon as editable. """ pkg_path = _create_test_package(script, name="testpkg") result = script.pip("install", pkg_path) direct_url_path = get_created_direct_url_path(result, "testpkg") assert direct_url_path # patch direct_url.json to simulate an editable install with open(direct_url_path) as f: direct_url = DirectUrl.from_json(f.read()) assert isinstance(direct_url.info, DirInfo) direct_url.info.editable = True with open(direct_url_path, "w") as f: f.write(direct_url.to_json()) result = script.pip("freeze") assert "# Editable Git install with no remote (testpkg==0.1)" in result.stdout
def dist_get_direct_url(dist): # type: (Distribution) -> Optional[DirectUrl] """Obtain a DirectUrl from a pkg_resource.Distribution. Returns None if the distribution has no `direct_url.json` metadata, or if `direct_url.json` is invalid. """ if not dist.has_metadata(DIRECT_URL_METADATA_NAME): return None try: return DirectUrl.from_json(dist.get_metadata(DIRECT_URL_METADATA_NAME)) except (DirectUrlValidationError, json.JSONDecodeError, UnicodeDecodeError) as e: logger.warning( "Error parsing %s for %s: %s", DIRECT_URL_METADATA_NAME, dist.project_name, e, ) return None
def test_list_pep610_editable(script: PipTestEnvironment) -> None: """ Test that a package installed with a direct_url.json with editable=true is correctly listed as editable. """ pkg_path = _create_test_package(script.scratch_path, name="testpkg") result = script.pip("install", pkg_path) direct_url_path = get_created_direct_url_path(result, "testpkg") assert direct_url_path # patch direct_url.json to simulate an editable install with open(direct_url_path) as f: direct_url = DirectUrl.from_json(f.read()) assert isinstance(direct_url.info, DirInfo) direct_url.info.editable = True with open(direct_url_path, "w") as f: f.write(direct_url.to_json()) result = script.pip("list", "--format=json") for item in json.loads(result.stdout): if item["name"] == "testpkg": assert item["editable_project_location"] break else: assert False, "package 'testpkg' not found in pip list result"
def direct_url(self) -> Optional[DirectUrl]: """Obtain a DirectUrl from this distribution. Returns None if the distribution has no `direct_url.json` metadata, or if `direct_url.json` is invalid. """ try: content = self.read_text(DIRECT_URL_METADATA_NAME) except FileNotFoundError: return None try: return DirectUrl.from_json(content) except ( UnicodeDecodeError, json.JSONDecodeError, DirectUrlValidationError, ) as e: logger.warning( "Error parsing %s for %s: %s", DIRECT_URL_METADATA_NAME, self.canonical_name, e, ) return None
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 = shared_data.packages.joinpath("simple-1.0.tar.gz").as_uri() 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 _redact_git(url): direct_url = DirectUrl( url=url, info=VcsInfo(vcs="git", commit_id="1"), ) return direct_url.redacted_url
def test_parsing_validation(): with pytest.raises(DirectUrlValidationError, match="url must have a value"): DirectUrl.from_dict({"dir_info": {}}) with pytest.raises( DirectUrlValidationError, match="missing one of archive_info, dir_info, vcs_info", ): DirectUrl.from_dict({"url": "http://..."}) with pytest.raises(DirectUrlValidationError, match="unexpected type for editable"): DirectUrl.from_dict({ "url": "http://...", "dir_info": { "editable": "false" } }) with pytest.raises(DirectUrlValidationError, match="unexpected type for hash"): DirectUrl.from_dict({"url": "http://...", "archive_info": {"hash": 1}}) with pytest.raises(DirectUrlValidationError, match="unexpected type for vcs"): DirectUrl.from_dict({"url": "http://...", "vcs_info": {"vcs": None}}) with pytest.raises(DirectUrlValidationError, match="commit_id must have a value"): DirectUrl.from_dict({"url": "http://...", "vcs_info": {"vcs": "git"}}) with pytest.raises( DirectUrlValidationError, match="more than one of archive_info, dir_info, vcs_info", ): DirectUrl.from_dict({ "url": "http://...", "dir_info": {}, "archive_info": {} })
def _redact_archive(url): direct_url = DirectUrl( url=url, info=ArchiveInfo(), ) return direct_url.redacted_url
def test_from_json() -> None: json = '{"url": "file:///home/user/project", "dir_info": {}}' direct_url = DirectUrl.from_json(json) assert direct_url.url == "file:///home/user/project" assert isinstance(direct_url.info, DirInfo) assert direct_url.info.editable is False
def direct_url_for_editable(source_dir: str) -> DirectUrl: return DirectUrl( url=path_to_url(source_dir), info=DirInfo(editable=True), )
def test_from_json(): json = '{"url": "file:///home/user/project", "dir_info": {}}' direct_url = DirectUrl.from_json(json) assert direct_url.url == "file:///home/user/project" assert direct_url.info.editable is False