def export( project: Project, candidates: list[Candidate] | list[Requirement], options: Namespace, ) -> str: lines = [] for candidate in sorted(candidates, key=lambda x: x.identify()): # type: ignore if isinstance(candidate, Candidate): req = dataclasses.replace( candidate.req, specifier=f"=={candidate.version}", marker=None ) else: assert isinstance(candidate, Requirement) req = candidate lines.append( req.as_line().replace( "${PROJECT_ROOT}", project.root.absolute().as_posix().lstrip("/") ) ) if options.hashes and getattr(candidate, "hashes", None): for item in candidate.hashes.values(): # type: ignore lines.append(f" \\\n --hash={item}") lines.append("\n") sources = project.tool_settings.get("source", []) for source in sources: url = expand_env_vars_in_auth(source["url"]) prefix = "--index-url" if source["name"] == "pypi" else "--extra-index-url" lines.append(f"{prefix} {url}\n") if not source.get("verify_ssl", True): host = urllib.parse.urlparse(url).hostname lines.append(f"--trusted-host {host}\n") return "".join(lines)
def get_finder( self, sources: Optional[List[Source]] = None, ignore_requires_python: bool = False, ) -> Generator[pip_shims.PackageFinder, None, None]: """Return the package finder of given index sources. :param sources: a list of sources the finder should search in. :param ignore_requires_python: whether to ignore the python version constraint. """ if sources is None: sources = self.project.sources for source in sources: source["url"] = expand_env_vars_in_auth(source["url"]) python_version, _ = get_python_version(self.python_executable, digits=2) finder = get_finder( sources, self.project.cache_dir.as_posix(), python_version, ignore_requires_python, ) # Reuse the auth across sessions to avoid prompting repeatly. finder.session.auth = self.auth yield finder finder.session.close()
def direct_url(self) -> dict[str, Any] | None: """PEP 610 direct_url.json data""" req = self.req if isinstance(req, VcsRequirement): if req.editable: assert self.ireq.source_dir return _filter_none({ "url": pip_shims.path_to_url(self.ireq.source_dir), "dir_info": { "editable": True }, "subdirectory": req.subdirectory, }) return _filter_none({ "url": url_without_fragments(req.repo), "vcs_info": _filter_none({ "vcs": req.vcs, "requested_revision": req.ref, "commit_id": self.revision, }), "subdirectory": req.subdirectory, }) elif isinstance(req, FileRequirement): if req.is_local_dir: return _filter_none({ "url": url_without_fragments(req.url), "dir_info": _filter_none({"editable": req.editable or None}), "subdirectory": req.subdirectory, }) url = expand_env_vars_in_auth( req.url.replace( "${PROJECT_ROOT}", self.environment.project.root.as_posix(). lstrip( # type: ignore "/"), )) with self.environment.get_finder() as finder: hash_cache = self.environment.project.make_hash_cache() hash_cache.session = finder.session # type: ignore return _filter_none({ "url": url_without_fragments(url), "archive_info": { "hash": hash_cache.get_hash(pip_shims.Link(url)).replace( ":", "=") }, "subdirectory": req.subdirectory, }) else: return None
def ireq(self) -> pip_shims.InstallRequirement: rv = self.req.as_ireq() if rv.link: rv.link = pip_shims.Link( expand_env_vars_in_auth( rv.link.url.replace( "${PROJECT_ROOT}", self.environment.project.root.as_posix().lstrip("/"), ) ) ) if rv.source_dir: rv.source_dir = os.path.normpath(os.path.abspath(rv.link.file_path)) if rv.local_file_path: rv.local_file_path = rv.link.file_path return rv
def get_ireq(self) -> pip_shims.InstallRequirement: rv, project = self.req.as_ireq(), self.environment.project if rv.link: rv.original_link = rv.link = pip_shims.Link( expand_env_vars_in_auth( rv.link.url.replace( "${PROJECT_ROOT}", project.root.as_posix().lstrip("/"), # type: ignore ))) if rv.source_dir: rv.source_dir = os.path.normpath( os.path.abspath(rv.link.file_path)) if rv.local_file_path: rv.local_file_path = rv.link.file_path elif self.candidate.link: rv.link = rv.original_link = self.candidate.link return rv
def sources(self) -> list[Source]: sources = list(self.tool_settings.get("source", [])) if all(source.get("name") != "pypi" for source in sources): sources.insert( 0, { "url": self.config["pypi.url"], "verify_ssl": self.config["pypi.verify_ssl"], "name": "pypi", }, ) expanded_sources: list[Source] = [ Source( url=expand_env_vars_in_auth(s["url"]), verify_ssl=s.get("verify_ssl", True), name=s.get("name", urlparse(s["url"]).hostname), ) for s in sources ] return expanded_sources
def build( self, ireq: pip_shims.InstallRequirement, hashes: Optional[Dict[str, str]] = None, allow_all: bool = True, ) -> str: """Build egg_info directory for editable candidates and a wheel for others. :param ireq: the InstallRequirment of the candidate. :param hashes: a dictionary of filename: hash_value to check against downloaded artifacts. :param allow_all: Allow building incompatible wheels. :returns: The full path of the built artifact. """ kwargs = self._make_building_args(ireq) wheel_cache = self.project.make_wheel_cache() with self.get_finder() as finder: with allow_all_wheels(allow_all): # temporarily allow all wheels to get a link. populate_link(finder, ireq, False) if hashes is None: cache_entry = wheel_cache.get_cache_entry( ireq.link, ireq.req.project_name, pip_shims.get_supported( version="".join( map( str, get_python_version(self.python_executable)[0][:2], ) ) ), ) if cache_entry is not None: stream.logger.debug( "Using cached wheel link: %s", cache_entry.link ) ireq.link = cache_entry.link if not ireq.editable and not ireq.req.name: ireq.source_dir = kwargs["build_dir"] else: ireq.ensure_has_source_dir(kwargs["build_dir"]) download_dir = kwargs["download_dir"] only_download = False if ireq.link.is_wheel: download_dir = kwargs["wheel_download_dir"] only_download = True if hashes: ireq.hash_options = convert_hashes(hashes) if not (ireq.editable and ireq.req.is_local_dir): downloader = pip_shims.Downloader(finder.session, "off") if ireq.link.is_vcs: ireq.link = pip_shims.Link(expand_env_vars_in_auth(ireq.link.url)) downloaded = pip_shims.unpack_url( ireq.link, ireq.source_dir, downloader, download_dir, ireq.hashes(False), ) # Preserve the downloaded file so that it won't be cleared. if downloaded and only_download: try: shutil.copy(downloaded.path, download_dir) except shutil.SameFileError: pass if ireq.link.is_wheel: # If the file is a wheel, should be already present under download dir. return (self.project.cache("wheels") / ireq.link.filename).as_posix() else: # Check the built wheel cache again after hashes are resolved. cache_entry = wheel_cache.get_cache_entry( ireq.link, ireq.req.project_name, pip_shims.get_supported( version="".join( map(str, get_python_version(self.python_executable)[0][:2]) ) ), ) if cache_entry is not None: stream.logger.debug("Using cached wheel link: %s", cache_entry.link) return cache_entry.link.file_path # Otherwise, now all source is prepared, build it. with EnvBuilder(ireq.unpacked_source_directory, self) as builder: if ireq.editable: ret = builder.build_egg_info(kwargs["build_dir"]) ireq.metadata_directory = ret else: should_cache = False if ireq.link.is_vcs: vcs = pip_shims.VcsSupport() vcs_backend = vcs.get_backend_for_scheme(ireq.link.scheme) if vcs_backend.is_immutable_rev_checkout( ireq.link.url, ireq.source_dir ): should_cache = True else: base, _ = ireq.link.splitext() if _egg_info_re.search(base) is not None: # Determine whether the string looks like an egg_info. should_cache = True output_dir = ( wheel_cache.get_path_for_link(ireq.link) if should_cache else kwargs["build_dir"] ) if not os.path.exists(output_dir): os.makedirs(output_dir, exist_ok=True) ret = builder.build_wheel(output_dir) return ret
def build( self, ireq: pip_shims.InstallRequirement, hashes: Optional[Dict[str, str]] = None, allow_all: bool = True, ) -> str: """Build egg_info directory for editable candidates and a wheel for others. :param ireq: the InstallRequirment of the candidate. :param hashes: a dictionary of filename: hash_value to check against downloaded artifacts. :param allow_all: Allow building incompatible wheels. :returns: The full path of the built artifact. """ build_dir = self._get_build_dir(ireq) wheel_cache = self.project.make_wheel_cache() supported_tags = pip_shims.get_supported("".join( map(str, get_python_version(self.python_executable, digits=2)[0]))) with self.get_finder(ignore_requires_python=True) as finder: with allow_all_wheels(allow_all): # temporarily allow all wheels to get a link. populate_link(finder, ireq, False) ireq.link = pip_shims.Link( expand_env_vars_in_auth( ireq.link.url.replace( "${PROJECT_ROOT}", self.project.root.as_posix().lstrip("/")))) if hashes is None and not ireq.editable: # If hashes are not given and cache is hit, replace the link with the # cached one. This can speed up by skipping the download and build. cache_entry = wheel_cache.get_cache_entry( ireq.link, ireq.req.project_name, supported_tags, ) if cache_entry is not None: termui.logger.debug("Using cached wheel link: %s", cache_entry.link) ireq.link = cache_entry.link if not ireq.editable and not ireq.req.name: ireq.source_dir = build_dir else: ireq.ensure_has_source_dir(build_dir) if hashes: ireq.hash_options = convert_hashes(hashes) if not (ireq.editable and ireq.req.is_local_dir): downloader = pip_shims.Downloader(finder.session, "off") downloaded = pip_shims.unpack_url( ireq.link, ireq.source_dir, downloader, hashes=ireq.hashes(False), ) if ireq.link.is_wheel: # If the file is a wheel, return the downloaded file directly. return downloaded.path # Check the built wheel cache again after hashes are resolved. if not ireq.editable: cache_entry = wheel_cache.get_cache_entry( ireq.link, ireq.req.project_name, supported_tags, ) if cache_entry is not None: termui.logger.debug("Using cached wheel link: %s", cache_entry.link) return cache_entry.link.file_path # Otherwise, as all source is already prepared, build it. with EnvBuilder(ireq.unpacked_source_directory, self) as builder: if ireq.editable: ret = builder.build_egg_info(build_dir) ireq.metadata_directory = ret return ret should_cache = False if ireq.link.is_vcs: vcs = pip_shims.VcsSupport() vcs_backend = vcs.get_backend_for_scheme(ireq.link.scheme) if vcs_backend.is_immutable_rev_checkout( ireq.link.url, ireq.source_dir): should_cache = True else: base, _ = ireq.link.splitext() if _egg_info_re.search(base) is not None: # Determine whether the string looks like an egg_info. should_cache = True output_dir = (wheel_cache.get_path_for_link(ireq.link) if should_cache else build_dir) if not os.path.exists(output_dir): os.makedirs(output_dir, exist_ok=True) return builder.build_wheel(output_dir)
def test_expend_env_vars_in_auth(given, expected, monkeypatch): monkeypatch.setenv("FOO", "hello") monkeypatch.setenv("BAR", "wo:rld") assert utils.expand_env_vars_in_auth(given) == expected