def get_benchopt_requirement(): """Specification for pip requirement to install benchopt in conda env. Find out how benchopt was installed so we can install the same version even if it was installed in develop mode. This requires pip version >= 20.1 Returns ------- pip_requirement : str String to pass to pip to instal benchopt in another environment. is_editable : bool Whether the current installation is in development mode or not. """ # Ignore distutils replacement warning when importing pip package. It is # not clear why this started to trigger such warning in #265 # XXX - Investigate this warning and fix it. import warnings warnings.filterwarnings("ignore", message="Setuptools is replacing distutils.", category=UserWarning) from pip._internal.metadata import get_default_environment from pip._internal.operations.freeze import FrozenRequirement dist = get_default_environment().get_distribution('benchopt') # If benchopt is installed in editable mode, get the module path to install # it directly from the folder. Else, install it correctly even if it is # installed with an url. assert dist is not None, ( 'benchopt is not installed in the current environment?') req = FrozenRequirement.from_dist(dist) if req.editable: return f'-e {dist.location}', True return str(req), False
def uninstall(self, auto_confirm: bool = False, verbose: bool = False) -> Optional[UninstallPathSet]: """ Uninstall the distribution currently satisfying this requirement. Prompts before removing or modifying files unless ``auto_confirm`` is True. Refuses to delete or modify files outside of ``sys.prefix`` - thus uninstallation within a virtual environment can only modify that virtual environment, even if the virtualenv is linked to global site-packages. """ assert self.req dist = get_default_environment().get_distribution(self.req.name) if not dist: logger.warning("Skipping %s as it is not installed.", self.name) return None logger.info("Found existing installation: %s", dist) uninstalled_pathset = UninstallPathSet.from_dist(dist) uninstalled_pathset.remove(auto_confirm, verbose) return uninstalled_pathset
def get_installed_distributions( local_only: bool = True, skip: Container[str] = stdlib_pkgs, include_editables: bool = True, editables_only: bool = False, user_only: bool = False, paths: Optional[List[str]] = None, ) -> List[Distribution]: # pragma: no cover """Return a list of installed Distribution objects. Left for compatibility until direct pkg_resources uses are refactored out. """ if paths is None: env = get_default_environment() else: env = get_environment(paths) dists = env.iter_installed_distributions( local_only=local_only, skip=skip, include_editables=include_editables, editables_only=editables_only, user_only=user_only, ) return [cast(_Dist, dist)._dist for dist in dists]
def check_requirements( self, reqs: Iterable[str]) -> Tuple[Set[Tuple[str, str]], Set[str]]: """Return 2 sets: - conflicting requirements: set of (installed, wanted) reqs tuples - missing requirements: set of reqs """ missing = set() conflicting = set() if reqs: env = (get_environment(self._lib_dirs) if hasattr( self, "_lib_dirs") else get_default_environment()) for req_str in reqs: req = Requirement(req_str) if req.marker is not None and not req.marker.evaluate(): continue # FIXME: Consider extras? dist = env.get_distribution(req.name) if not dist: missing.add(req_str) continue if isinstance(dist.version, Version): installed_req_str = f"{req.name}=={dist.version}" else: installed_req_str = f"{req.name}==={dist.version}" if dist.version not in req.specifier: conflicting.add((installed_req_str, req_str)) # FIXME: Consider direct URL? return conflicting, missing
def pip_self_version_check(session: PipSession, options: optparse.Values) -> None: """Check for an update for pip. Limit the frequency of checks to once per week. State is stored either in the active virtualenv or in the user's USER_CACHE_DIR keyed off the prefix of the pip script path. """ installed_dist = get_default_environment().get_distribution("pip") if not installed_dist: return try: upgrade_prompt = _self_version_check_logic( state=SelfCheckState(cache_dir=options.cache_dir), current_time=datetime.datetime.utcnow(), local_version=installed_dist.version, get_remote_version=functools.partial( _get_current_remote_pip_version, session, options), ) if upgrade_prompt is not None: logger.info("[present-rich] %s", upgrade_prompt) except Exception: logger.warning( "There was an error checking the latest version of pip.") logger.debug("See below for error", exc_info=True)
def check_requirements( self, reqs: Iterable[str] ) -> Tuple[Set[Tuple[str, str]], Set[str]]: """Return 2 sets: - conflicting requirements: set of (installed, wanted) reqs tuples - missing requirements: set of reqs """ missing = set() conflicting = set() if reqs: env = ( get_environment(self._lib_dirs) if hasattr(self, "_lib_dirs") else get_default_environment() ) for req_str in reqs: req = Requirement(req_str) # We're explicitly evaluating with an empty extra value, since build # environments are not provided any mechanism to select specific extras. if req.marker is not None and not req.marker.evaluate({"extra": ""}): continue dist = env.get_distribution(req.name) if not dist: missing.add(req_str) continue if isinstance(dist.version, Version): installed_req_str = f"{req.name}=={dist.version}" else: installed_req_str = f"{req.name}==={dist.version}" if not req.specifier.contains(dist.version, prereleases=True): conflicting.add((installed_req_str, req_str)) # FIXME: Consider direct URL? return conflicting, missing
def get_installed_distributions( local_only=True, # type: bool skip=stdlib_pkgs, # type: Container[str] include_editables=True, # type: bool editables_only=False, # type: bool user_only=False, # type: bool paths=None # type: Optional[List[str]] ): # type: (...) -> List[Distribution] """Return a list of installed Distribution objects. Left for compatibility until direct pkg_resources uses are refactored out. """ from pip._internal.metadata import get_default_environment, get_environment from pip._internal.metadata.pkg_resources import Distribution as _Dist if paths is None: env = get_default_environment() else: env = get_environment(paths) dists = env.iter_installed_distributions( local_only=local_only, skip=skip, include_editables=include_editables, editables_only=editables_only, user_only=user_only, ) return [cast(_Dist, dist)._dist for dist in dists]
def get_benchopt_requirement(): """Specification for pip requirement to install benchopt in conda env. Find out how benchopt where installed so we can install the same version even if it was installed in develop mode. This requires pip version >= 20.1 Returns ------- pip_requirement : str String to pass to pip to instal benchopt in another environment. is_editable : bool Whether the current installation is in development mode or not. """ from pip._internal.metadata import get_default_environment from pip._internal.operations.freeze import FrozenRequirement dist = get_default_environment().get_distribution('benchopt') # If benchopt is installed in editable mode, get the module path to install # it directly from the folder. Else, install it correctly even if it is # installed with an url. assert dist is not None, ( 'benchopt is not installed in the current environment?') req = FrozenRequirement.from_dist(dist) if req.editable: return f'-e {dist.location}', True return str(req), False
def was_installed_by_pip(pkg: str) -> bool: """Checks whether pkg was installed by pip This is used not to display the upgrade message when pip is in fact installed by system package manager, such as dnf on Fedora. """ dist = get_default_environment().get_distribution(pkg) return dist is not None and "pip" == dist.installer
def get_installed_distributions(local_only=True, skip=None, include_editables=True, editables_only=False, user_only=False, use_cmd=False): """ Directs call to function *get_installed_distributions* from :epkg:`pip`. Return a list of installed Distribution objects. :param local_only: if True (default), only return installations local to the current virtualenv, if in a virtualenv. :param skip: argument is an iterable of lower-case project names to ignore; defaults to ``pip.compat.stdlib_pkgs`` (if *skip* is None) :param editables: if False, don't report editables. :param editables_only: if True , only report editables. :param user_only: if True , only report installations in the user site directory. :param use_cmd: if True, use a different process (updated package list) :return: list of installed Distribution objects. .. versionadded:: 1.5 """ if use_cmd: raise NotImplementedError("use_cmd should be False") if skip is None: try: from pip._internal.utils.compat import stdlib_pkgs skip = stdlib_pkgs except ImportError: pass try: from pip._internal.metadata import get_default_environment return list( map( Distribution, get_default_environment().iter_installed_distributions( local_only=local_only, skip=skip, include_editables=include_editables, editables_only=editables_only, user_only=user_only))) except ImportError: from pip._internal.utils.misc import get_installed_distributions as getd return list( map( Distribution, getd(local_only=local_only, skip=skip, include_editables=include_editables, editables_only=editables_only, user_only=user_only, use_cmd=use_cmd)))
def print_dist_installation_info(name: str, latest: str) -> None: env = get_default_environment() dist = env.get_distribution(name) if dist is not None: with indent_log(): if dist.version == latest: write_output('INSTALLED: %s (latest)', dist.version) else: write_output('INSTALLED: %s', dist.version) if parse_version(latest).pre: write_output('LATEST: %s (pre-release; install' ' with "pip install --pre")', latest) else: write_output('LATEST: %s', latest)
def create_package_set_from_installed() -> Tuple[PackageSet, bool]: """Converts a list of distributions into a PackageSet.""" package_set = {} problems = False env = get_default_environment() for dist in env.iter_installed_distributions(local_only=False, skip=()): name = dist.canonical_name try: dependencies = list(dist.iter_dependencies()) package_set[name] = PackageDetails(dist.version, dependencies) except (OSError, ValueError) as e: # Don't crash on unreadable or broken metadata. logger.warning("Error parsing requirements for %s: %s", name, e) problems = True return package_set, problems
def get_distribution(req_name): # type: (str) -> Optional[Distribution] """Given a requirement name, return the installed Distribution object. This searches from *all* distributions available in the environment, to match the behavior of ``pkg_resources.get_distribution()``. Left for compatibility until direct pkg_resources uses are refactored out. """ from pip._internal.metadata import get_default_environment from pip._internal.metadata.pkg_resources import Distribution as _Dist dist = get_default_environment().get_distribution(req_name) if dist is None: return None return cast(_Dist, dist)._dist
def test_uninstallpathset_no_paths(caplog: pytest.LogCaptureFixture) -> None: """ Test UninstallPathSet logs notification when there are no paths to uninstall """ from pip._internal.metadata import get_default_environment from pip._internal.req.req_uninstall import UninstallPathSet caplog.set_level(logging.INFO) test_dist = get_default_environment().get_distribution("pip") assert test_dist is not None, "pip not installed" uninstall_set = UninstallPathSet(test_dist) uninstall_set.remove() # with no files added to set assert "Can't uninstall 'pip'. No files were found to uninstall." in caplog.text
def get_installed_distributions(): from pip._internal.metadata import ( get_default_environment, get_environment, ) from pip._internal.metadata.pkg_resources import ( Distribution as _Dist, ) from pip._internal.utils.compat import stdlib_pkgs env = get_default_environment() dists = env.iter_installed_distributions( local_only=True, skip=stdlib_pkgs, include_editables=True, editables_only=False, user_only=False, ) return [dist._dist for dist in dists]
def print_results(hits, name_column_width=None, terminal_width=None): # type: (List[TransformedHit], Optional[int], Optional[int]) -> None if not hits: return if name_column_width is None: name_column_width = max([ len(hit['name']) + len(highest_version(hit.get('versions', ['-']))) for hit in hits ]) + 4 env = get_default_environment() for hit in hits: name = hit['name'] summary = hit['summary'] or '' latest = highest_version(hit.get('versions', ['-'])) if terminal_width is not None: target_width = terminal_width - name_column_width - 5 if target_width > 10: # wrap and indent summary to fit terminal summary_lines = textwrap.wrap(summary, target_width) summary = ('\n' + ' ' * (name_column_width + 3)).join(summary_lines) line = '{name_latest:{name_column_width}} - {summary}'.format( name_latest='{name} ({latest})'.format(**locals()), **locals()) try: write_output(line) dist = env.get_distribution(name) if dist is not None: with indent_log(): if dist.version == latest: write_output('INSTALLED: %s (latest)', dist.version) else: write_output('INSTALLED: %s', dist.version) if parse_version(latest).pre: write_output( 'LATEST: %s (pre-release; install' ' with "pip install --pre")', latest) else: write_output('LATEST: %s', latest) except UnicodeEncodeError: pass
def get_installed_distributions( local_only = True, skip = stdlib_pkgs, include_editables = True, editables_only = False, user_only = False, paths = None, ): if paths is None: env = get_default_environment() else: env = get_environment(paths) dists = env.iter_installed_distributions( local_only=local_only, skip=skip, include_editables=include_editables, editables_only=editables_only, user_only=user_only, ) return dists
def __init__( self, finder: PackageFinder, preparer: RequirementPreparer, make_install_req: InstallRequirementProvider, wheel_cache: Optional[WheelCache], use_user_site: bool, force_reinstall: bool, ignore_installed: bool, ignore_requires_python: bool, suppress_build_failures: bool, py_version_info: Optional[Tuple[int, ...]] = None, ) -> None: self._finder = finder self.preparer = preparer self._wheel_cache = wheel_cache self._python_candidate = RequiresPythonCandidate(py_version_info) self._make_install_req_from_spec = make_install_req self._use_user_site = use_user_site self._force_reinstall = force_reinstall self._ignore_requires_python = ignore_requires_python self._suppress_build_failures = suppress_build_failures self._build_failures: Cache[InstallationError] = {} self._link_candidate_cache: Cache[LinkCandidate] = {} self._editable_candidate_cache: Cache[EditableCandidate] = {} self._installed_candidate_cache: Dict[str, AlreadyInstalledCandidate] = {} self._extras_candidate_cache: Dict[Tuple[int, FrozenSet[str]], ExtrasCandidate] = {} if not ignore_installed: env = get_default_environment() self._installed_dists = { dist.canonical_name: dist for dist in env.iter_installed_distributions(local_only=False) } else: self._installed_dists = {}
def check_if_exists(self, use_user_site: bool) -> None: """Find an installed distribution that satisfies or conflicts with this requirement, and set self.satisfied_by or self.should_reinstall appropriately. """ if self.req is None: return existing_dist = get_default_environment().get_distribution( self.req.name) if not existing_dist: return version_compatible = self.req.specifier.contains( existing_dist.version, prereleases=True, ) if not version_compatible: self.satisfied_by = None if use_user_site: if existing_dist.in_usersite: self.should_reinstall = True elif running_under_virtualenv( ) and existing_dist.in_site_packages: raise InstallationError( f"Will not install to the user site because it will " f"lack sys.path precedence to {existing_dist.raw_name} " f"in {existing_dist.location}") else: self.should_reinstall = True else: if self.editable: self.should_reinstall = True # when installing editables, nothing pre-existing should ever # satisfy self.satisfied_by = None else: self.satisfied_by = existing_dist
def get_installed_distributions( local_only=True, include_editables=True, editables_only=False, user_only=False, paths=None, ): """Return a list of installed Distribution objects. Left for compatibility until direct pkg_resources uses are refactored out. """ from pip._internal.metadata import get_default_environment, get_environment from pip._internal.metadata.pkg_resources import Distribution as _Dist if paths is None: env = get_default_environment() else: env = get_environment(paths) dists = env.iter_installed_distributions( local_only=local_only, include_editables=include_editables, editables_only=editables_only, user_only=user_only, ) return [cast(_Dist, dist)._dist for dist in dists]
def search_packages_info(query: List[str]) -> Iterator[_PackageInfo]: """ Gather details from installed distributions. Print distribution name, version, location, and installed files. Installed files requires a pip generated 'installed-files.txt' in the distributions '.egg-info' directory. """ env = get_default_environment() installed = {dist.canonical_name: dist for dist in env.iter_distributions()} query_names = [canonicalize_name(name) for name in query] missing = sorted( [name for name, pkg in zip(query, query_names) if pkg not in installed] ) if missing: logger.warning("Package(s) not found: %s", ", ".join(missing)) def _get_requiring_packages(current_dist: BaseDistribution) -> Iterator[str]: return ( dist.metadata["Name"] or "UNKNOWN" for dist in installed.values() if current_dist.canonical_name in {canonicalize_name(d.name) for d in dist.iter_dependencies()} ) for query_name in query_names: try: dist = installed[query_name] except KeyError: continue requires = sorted((req.name for req in dist.iter_dependencies()), key=str.lower) required_by = sorted(_get_requiring_packages(dist), key=str.lower) try: entry_points_text = dist.read_text("entry_points.txt") entry_points = entry_points_text.splitlines(keepends=False) except FileNotFoundError: entry_points = [] files_iter = dist.iter_declared_entries() if files_iter is None: files: Optional[List[str]] = None else: files = sorted(files_iter) metadata = dist.metadata yield _PackageInfo( name=dist.raw_name, version=str(dist.version), location=dist.location or "", requires=requires, required_by=required_by, installer=dist.installer, metadata_version=dist.metadata_version or "", classifiers=metadata.get_all("Classifier", []), summary=metadata.get("Summary", ""), homepage=metadata.get("Home-page", ""), author=metadata.get("Author", ""), author_email=metadata.get("Author-email", ""), license=metadata.get("License", ""), entry_points=entry_points, files=files, )
def autocomplete() -> None: """Entry Point for completion of main and subcommand options.""" # Don't complete if user hasn't sourced bash_completion file. if "PIP_AUTO_COMPLETE" not in os.environ: return cwords = os.environ["COMP_WORDS"].split()[1:] cword = int(os.environ["COMP_CWORD"]) try: current = cwords[cword - 1] except IndexError: current = "" parser = create_main_parser() subcommands = list(commands_dict) options = [] # subcommand subcommand_name: Optional[str] = None for word in cwords: if word in subcommands: subcommand_name = word break # subcommand options if subcommand_name is not None: # special case: 'help' subcommand has no options if subcommand_name == "help": sys.exit(1) # special case: list locally installed dists for show and uninstall should_list_installed = not current.startswith( "-") and subcommand_name in [ "show", "uninstall", ] if should_list_installed: env = get_default_environment() lc = current.lower() installed = [ dist.canonical_name for dist in env.iter_installed_distributions(local_only=True) if dist.canonical_name.startswith(lc) and dist.canonical_name not in cwords[1:] ] # if there are no dists installed, fall back to option completion if installed: for dist in installed: print(dist) sys.exit(1) should_list_installables = (not current.startswith("-") and subcommand_name == "install") if should_list_installables: for path in auto_complete_paths(current, "path"): print(path) sys.exit(1) subcommand = create_command(subcommand_name) for opt in subcommand.parser.option_list_all: if opt.help != optparse.SUPPRESS_HELP: for opt_str in opt._long_opts + opt._short_opts: options.append((opt_str, opt.nargs)) # filter out previously specified options from available options prev_opts = [x.split("=")[0] for x in cwords[1:cword - 1]] options = [(x, v) for (x, v) in options if x not in prev_opts] # filter options by current input options = [(k, v) for k, v in options if k.startswith(current)] # get completion type given cwords and available subcommand options completion_type = get_path_completion_type( cwords, cword, subcommand.parser.option_list_all, ) # get completion files and directories if ``completion_type`` is # ``<file>``, ``<dir>`` or ``<path>`` if completion_type: paths = auto_complete_paths(current, completion_type) options = [(path, 0) for path in paths] for option in options: opt_label = option[0] # append '=' to options which require args if option[1] and option[0][:2] == "--": opt_label += "=" print(opt_label) else: # show main parser options only when necessary opts = [i.option_list for i in parser.option_groups] opts.append(parser.option_list) flattened_opts = chain.from_iterable(opts) if current.startswith("-"): for opt in flattened_opts: if opt.help != optparse.SUPPRESS_HELP: subcommands += opt._long_opts + opt._short_opts else: # get completion type given cwords and all available options completion_type = get_path_completion_type(cwords, cword, flattened_opts) if completion_type: subcommands = list( auto_complete_paths(current, completion_type)) print(" ".join([x for x in subcommands if x.startswith(current)])) sys.exit(1)
def user_agent() -> str: """ Return a string representing the user agent. """ data: Dict[str, Any] = { "installer": {"name": "pip", "version": __version__}, "python": platform.python_version(), "implementation": { "name": platform.python_implementation(), }, } if data["implementation"]["name"] == "CPython": data["implementation"]["version"] = platform.python_version() elif data["implementation"]["name"] == "PyPy": pypy_version_info = sys.pypy_version_info # type: ignore if pypy_version_info.releaselevel == "final": pypy_version_info = pypy_version_info[:3] data["implementation"]["version"] = ".".join( [str(x) for x in pypy_version_info] ) elif data["implementation"]["name"] == "Jython": # Complete Guess data["implementation"]["version"] = platform.python_version() elif data["implementation"]["name"] == "IronPython": # Complete Guess data["implementation"]["version"] = platform.python_version() if sys.platform.startswith("linux"): from pip._vendor import distro # https://github.com/nir0s/distro/pull/269 linux_distribution = distro.linux_distribution() # type: ignore distro_infos = dict( filter( lambda x: x[1], zip(["name", "version", "id"], linux_distribution), ) ) libc = dict( filter( lambda x: x[1], zip(["lib", "version"], libc_ver()), ) ) if libc: distro_infos["libc"] = libc if distro_infos: data["distro"] = distro_infos if sys.platform.startswith("darwin") and platform.mac_ver()[0]: data["distro"] = {"name": "macOS", "version": platform.mac_ver()[0]} if platform.system(): data.setdefault("system", {})["name"] = platform.system() if platform.release(): data.setdefault("system", {})["release"] = platform.release() if platform.machine(): data["cpu"] = platform.machine() if has_tls(): import _ssl as ssl data["openssl_version"] = ssl.OPENSSL_VERSION setuptools_dist = get_default_environment().get_distribution("setuptools") if setuptools_dist is not None: data["setuptools_version"] = str(setuptools_dist.version) if shutil.which("rustc") is not None: # If for any reason `rustc --version` fails, silently ignore it try: rustc_output = subprocess.check_output( ["rustc", "--version"], stderr=subprocess.STDOUT, timeout=0.5 ) except Exception: pass else: if rustc_output.startswith(b"rustc "): # The format of `rustc --version` is: # `b'rustc 1.52.1 (9bc8c42bb 2021-05-09)\n'` # We extract just the middle (1.52.1) part data["rustc_version"] = rustc_output.split(b" ")[1].decode() # Use None rather than False so as not to give the impression that # pip knows it is not being run under CI. Rather, it is a null or # inconclusive result. Also, we include some value rather than no # value to make it easier to know that the check has been run. data["ci"] = True if looks_like_ci() else None user_data = os.environ.get("PIP_USER_AGENT_USER_DATA") if user_data is not None: data["user_data"] = user_data return "{data[installer][name]}/{data[installer][version]} {json}".format( data=data, json=json.dumps(data, separators=(",", ":"), sort_keys=True), )
def search_packages_info(query: List[str]) -> Iterator[_PackageInfo]: """ Gather details from installed distributions. Print distribution name, version, location, and installed files. Installed files requires a pip generated 'installed-files.txt' in the distributions '.egg-info' directory. """ env = get_default_environment() installed = { dist.canonical_name: dist for dist in env.iter_distributions() } query_names = [canonicalize_name(name) for name in query] missing = sorted([ name for name, pkg in zip(query, query_names) if pkg not in installed ]) if missing: logger.warning('Package(s) not found: %s', ', '.join(missing)) def _get_requiring_packages(current_dist: BaseDistribution) -> List[str]: return [ dist.metadata["Name"] or "UNKNOWN" for dist in installed.values() if current_dist.canonical_name in {canonicalize_name(d.name) for d in dist.iter_dependencies()} ] def _files_from_record(dist: BaseDistribution) -> Optional[Iterator[str]]: try: text = dist.read_text('RECORD') except FileNotFoundError: return None # This extra Path-str cast normalizes entries. return (str(pathlib.Path(row[0])) for row in csv.reader(text.splitlines())) def _files_from_legacy(dist: BaseDistribution) -> Optional[Iterator[str]]: try: text = dist.read_text('installed-files.txt') except FileNotFoundError: return None paths = (p for p in text.splitlines(keepends=False) if p) root = dist.location info = dist.info_directory if root is None or info is None: return paths try: info_rel = pathlib.Path(info).relative_to(root) except ValueError: # info is not relative to root. return paths if not info_rel.parts: # info *is* root. return paths return (_covert_legacy_entry(pathlib.Path(p).parts, info_rel.parts) for p in paths) for query_name in query_names: try: dist = installed[query_name] except KeyError: continue try: entry_points_text = dist.read_text('entry_points.txt') entry_points = entry_points_text.splitlines(keepends=False) except FileNotFoundError: entry_points = [] files_iter = _files_from_record(dist) or _files_from_legacy(dist) if files_iter is None: files: Optional[List[str]] = None else: files = sorted(files_iter) metadata = dist.metadata yield _PackageInfo( name=dist.raw_name, version=str(dist.version), location=dist.location or "", requires=[req.name for req in dist.iter_dependencies()], required_by=_get_requiring_packages(dist), installer=dist.installer, metadata_version=dist.metadata_version or "", classifiers=metadata.get_all("Classifier", []), summary=metadata.get("Summary", ""), homepage=metadata.get("Home-page", ""), author=metadata.get("Author", ""), author_email=metadata.get("Author-email", ""), license=metadata.get("License", ""), entry_points=entry_points, files=files, )
def pip_self_version_check(session, options): # type: (PipSession, optparse.Values) -> None """Check for an update for pip. Limit the frequency of checks to once per week. State is stored either in the active virtualenv or in the user's USER_CACHE_DIR keyed off the prefix of the pip script path. """ installed_dist = get_default_environment().get_distribution("pip") if not installed_dist: return pip_version = installed_dist.version pypi_version = None try: state = SelfCheckState(cache_dir=options.cache_dir) current_time = datetime.datetime.utcnow() # Determine if we need to refresh the state if "last_check" in state.state and "pypi_version" in state.state: last_check = datetime.datetime.strptime(state.state["last_check"], SELFCHECK_DATE_FMT) if (current_time - last_check).total_seconds() < 7 * 24 * 60 * 60: pypi_version = state.state["pypi_version"] # Refresh the version if we need to or just see if we need to warn if pypi_version is None: # Lets use PackageFinder to see what the latest pip version is link_collector = LinkCollector.create( session, options=options, suppress_no_index=True, ) # Pass allow_yanked=False so we don't suggest upgrading to a # yanked version. selection_prefs = SelectionPreferences( allow_yanked=False, allow_all_prereleases=False, # Explicitly set to False ) finder = PackageFinder.create( link_collector=link_collector, selection_prefs=selection_prefs, ) best_candidate = finder.find_best_candidate("pip").best_candidate if best_candidate is None: return pypi_version = str(best_candidate.version) # save that we've performed a check state.save(pypi_version, current_time) remote_version = parse_version(pypi_version) local_version_is_older = ( pip_version < remote_version and pip_version.base_version != remote_version.base_version and was_installed_by_pip('pip')) # Determine if our pypi_version is older if not local_version_is_older: return # We cannot tell how the current pip is available in the current # command context, so be pragmatic here and suggest the command # that's always available. This does not accommodate spaces in # `sys.executable`. pip_cmd = f"{sys.executable} -m pip" logger.warning( "You are using pip version %s; however, version %s is " "available.\nYou should consider upgrading via the " "'%s install --upgrade pip' command.", pip_version, pypi_version, pip_cmd) except Exception: logger.debug( "There was an error checking the latest version of pip", exc_info=True, )
def user_agent(): """ Return a string representing the user agent. """ data = { "installer": { "name": "pip", "version": __version__ }, "python": platform.python_version(), "implementation": { "name": platform.python_implementation(), }, } if data["implementation"]["name"] == 'CPython': data["implementation"]["version"] = platform.python_version() elif data["implementation"]["name"] == 'PyPy': if sys.pypy_version_info.releaselevel == 'final': pypy_version_info = sys.pypy_version_info[:3] else: pypy_version_info = sys.pypy_version_info data["implementation"]["version"] = ".".join( [str(x) for x in pypy_version_info]) elif data["implementation"]["name"] == 'Jython': # Complete Guess data["implementation"]["version"] = platform.python_version() elif data["implementation"]["name"] == 'IronPython': # Complete Guess data["implementation"]["version"] = platform.python_version() if sys.platform.startswith("linux"): from pip._vendor import distro distro_infos = dict( filter( lambda x: x[1], zip(["name", "version", "id"], distro.linux_distribution()), )) libc = dict( filter( lambda x: x[1], zip(["lib", "version"], libc_ver()), )) if libc: distro_infos["libc"] = libc if distro_infos: data["distro"] = distro_infos if sys.platform.startswith("darwin") and platform.mac_ver()[0]: data["distro"] = {"name": "macOS", "version": platform.mac_ver()[0]} if platform.system(): data.setdefault("system", {})["name"] = platform.system() if platform.release(): data.setdefault("system", {})["release"] = platform.release() if platform.machine(): data["cpu"] = platform.machine() if has_tls(): import _ssl as ssl data["openssl_version"] = ssl.OPENSSL_VERSION setuptools_dist = get_default_environment().get_distribution("setuptools") if setuptools_dist is not None: data["setuptools_version"] = str(setuptools_dist.version) # Use None rather than False so as not to give the impression that # pip knows it is not being run under CI. Rather, it is a null or # inconclusive result. Also, we include some value rather than no # value to make it easier to know that the check has been run. data["ci"] = True if looks_like_ci() else None user_data = os.environ.get("PIP_USER_AGENT_USER_DATA") if user_data is not None: data["user_data"] = user_data return "{data[installer][name]}/{data[installer][version]} {json}".format( data=data, json=json.dumps(data, separators=(",", ":"), sort_keys=True), )