def create_dependencies(self, force=False): # type: (bool) -> "PackageInfo" """Create values for **self.dependencies**. :param bool force: Sets **self.dependencies** to an empty tuple if it would be None, defaults to False. :return: An updated instance of the current object with **self.dependencies** updated accordingly. :rtype: :class:`PackageInfo` """ if not self.dependencies and not self.requires_dist: if force: return attr.evolve(self, dependencies=tuple()) return self self_dependency = self.to_dependency() deps = set() self_dependencies = tuple() if not self.dependencies else self.dependencies for dep in self_dependencies: if dep is None: continue new_dep = dep.add_parent(self_dependency) deps.add(new_dep) created_deps = create_dependencies(self.requires_dist, parent=self_dependency) if created_deps is not None: for dep in created_deps: if dep is None: continue deps.add(dep) return attr.evolve(self, dependencies=tuple(sorted(deps)))
def get_dependencies(self): # type: () -> "Package" urls = [] # type: List[ReleaseUrl] deps = set() # type: Set[str] info = self.info if info.dependencies is None: for url in self.urls: try: url, dep_dict = url.get_dependencies() except (RuntimeError, TypeError): # This happens if we are parsing `setup.py` and we fail if url.is_sdist: continue else: raise markers = url.markers dep_list = dep_dict.get("requires_dist", []) for dep in dep_list: # XXX: We need to parse these as requirements and "and" the markers # XXX: together because they may contain "extra" markers which we # XXX: will need to parse and remove deps.add(add_markers_to_dep(dep, markers)) urls.append(url) if None in deps: deps.remove(None) info = attr.evolve( self.info, requires_dist=tuple(sorted(deps)) ).create_dependencies(force=True) return attr.evolve(self, info=info, urls=urls)
def _parse_fragment(self): # type: () -> URI subdirectory = self.subdirectory if self.subdirectory else "" fragment = self.fragment if self.fragment else "" if self.fragment is None: return self fragments = self.fragment.split("&") fragment_items = {} name = self.name if self.name else "" extras = self.extras for q in fragments: key, _, val = q.partition("=") val = unquote_plus(val) fragment_items[key] = val if key == "egg": from .utils import parse_extras name, stripped_extras = pip_shims.shims._strip_extras(val) if stripped_extras: extras = tuple(parse_extras(stripped_extras)) elif key == "subdirectory": subdirectory = val return attr.evolve( self, fragment_dict=fragment_items, subdirectory=subdirectory, fragment=fragment, extras=extras, name=name, )
def create(cls, release_dict, name=None): # type: (TReleaseUrlDict, Optional[str]) -> "ReleaseUrl" valid_digest_keys = set("{0}_digest".format(k) for k in VALID_ALGORITHMS.keys()) digest_keys = set(release_dict.keys()) & valid_digest_keys creation_kwargs = { } # type: Dict[str, Union[bool, int, str, Digest, TDigestDict]] creation_kwargs = { k: v for k, v in release_dict.items() if k not in digest_keys } if name is not None: creation_kwargs["name"] = name for k in digest_keys: digest = release_dict[k] if not isinstance(digest, six.string_types): raise TypeError( "Digests must be strings, got {!r}".format(digest)) creation_kwargs[k] = Digest.create(k.replace("_digest", ""), digest) release_url = cls(**filter_dict(creation_kwargs)) # type: ignore if release_url.is_wheel: supported_tags = [ parse_tag(Tag(*tag)) for tag in distlib.wheel.Wheel(release_url.url).tags ] release_url = attr.evolve(release_url, tags=supported_tags) return release_url
def _setup_asdf(self): # type: () -> "SystemPath" if "asdf" in self.finders and self.asdf_finder is not None: return self from .python import PythonFinder os_path = os.environ["PATH"].split(os.pathsep) asdf_finder = PythonFinder.create( root=ASDF_DATA_DIR, ignore_unsupported=True, sort_function=parse_asdf_version_order, version_glob_path="installs/python/*", ) asdf_index = None try: asdf_index = self._get_last_instance(ASDF_DATA_DIR) except ValueError: asdf_index = 0 if is_in_path(next(iter(os_path), ""), ASDF_DATA_DIR) else -1 if asdf_index is None: # we are in a virtualenv without global pyenv on the path, so we should # not write pyenv to the path here return self # * These are the root paths for the finder _ = [p for p in asdf_finder.roots] new_instance = self._slice_in_paths(asdf_index, [asdf_finder.root]) paths = self.paths.copy() paths[asdf_finder.root] = asdf_finder paths.update(asdf_finder.roots) return ( attr.evolve(new_instance, paths=paths, asdf_finder=asdf_finder) ._remove_path(normalize_path(os.path.join(ASDF_DATA_DIR, "shims"))) ._register_finder("asdf", asdf_finder) )
def _setup_pyenv(self): # type: () -> "SystemPath" if "pyenv" in self.finders and self.pyenv_finder is not None: return self from .python import PythonFinder os_path = os.environ["PATH"].split(os.pathsep) pyenv_finder = PythonFinder.create( root=PYENV_ROOT, sort_function=parse_pyenv_version_order, version_glob_path="versions/*", ignore_unsupported=self.ignore_unsupported, ) pyenv_index = None try: pyenv_index = self._get_last_instance(PYENV_ROOT) except ValueError: pyenv_index = 0 if is_in_path(next(iter(os_path), ""), PYENV_ROOT) else -1 if pyenv_index is None: # we are in a virtualenv without global pyenv on the path, so we should # not write pyenv to the path here return self # * These are the root paths for the finder _ = [p for p in pyenv_finder.roots] new_instance = self._slice_in_paths(pyenv_index, [pyenv_finder.root]) paths = new_instance.paths.copy() paths[pyenv_finder.root] = pyenv_finder paths.update(pyenv_finder.roots) return ( attr.evolve(new_instance, paths=paths, pyenv_finder=pyenv_finder) ._remove_path(os.path.join(PYENV_ROOT, "shims")) ._register_finder("pyenv", pyenv_finder) )
def add_dependency(self, dependency): # type: ("Dependency") -> "ExtrasCollection" if not isinstance(dependency, Dependency): raise TypeError( "Expected a Dependency instance, received {0!r}".format(dependency) ) dependencies = self.dependencies.copy() dependencies.add(dependency) return attr.evolve(self, dependencies=dependencies)
def create_release_urls_from_list(urls, name=None): # type: (Union[TReleasesList, List[ReleaseUrl]], Optional[str]) -> List[ReleaseUrl] url_list = [] for release_dict in urls: if isinstance(release_dict, ReleaseUrl): if name and not release_dict.name: release_dict = attr.evolve(release_dict, name=name) url_list.append(release_dict) continue url_list.append(ReleaseUrl.create(release_dict, name=name)) return url_list
def _remove_path(self, path): # type: (str) -> "SystemPath" path_copy = [p for p in reversed(self.path_order[:])] new_order = [] target = normalize_path(path) path_map = {normalize_path(pth): pth for pth in self.paths.keys()} new_paths = self.paths.copy() if target in path_map: del new_paths[path_map[target]] for current_path in path_copy: normalized = normalize_path(current_path) if normalized != target: new_order.append(normalized) new_order = [ensure_path(p).as_posix() for p in reversed(new_order)] return attr.evolve(self, path_order=new_order, paths=new_paths)
def _slice_in_paths(self, start_idx, paths): # type: (int, List[Path]) -> "SystemPath" before_path = [] # type: List[str] after_path = [] # type: List[str] if start_idx == 0: after_path = self.path_order[:] elif start_idx == -1: before_path = self.path_order[:] else: before_path = self.path_order[: start_idx + 1] after_path = self.path_order[start_idx + 2 :] path_order = before_path + [p.as_posix() for p in paths] + after_path if path_order == self.path_order: return self return attr.evolve(self, path_order=path_order)
def _setup_windows(self): # type: () -> "SystemPath" if "windows" in self.finders and self.windows_finder is not None: return self from .windows import WindowsFinder windows_finder = WindowsFinder.create() root_paths = (p for p in windows_finder.paths if p.is_root) path_addition = [p.path.as_posix() for p in root_paths] new_path_order = self.path_order[:] + path_addition new_paths = self.paths.copy() new_paths.update({p.path: p for p in root_paths}) return attr.evolve( self, windows_finder=windows_finder, path_order=new_path_order, paths=new_paths, )._register_finder("windows", windows_finder)
def clear_caches(self): for key in ["executables", "python_executables", "version_dict", "path_entries"]: if key in self.__dict__: del self.__dict__[key] for finder in list(self.__finders.keys()): del self.__finders[finder] self.__finders = {} return attr.evolve( self, executables=[], python_executables={}, python_version_dict=defaultdict(list), version_dict=defaultdict(list), pyenv_finder=None, windows_finder=None, asdf_finder=None, path_order=[], paths=defaultdict(PathEntry), )
def _parse_query(self): # type: () -> URI query = self.query if self.query is not None else "" query_dict = omdict() queries = query.split("&") query_items = [] subdirectory = self.subdirectory if self.subdirectory else None for q in queries: key, _, val = q.partition("=") val = unquote_plus(val) if key == "subdirectory" and not subdirectory: subdirectory = val else: query_items.append((key, val)) query_dict.load(query_items) return attr.evolve(self, query_dict=query_dict, subdirectory=subdirectory, query=query)
def _parse_auth(self): # type: () -> URI if self._auth: username, _, password = self._auth.partition(":") username_is_quoted, password_is_quoted = False, False quoted_username, quoted_password = "", "" if password: quoted_password = quote(password) password_is_quoted = quoted_password != password if username: quoted_username = quote(username) username_is_quoted = quoted_username != username return attr.evolve( self, username=quoted_username, password=quoted_password, username_is_quoted=username_is_quoted, password_is_quoted=password_is_quoted, ) return self
def get_dependencies(self): # type: () -> Tuple["ReleaseUrl", Dict[str, Union[List[str], str]]] results = {"requires_python": None} requires_dist = [] # type: List[str] if self.is_wheel: metadata = get_remote_wheel_metadata(self.url) if metadata is not None: requires_dist = metadata.run_requires if not self.requires_python: results["requires_python"] = metadata._legacy.get("Requires-Python") else: try: metadata = get_remote_sdist_metadata(self.pep508_url) except Exception: requires_dist = [] else: requires_dist = [str(v) for v in metadata.requires.values()] results["requires_dist"] = requires_dist requires_python = getattr(self, "requires_python", results["requires_python"]) return attr.evolve(self, requires_python=requires_python), results
def _run_setup(self): # type: () -> "SystemPath" if not self.__class__ == SystemPath: return self new_instance = self path_order = new_instance.path_order[:] path_entries = self.paths.copy() if self.global_search and "PATH" in os.environ: path_order = path_order + os.environ["PATH"].split(os.pathsep) path_order = list(dedup(path_order)) path_instances = [ ensure_path(p.strip('"')) for p in path_order if not any( is_in_path(normalize_path(str(p)), normalize_path(shim)) for shim in SHIM_PATHS ) ] path_entries.update( { p.as_posix(): PathEntry.create( path=p.absolute(), is_root=True, only_python=self.only_python ) for p in path_instances if p.exists() } ) new_instance = attr.evolve( new_instance, path_order=[p.as_posix() for p in path_instances if p.exists()], paths=path_entries, ) if os.name == "nt" and "windows" not in self.finders: new_instance = new_instance._setup_windows() #: slice in pyenv if self.check_for_pyenv() and "pyenv" not in self.finders: new_instance = new_instance._setup_pyenv() #: slice in asdf if self.check_for_asdf() and "asdf" not in self.finders: new_instance = new_instance._setup_asdf() venv = os.environ.get("VIRTUAL_ENV") if os.name == "nt": bin_dir = "Scripts" else: bin_dir = "bin" if venv and (new_instance.system or new_instance.global_search): p = ensure_path(venv) path_order = [(p / bin_dir).as_posix()] + new_instance.path_order new_instance = attr.evolve(new_instance, path_order=path_order) paths = new_instance.paths.copy() paths[p] = new_instance.get_path(p.joinpath(bin_dir)) new_instance = attr.evolve(new_instance, paths=paths) if new_instance.system: syspath = Path(sys.executable) syspath_bin = syspath.parent if syspath_bin.name != bin_dir and syspath_bin.joinpath(bin_dir).exists(): syspath_bin = syspath_bin / bin_dir path_order = [syspath_bin.as_posix()] + new_instance.path_order paths = new_instance.paths.copy() paths[syspath_bin] = PathEntry.create( path=syspath_bin, is_root=True, only_python=False ) new_instance = attr.evolve(new_instance, path_order=path_order, paths=paths) return new_instance
def add_parent(self, parent): # type: ("Dependency") -> "Dependency" return attr.evolve(self, parent=parent)