def __init__( self, candidates: Dict[str, Candidate], environment: Environment, clean: bool = False, dry_run: bool = False, retry_times: int = 1, install_self: bool = False, no_editable: bool = False, ) -> None: self.environment = environment self.clean = clean self.dry_run = dry_run self.retry_times = retry_times self.no_editable = no_editable self.install_self = install_self self.parallel = environment.project.config["parallel_install"] locked_repository = environment.project.locked_repository self.all_candidate_keys = list(locked_repository.all_candidates) self.working_set = environment.get_working_set() self.ui = environment.project.core.ui if self.no_editable: for candidate in candidates.values(): candidate.req.editable = None # type: ignore self.candidates = candidates
def __init__( self, candidates: Dict[str, Candidate], environment: Environment, ) -> None: self.candidates = candidates self.environment = environment self.parallel = environment.project.config["parallel_install"] self.working_set = environment.get_working_set()
def ireq_as_line(ireq: InstallRequirement, environment: Environment) -> str: """Formats an `InstallRequirement` instance as a PEP 508 dependency string. Generic formatter for pretty printing InstallRequirements to the terminal in a less verbose way than using its `__str__` method. :param :class:`InstallRequirement` ireq: A pip **InstallRequirement** instance. :return: A formatted string for prettyprinting :rtype: str """ if ireq.editable: line = "-e {}".format(ireq.link) else: if not ireq.req: ireq.req = parse_requirement("dummy @" + ireq.link.url) wheel = Wheel(environment.build(ireq)) ireq.req.name = wheel.name line = _requirement_to_str_lowercase_name(ireq.req) if str(ireq.req.marker) != str(ireq.markers): if not ireq.req.marker: line = "{}; {}".format(line, ireq.markers) else: name, markers = line.split(";", 1) markers = Marker(markers) & ireq.markers line = "{}; {}".format(name, markers) return line
def ireq_as_line(ireq: InstallRequirement, environment: Environment) -> str: """Formats an `InstallRequirement` instance as a PEP 508 dependency string. Generic formatter for pretty printing InstallRequirements to the terminal in a less verbose way than using its `__str__` method. :param :class:`InstallRequirement` ireq: A pip **InstallRequirement** instance. :return: A formatted string for prettyprinting :rtype: str """ if ireq.editable: line = "-e {}".format(ireq.link) else: if not ireq.req: ireq.req = parse_requirement("dummy @" + ireq.link.url) # type: ignore wheel = Wheel(environment.build(ireq)) ireq.req.name = wheel.name # type: ignore line = _requirement_to_str_lowercase_name(cast(PRequirement, ireq.req)) assert ireq.req if not ireq.req.marker and ireq.markers: line = f"{line}; {ireq.markers}" return line
def environment(self) -> Environment: if self.is_global: return GlobalEnvironment(self) if self.config["use_venv"] and "VIRTUAL_ENV" in os.environ: self.project_config["python.path"] = get_venv_python() return GlobalEnvironment(self) return Environment(self)
def editables_candidate(environment: Environment) -> Candidate | None: """Return a candidate for `editables` package""" with environment.get_finder() as finder: best_match = finder.find_best_candidate("editables") if best_match.best_candidate is None: return None return Candidate.from_installation_candidate( best_match.best_candidate, parse_requirement("editables"), environment)
def get_environment(self) -> Environment: if self.is_global: env = GlobalEnvironment(self) # Rewrite global project's python requires to be # compatible with the exact version env.python_requires = PySpecSet(f"=={self.python.version}") return env if self.config["python.use_venv"]: if self.project_config.get("python.path") and not os.getenv( "PDM_IGNORE_SAVED_PYTHON"): return (GlobalEnvironment(self) if is_venv_python( self.python.executable) else Environment(self)) if os.getenv("VIRTUAL_ENV"): venv = os.getenv("VIRTUAL_ENV") self.core.ui.echo( f"Detected inside an active virtualenv {termui.green(venv)}, " "reuse it.") # Temporary usage, do not save in .pdm.toml self._python = PythonInfo.from_path(get_venv_python( Path(venv))) return GlobalEnvironment(self) existing_venv = next((venv for _, venv in iter_venvs(self)), None) if existing_venv: self.core.ui.echo( f"Virtualenv {termui.green(str(existing_venv))} is reused.", err=True, ) path = existing_venv else: # Create a virtualenv using the selected Python interpreter self.core.ui.echo( "python.use_venv is on, creating a virtualenv for this project...", fg="yellow", err=True, ) backend: str = self.config["venv.backend"] venv_backend = BACKENDS[backend](self, None) path = venv_backend.create(None, (), False, self.config["venv.in_project"]) self.core.ui.echo(f"Virtualenv {path} is created successfully") self.python = PythonInfo.from_path(get_venv_python(path)) return GlobalEnvironment(self) else: return Environment(self)
def from_dist(cls: Type[_T], dist: Distribution, environment: Environment) -> _T: """Create an instance from the distribution""" scheme = environment.get_paths() instance = cls(dist, environment) meta_location = os.path.normcase(dist._path.absolute()) # type: ignore dist_location = os.path.dirname(meta_location) if is_egg_link(dist): # pragma: no cover egg_link_path = cast("Path | None", getattr(dist, "link_file", None)) if not egg_link_path: termui.logger.warn( "No egg link is found for editable distribution %s, do nothing.", dist.metadata["Name"], ) else: link_pointer = os.path.normcase( egg_link_path.open("rb").readline().decode().strip()) if link_pointer != dist_location: raise UninstallError( f"The link pointer in {egg_link_path} doesn't match " f"the location of {dist.metadata['Name']}(at {dist_location}" ) instance.add_path(str(egg_link_path)) instance.add_pth(link_pointer) elif dist.files: for file in dist.files: location = dist.locate_file(file) instance.add_path(str(location)) bare_name, ext = os.path.splitext(location) if ext == ".py": # .pyc files are added by add_path() instance.add_path(bare_name + ".pyo") bin_dir = scheme["scripts"] if os.path.isdir(os.path.join(meta_location, "scripts")): # pragma: no cover for script in os.listdir(os.path.join(meta_location, "scripts")): instance.add_path(os.path.join(bin_dir, script)) if os.name == "nt": instance.add_path(os.path.join(bin_dir, script) + ".bat") # find console_scripts _scripts_to_remove: list[str] = [] for ep in dist.entry_points: if ep.group == "console_scripts": _scripts_to_remove.extend(_script_names(ep.name, False)) elif ep.group == "gui_scripts": _scripts_to_remove.extend(_script_names(ep.name, True)) for s in _scripts_to_remove: instance.add_path(os.path.join(bin_dir, s)) return instance
def get_environment(self) -> Environment: """Get the environment selected by this project""" if self.is_global: env = GlobalEnvironment(self) # Rewrite global project's python requires to be # compatible with the exact version env.python_requires = PySpecSet(f"=={self.python.version}") return env if self.config["use_venv"] and is_venv_python(self.python.executable): # Only recognize venv created by python -m venv and virtualenv>20 return GlobalEnvironment(self) return Environment(self)
def environment(self) -> Environment: if self.is_global: env = GlobalEnvironment(self) # Rewrite global project's python requires to be # compatible with the exact version env.python_requires = PySpecSet( "==" + get_python_version(self.python_executable, True)[0]) return env if self.config["use_venv"] and is_venv_python(self.python_executable): # Only recognize venv created by python -m venv and virtualenv>20 return GlobalEnvironment(self) return Environment(self)
def __init__( self, candidates: Dict[str, Candidate], environment: Environment, retry_times: int = 1, ) -> None: self.candidates = candidates self.environment = environment self.parallel = environment.project.config["parallel_install"] self.all_candidates = environment.project.get_locked_candidates( "__all__") self.working_set = environment.get_working_set() self.retry_times = retry_times
def environment(self) -> Environment: if self.is_global: env = GlobalEnvironment(self) # Rewrite global project's python requires to be # compatible with the exact version env.python_requires = PySpecSet( "==" + get_python_version(env.python_executable, True)) return env if self.config["use_venv"]: venv_python = get_venv_python(self.root) if venv_python: self.project_config["python.path"] = venv_python return GlobalEnvironment(self) return Environment(self)
def install_wheel(wheel: str, environment: Environment, direct_url: dict[str, Any] | None = None) -> None: """Install a normal wheel file into the environment.""" additional_metadata = None if direct_url is not None: additional_metadata = { "direct_url.json": json.dumps(direct_url, indent=2).encode() } _install_wheel( wheel=wheel, interpreter=environment.interpreter.executable, script_kind=_get_kind(environment), scheme_dict=environment.get_paths(), additional_metadata=additional_metadata, )
def __init__( self, candidates: Dict[str, Candidate], environment: Environment, clean: bool = False, dry_run: bool = False, retry_times: int = 1, install_self: bool = False, no_editable: bool | Collection[str] = False, use_install_cache: bool = False, reinstall: bool = False, ) -> None: self.environment = environment self.clean = clean self.dry_run = dry_run self.retry_times = retry_times self.no_editable = no_editable self.install_self = install_self self.use_install_cache = use_install_cache self.reinstall = reinstall self.parallel = environment.project.config["install.parallel"] locked_repository = environment.project.locked_repository self.all_candidate_keys = list(locked_repository.all_candidates) self.working_set = environment.get_working_set() self.ui = environment.project.core.ui if isinstance(self.no_editable, Collection): keys = self.no_editable elif self.no_editable: keys = candidates.keys() else: keys = [] if (self.install_self and getattr(self.environment.project.meta, "editable_backend", "path") == "editables" and "editables" not in candidates): # Install `editables` as well as required by self project editables = editables_candidate(environment) if editables is not None: candidates["editables"] = editables for key in keys: if key in candidates: candidates[key].req.editable = False self.candidates = candidates self._manager: InstallManager | None = None
def install_wheel_with_cache(wheel: str, environment: Environment, direct_url: dict[str, Any] | None = None) -> None: """Only create .pth files referring to the cached package. If the cache doesn't exist, create one. """ wheel_stem = Path(wheel).stem cache_path = environment.project.cache("packages") / wheel_stem package_cache = CachedPackage(cache_path) interpreter = environment.interpreter.executable script_kind = _get_kind(environment) if not cache_path.is_dir(): logger.debug("Installing wheel into cached location %s", cache_path) cache_path.mkdir(exist_ok=True) _install_wheel( wheel=wheel, interpreter=interpreter, script_kind=script_kind, scheme_dict=package_cache.scheme(), ) additional_metadata = {"REFER_TO": package_cache.path.as_posix().encode()} if direct_url is not None: additional_metadata["direct_url.json"] = json.dumps(direct_url, indent=2).encode() def skip_files(scheme: Scheme, path: str) -> bool: return not (scheme == "scripts" or path.split("/")[0].endswith(".dist-info") or path.endswith(".pth")) filename = wheel_stem.split("-")[0] + ".pth" lib_path = package_cache.scheme()["purelib"] dist_info_dir = _install_wheel( wheel=wheel, interpreter=interpreter, script_kind=script_kind, scheme_dict=environment.get_paths(), excludes=skip_files, additional_files=[(None, filename, io.BytesIO(f"{lib_path}\n".encode()))], additional_metadata=additional_metadata, ) package_cache.add_referrer(dist_info_dir)
def environment(self) -> Environment: if self.is_global: env = GlobalEnvironment(self) # Rewrite global project's python requires to be # compatible with the exact version env.python_requires = PySpecSet( "==" + get_python_version(env.python_executable, True)[0]) return env if not self.project_config.get( "python.path") and self.config["use_venv"]: venv_python = get_venv_python(self.root) if venv_python: self.project_config["python.path"] = venv_python if (self.config["use_venv"] and self.project_config.get("python.path") and Path(self.project_config.get("python.path") ).parent.parent.joinpath("pyvenv.cfg").exists()): # Only recognize venv created by python -m venv and virtualenv>20 return GlobalEnvironment(self) return Environment(self)
def install_wheel_with_cache(wheel: str, environment: Environment, direct_url: dict[str, Any] | None = None) -> None: """Only create .pth files referring to the cached package. If the cache doesn't exist, create one. """ wheel_stem = Path(wheel).stem cache_path = environment.project.cache("packages") / wheel_stem package_cache = CachedPackage(cache_path) interpreter = str(environment.interpreter.executable) script_kind = _get_kind(environment) supports_symlink = (environment.project.config["install.cache_method"] == "symlink" and fs_supports_symlink()) if not cache_path.is_dir(): logger.debug("Installing wheel into cached location %s", cache_path) cache_path.mkdir(exist_ok=True) destination = InstallDestination( scheme_dict=package_cache.scheme(), interpreter=interpreter, script_kind=script_kind, ) _install_wheel(wheel=wheel, destination=destination) additional_metadata = {"REFER_TO": package_cache.path.as_posix().encode()} if direct_url is not None: additional_metadata["direct_url.json"] = json.dumps(direct_url, indent=2).encode() def skip_files(source: WheelFile, element: WheelContentElement) -> bool: root_scheme = _process_WHEEL_file(source) scheme, path = _determine_scheme(element[0][0], source, root_scheme) return not ( scheme not in ("purelib", "platlib") or path.split("/")[0].endswith(".dist-info") # We need to skip the *-nspkg.pth files generated by setuptools' # namespace_packages merchanism. See issue#623 for details or not supports_symlink and path.endswith(".pth") and not path.endswith("-nspkg.pth")) additional_contents: list[WheelContentElement] = [] lib_path = package_cache.scheme()["purelib"] if not supports_symlink: # HACK: Prefix with aaa_ to make it processed as early as possible filename = "aaa_" + wheel_stem.split("-")[0] + ".pth" stream = io.BytesIO(f"{lib_path}\n".encode()) additional_contents.append( ((filename, "", str(len(stream.getvalue()))), stream, False)) destination = InstallDestination( scheme_dict=environment.get_paths(), interpreter=interpreter, script_kind=script_kind, symlink_to=lib_path if supports_symlink else None, ) dist_info_dir = _install_wheel( wheel=wheel, destination=destination, excludes=skip_files, additional_contents=additional_contents, additional_metadata=additional_metadata, ) package_cache.add_referrer(dist_info_dir)
def environment(self) -> Environment: return Environment(self.python_requires, self.config)