diff options
author | Jon Dufresne <jon.dufresne@gmail.com> | 2021-08-02 07:42:41 -0700 |
---|---|---|
committer | Xavier Fernandez <xav.fernandez@gmail.com> | 2021-08-15 21:54:40 +0200 |
commit | 3064f71161047cf57f55950a86b48d44cae83ced (patch) | |
tree | 7af67754d7c8aef2793d1335df41207f034ed9b8 | |
parent | 402bbcbd12ca68b5eac176fd7d651f0f0fa31057 (diff) | |
download | pip-3064f71161047cf57f55950a86b48d44cae83ced.tar.gz |
Upgrade vendored distro to use new typing and API
The most recent version of distro, 1.6.0, include type information. This
allows pip to remove some workarounds. See upstream commit:
https://github.com/python-distro/distro/commit/20cb68d6b00179135f1461d51972fb3395e75363
The new version also deprecated the top level function
distro.linux_distribution(). Switch to the new preferred API. See
upstream commit:
https://github.com/python-distro/distro/commit/f947776f5a9f018325f0daa446b92d62f1811a91
-rw-r--r-- | news/c8285e11-5b65-498c-9eaa-85ca096243f9.vendor.rst | 1 | ||||
-rw-r--r-- | pyproject.toml | 1 | ||||
-rw-r--r-- | src/pip/_internal/network/session.py | 15 | ||||
-rw-r--r-- | src/pip/_vendor/distro.py | 504 | ||||
-rw-r--r-- | src/pip/_vendor/distro.pyi | 1 | ||||
-rw-r--r-- | src/pip/_vendor/vendor.txt | 2 |
6 files changed, 335 insertions, 189 deletions
diff --git a/news/c8285e11-5b65-498c-9eaa-85ca096243f9.vendor.rst b/news/c8285e11-5b65-498c-9eaa-85ca096243f9.vendor.rst new file mode 100644 index 000000000..759696ce2 --- /dev/null +++ b/news/c8285e11-5b65-498c-9eaa-85ca096243f9.vendor.rst @@ -0,0 +1 @@ +Upgrade distro to 1.6.0. diff --git a/pyproject.toml b/pyproject.toml index 9f6dbe12a..f54c016ac 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,6 +51,7 @@ drop = [ [tool.vendoring.typing-stubs] six = ["six.__init__", "six.moves.__init__", "six.moves.configparser"] appdirs = [] +distro = [] [tool.vendoring.license.directories] setuptools = "pkg_resources" diff --git a/src/pip/_internal/network/session.py b/src/pip/_internal/network/session.py index faaae4059..fe1cf717e 100644 --- a/src/pip/_internal/network/session.py +++ b/src/pip/_internal/network/session.py @@ -2,16 +2,6 @@ network request configuration and behavior. """ -# When mypy runs on Windows the call to distro.linux_distribution() is skipped -# resulting in the failure: -# -# error: unused 'type: ignore' comment -# -# If the upstream module adds typing, this comment should be removed. See -# https://github.com/nir0s/distro/pull/269 -# -# mypy: warn-unused-ignores=False - import email.utils import ipaddress import json @@ -128,9 +118,8 @@ def user_agent() -> str: 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( + linux_distribution = distro.name(), distro.version(), distro.codename() + distro_infos: Dict[str, Any] = dict( filter( lambda x: x[1], zip(["name", "version", "id"], linux_distribution), diff --git a/src/pip/_vendor/distro.py b/src/pip/_vendor/distro.py index 0611b62a3..789274134 100644 --- a/src/pip/_vendor/distro.py +++ b/src/pip/_vendor/distro.py @@ -20,26 +20,61 @@ machine-readable distro ID, or version information. It is the recommended replacement for Python's original :py:func:`platform.linux_distribution` function, but it provides much more functionality. An alternative implementation became necessary because Python -3.5 deprecated this function, and Python 3.8 will remove it altogether. -Its predecessor function :py:func:`platform.dist` was already -deprecated since Python 2.6 and will also be removed in Python 3.8. -Still, there are many cases in which access to OS distribution information -is needed. See `Python issue 1322 <https://bugs.python.org/issue1322>`_ for -more information. +3.5 deprecated this function, and Python 3.8 removed it altogether. Its +predecessor function :py:func:`platform.dist` was already deprecated since +Python 2.6 and removed in Python 3.8. Still, there are many cases in which +access to OS distribution information is needed. See `Python issue 1322 +<https://bugs.python.org/issue1322>`_ for more information. """ +import argparse +import json +import logging import os import re -import sys -import json import shlex -import logging -import argparse import subprocess +import sys +import warnings + +__version__ = "1.6.0" + +# Use `if False` to avoid an ImportError on Python 2. After dropping Python 2 +# support, can use typing.TYPE_CHECKING instead. See: +# https://docs.python.org/3/library/typing.html#typing.TYPE_CHECKING +if False: # pragma: nocover + from typing import ( + Any, + Callable, + Dict, + Iterable, + Optional, + Sequence, + TextIO, + Tuple, + Type, + TypedDict, + Union, + ) + + VersionDict = TypedDict( + "VersionDict", {"major": str, "minor": str, "build_number": str} + ) + InfoDict = TypedDict( + "InfoDict", + { + "id": str, + "version": str, + "version_parts": VersionDict, + "like": str, + "codename": str, + }, + ) -_UNIXCONFDIR = os.environ.get('UNIXCONFDIR', '/etc') -_OS_RELEASE_BASENAME = 'os-release' +_UNIXCONFDIR = os.environ.get("UNIXCONFDIR", "/etc") +_UNIXUSRLIBDIR = os.environ.get("UNIXUSRLIBDIR", "/usr/lib") +_OS_RELEASE_BASENAME = "os-release" #: Translation table for normalizing the "ID" attribute defined in os-release #: files, for use by the :func:`distro.id` method. @@ -49,7 +84,7 @@ _OS_RELEASE_BASENAME = 'os-release' #: #: * Value: Normalized value. NORMALIZED_OS_ID = { - 'ol': 'oracle', # Oracle Linux + "ol": "oracle", # Oracle Linux } #: Translation table for normalizing the "Distributor ID" attribute returned by @@ -60,11 +95,11 @@ NORMALIZED_OS_ID = { #: #: * Value: Normalized value. NORMALIZED_LSB_ID = { - 'enterpriseenterpriseas': 'oracle', # Oracle Enterprise Linux 4 - 'enterpriseenterpriseserver': 'oracle', # Oracle Linux 5 - 'redhatenterpriseworkstation': 'rhel', # RHEL 6, 7 Workstation - 'redhatenterpriseserver': 'rhel', # RHEL 6, 7 Server - 'redhatenterprisecomputenode': 'rhel', # RHEL 6 ComputeNode + "enterpriseenterpriseas": "oracle", # Oracle Enterprise Linux 4 + "enterpriseenterpriseserver": "oracle", # Oracle Linux 5 + "redhatenterpriseworkstation": "rhel", # RHEL 6, 7 Workstation + "redhatenterpriseserver": "rhel", # RHEL 6, 7 Server + "redhatenterprisecomputenode": "rhel", # RHEL 6 ComputeNode } #: Translation table for normalizing the distro ID derived from the file name @@ -75,30 +110,39 @@ NORMALIZED_LSB_ID = { #: #: * Value: Normalized value. NORMALIZED_DISTRO_ID = { - 'redhat': 'rhel', # RHEL 6.x, 7.x + "redhat": "rhel", # RHEL 6.x, 7.x } # Pattern for content of distro release file (reversed) _DISTRO_RELEASE_CONTENT_REVERSED_PATTERN = re.compile( - r'(?:[^)]*\)(.*)\()? *(?:STL )?([\d.+\-a-z]*\d) *(?:esaeler *)?(.+)') + r"(?:[^)]*\)(.*)\()? *(?:STL )?([\d.+\-a-z]*\d) *(?:esaeler *)?(.+)" +) # Pattern for base file name of distro release file -_DISTRO_RELEASE_BASENAME_PATTERN = re.compile( - r'(\w+)[-_](release|version)$') +_DISTRO_RELEASE_BASENAME_PATTERN = re.compile(r"(\w+)[-_](release|version)$") # Base file names to be ignored when searching for distro release file _DISTRO_RELEASE_IGNORE_BASENAMES = ( - 'debian_version', - 'lsb-release', - 'oem-release', + "debian_version", + "lsb-release", + "oem-release", _OS_RELEASE_BASENAME, - 'system-release', - 'plesk-release', + "system-release", + "plesk-release", + "iredmail-release", ) def linux_distribution(full_distribution_name=True): + # type: (bool) -> Tuple[str, str, str] """ + .. deprecated:: 1.6.0 + + :func:`distro.linux_distribution()` is deprecated. It should only be + used as a compatibility shim with Python's + :py:func:`platform.linux_distribution()`. Please use :func:`distro.id`, + :func:`distro.version` and :func:`distro.name` instead. + Return information about the current OS distribution as a tuple ``(id_name, version, codename)`` with items as follows: @@ -122,10 +166,18 @@ def linux_distribution(full_distribution_name=True): method normalizes the distro ID string to a reliable machine-readable value for a number of popular OS distributions. """ + warnings.warn( + "distro.linux_distribution() is deprecated. It should only be used as a " + "compatibility shim with Python's platform.linux_distribution(). Please use " + "distro.id(), distro.version() and distro.name() instead.", + DeprecationWarning, + stacklevel=2, + ) return _distro.linux_distribution(full_distribution_name) def id(): + # type: () -> str """ Return the distro ID of the current distribution, as a machine-readable string. @@ -205,6 +257,7 @@ def id(): def name(pretty=False): + # type: (bool) -> str """ Return the name of the current OS distribution, as a human-readable string. @@ -244,6 +297,7 @@ def name(pretty=False): def version(pretty=False, best=False): + # type: (bool, bool) -> str """ Return the version of the current OS distribution, as a human-readable string. @@ -288,6 +342,7 @@ def version(pretty=False, best=False): def version_parts(best=False): + # type: (bool) -> Tuple[str, str, str] """ Return the version of the current OS distribution as a tuple ``(major, minor, build_number)`` with items as follows: @@ -305,6 +360,7 @@ def version_parts(best=False): def major_version(best=False): + # type: (bool) -> str """ Return the major version of the current OS distribution, as a string, if provided. @@ -318,6 +374,7 @@ def major_version(best=False): def minor_version(best=False): + # type: (bool) -> str """ Return the minor version of the current OS distribution, as a string, if provided. @@ -331,6 +388,7 @@ def minor_version(best=False): def build_number(best=False): + # type: (bool) -> str """ Return the build number of the current OS distribution, as a string, if provided. @@ -344,6 +402,7 @@ def build_number(best=False): def like(): + # type: () -> str """ Return a space-separated list of distro IDs of distributions that are closely related to the current OS distribution in regards to packaging @@ -361,6 +420,7 @@ def like(): def codename(): + # type: () -> str """ Return the codename for the release of the current OS distribution, as a string. @@ -385,6 +445,7 @@ def codename(): def info(pretty=False, best=False): + # type: (bool, bool) -> InfoDict """ Return certain machine-readable information items about the current OS distribution in a dictionary, as shown in the following example: @@ -429,6 +490,7 @@ def info(pretty=False, best=False): def os_release_info(): + # type: () -> Dict[str, str] """ Return a dictionary containing key-value pairs for the information items from the os-release file data source of the current OS distribution. @@ -439,6 +501,7 @@ def os_release_info(): def lsb_release_info(): + # type: () -> Dict[str, str] """ Return a dictionary containing key-value pairs for the information items from the lsb_release command data source of the current OS distribution. @@ -450,6 +513,7 @@ def lsb_release_info(): def distro_release_info(): + # type: () -> Dict[str, str] """ Return a dictionary containing key-value pairs for the information items from the distro release file data source of the current OS distribution. @@ -460,6 +524,7 @@ def distro_release_info(): def uname_info(): + # type: () -> Dict[str, str] """ Return a dictionary containing key-value pairs for the information items from the distro release file data source of the current OS distribution. @@ -468,6 +533,7 @@ def uname_info(): def os_release_attr(attribute): + # type: (str) -> str """ Return a single named information item from the os-release file data source of the current OS distribution. @@ -487,6 +553,7 @@ def os_release_attr(attribute): def lsb_release_attr(attribute): + # type: (str) -> str """ Return a single named information item from the lsb_release command output data source of the current OS distribution. @@ -507,6 +574,7 @@ def lsb_release_attr(attribute): def distro_release_attr(attribute): + # type: (str) -> str """ Return a single named information item from the distro release file data source of the current OS distribution. @@ -526,6 +594,7 @@ def distro_release_attr(attribute): def uname_attr(attribute): + # type: (str) -> str """ Return a single named information item from the distro release file data source of the current OS distribution. @@ -542,19 +611,26 @@ def uname_attr(attribute): return _distro.uname_attr(attribute) -class cached_property(object): - """A version of @property which caches the value. On access, it calls the - underlying function and sets the value in `__dict__` so future accesses - will not re-call the property. - """ - def __init__(self, f): - self._fname = f.__name__ - self._f = f +try: + from functools import cached_property +except ImportError: + # Python < 3.8 + class cached_property(object): # type: ignore + """A version of @property which caches the value. On access, it calls the + underlying function and sets the value in `__dict__` so future accesses + will not re-call the property. + """ + + def __init__(self, f): + # type: (Callable[[Any], Any]) -> None + self._fname = f.__name__ + self._f = f - def __get__(self, obj, owner): - assert obj is not None, 'call {} on an instance'.format(self._fname) - ret = obj.__dict__[self._fname] = self._f(obj) - return ret + def __get__(self, obj, owner): + # type: (Any, Type[Any]) -> Any + assert obj is not None, "call {} on an instance".format(self._fname) + ret = obj.__dict__[self._fname] = self._f(obj) + return ret class LinuxDistribution(object): @@ -575,11 +651,15 @@ class LinuxDistribution(object): lsb_release command. """ - def __init__(self, - include_lsb=True, - os_release_file='', - distro_release_file='', - include_uname=True): + def __init__( + self, + include_lsb=True, + os_release_file="", + distro_release_file="", + include_uname=True, + root_dir=None, + ): + # type: (bool, str, str, bool, Optional[str]) -> None """ The initialization method of this class gathers information from the available data sources, and stores that in private instance attributes. @@ -618,6 +698,9 @@ class LinuxDistribution(object): the program execution path the data source for the uname command will be empty. + * ``root_dir`` (string): The absolute path to the root directory to use + to find distro-related information files. + Public instance attributes: * ``os_release_file`` (string): The path name of the @@ -647,28 +730,50 @@ class LinuxDistribution(object): * :py:exc:`UnicodeError`: A data source has unexpected characters or uses an unexpected encoding. """ - self.os_release_file = os_release_file or \ - os.path.join(_UNIXCONFDIR, _OS_RELEASE_BASENAME) - self.distro_release_file = distro_release_file or '' # updated later + self.root_dir = root_dir + self.etc_dir = os.path.join(root_dir, "etc") if root_dir else _UNIXCONFDIR + self.usr_lib_dir = ( + os.path.join(root_dir, "usr/lib") if root_dir else _UNIXUSRLIBDIR + ) + + if os_release_file: + self.os_release_file = os_release_file + else: + etc_dir_os_release_file = os.path.join(self.etc_dir, _OS_RELEASE_BASENAME) + usr_lib_os_release_file = os.path.join( + self.usr_lib_dir, _OS_RELEASE_BASENAME + ) + + # NOTE: The idea is to respect order **and** have it set + # at all times for API backwards compatibility. + if os.path.isfile(etc_dir_os_release_file) or not os.path.isfile( + usr_lib_os_release_file + ): + self.os_release_file = etc_dir_os_release_file + else: + self.os_release_file = usr_lib_os_release_file + + self.distro_release_file = distro_release_file or "" # updated later self.include_lsb = include_lsb self.include_uname = include_uname def __repr__(self): - """Return repr of all info - """ - return \ - "LinuxDistribution(" \ - "os_release_file={self.os_release_file!r}, " \ - "distro_release_file={self.distro_release_file!r}, " \ - "include_lsb={self.include_lsb!r}, " \ - "include_uname={self.include_uname!r}, " \ - "_os_release_info={self._os_release_info!r}, " \ - "_lsb_release_info={self._lsb_release_info!r}, " \ - "_distro_release_info={self._distro_release_info!r}, " \ - "_uname_info={self._uname_info!r})".format( - self=self) + # type: () -> str + """Return repr of all info""" + return ( + "LinuxDistribution(" + "os_release_file={self.os_release_file!r}, " + "distro_release_file={self.distro_release_file!r}, " + "include_lsb={self.include_lsb!r}, " + "include_uname={self.include_uname!r}, " + "_os_release_info={self._os_release_info!r}, " + "_lsb_release_info={self._lsb_release_info!r}, " + "_distro_release_info={self._distro_release_info!r}, " + "_uname_info={self._uname_info!r})".format(self=self) + ) def linux_distribution(self, full_distribution_name=True): + # type: (bool) -> Tuple[str, str, str] """ Return information about the OS distribution that is compatible with Python's :func:`platform.linux_distribution`, supporting a subset @@ -679,92 +784,102 @@ class LinuxDistribution(object): return ( self.name() if full_distribution_name else self.id(), self.version(), - self.codename() + self.codename(), ) def id(self): + # type: () -> str """Return the distro ID of the OS distribution, as a string. For details, see :func:`distro.id`. """ + def normalize(distro_id, table): - distro_id = distro_id.lower().replace(' ', '_') + # type: (str, Dict[str, str]) -> str + distro_id = distro_id.lower().replace(" ", "_") return table.get(distro_id, distro_id) - distro_id = self.os_release_attr('id') + distro_id = self.os_release_attr("id") if distro_id: return normalize(distro_id, NORMALIZED_OS_ID) - distro_id = self.lsb_release_attr('distributor_id') + distro_id = self.lsb_release_attr("distributor_id") if distro_id: return normalize(distro_id, NORMALIZED_LSB_ID) - distro_id = self.distro_release_attr('id') + distro_id = self.distro_release_attr("id") if distro_id: return normalize(distro_id, NORMALIZED_DISTRO_ID) - distro_id = self.uname_attr('id') + distro_id = self.uname_attr("id") if distro_id: return normalize(distro_id, NORMALIZED_DISTRO_ID) - return '' + return "" def name(self, pretty=False): + # type: (bool) -> str """ Return the name of the OS distribution, as a string. For details, see :func:`distro.name`. """ - name = self.os_release_attr('name') \ - or self.lsb_release_attr('distributor_id') \ - or self.distro_release_attr('name') \ - or self.uname_attr('name') + name = ( + self.os_release_attr("name") + or self.lsb_release_attr("distributor_id") + or self.distro_release_attr("name") + or self.uname_attr("name") + ) if pretty: - name = self.os_release_attr('pretty_name') \ - or self.lsb_release_attr('description') + name = self.os_release_attr("pretty_name") or self.lsb_release_attr( + "description" + ) if not name: - name = self.distro_release_attr('name') \ - or self.uname_attr('name') + name = self.distro_release_attr("name") or self.uname_attr("name") version = self.version(pretty=True) if version: - name = name + ' ' + version - return name or '' + name = name + " " + version + return name or "" def version(self, pretty=False, best=False): + # type: (bool, bool) -> str """ Return the version of the OS distribution, as a string. For details, see :func:`distro.version`. """ versions = [ - self.os_release_attr('version_id'), - self.lsb_release_attr('release'), - self.distro_release_attr('version_id'), - self._parse_distro_release_content( - self.os_release_attr('pretty_name')).get('version_id', ''), + self.os_release_attr("version_id"), + self.lsb_release_attr("release"), + self.distro_release_attr("version_id"), + self._parse_distro_release_content(self.os_release_attr("pretty_name")).get( + "version_id", "" + ), self._parse_distro_release_content( - self.lsb_release_attr('description')).get('version_id', ''), - self.uname_attr('release') + self.lsb_release_attr("description") + ).get("version_id", ""), + self.uname_attr("release"), ] - version = '' + version = "" if best: # This algorithm uses the last version in priority order that has # the best precision. If the versions are not in conflict, that # does not matter; otherwise, using the last one instead of the # first one might be considered a surprise. for v in versions: - if v.count(".") > version.count(".") or version == '': + if v.count(".") > version.count(".") or version == "": version = v else: for v in versions: - if v != '': + if v != "": version = v break if pretty and version and self.codename(): - version = '{0} ({1})'.format(version, self.codename()) + version = "{0} ({1})".format(version, self.codename()) return version def version_parts(self, best=False): + # type: (bool) -> Tuple[str, str, str] """ Return the version of the OS distribution, as a tuple of version numbers. @@ -773,14 +888,15 @@ class LinuxDistribution(object): """ version_str = self.version(best=best) if version_str: - version_regex = re.compile(r'(\d+)\.?(\d+)?\.?(\d+)?') + version_regex = re.compile(r"(\d+)\.?(\d+)?\.?(\d+)?") matches = version_regex.match(version_str) if matches: major, minor, build_number = matches.groups() - return major, minor or '', build_number or '' - return '', '', '' + return major, minor or "", build_number or "" + return "", "", "" def major_version(self, best=False): + # type: (bool) -> str """ Return the major version number of the current distribution. @@ -789,6 +905,7 @@ class LinuxDistribution(object): return self.version_parts(best)[0] def minor_version(self, best=False): + # type: (bool) -> str """ Return the minor version number of the current distribution. @@ -797,6 +914,7 @@ class LinuxDistribution(object): return self.version_parts(best)[1] def build_number(self, best=False): + # type: (bool) -> str """ Return the build number of the current distribution. @@ -805,14 +923,16 @@ class LinuxDistribution(object): return self.version_parts(best)[2] def like(self): + # type: () -> str """ Return the IDs of distributions that are like the OS distribution. For details, see :func:`distro.like`. """ - return self.os_release_attr('id_like') or '' + return self.os_release_attr("id_like") or "" def codename(self): + # type: () -> str """ Return the codename of the OS distribution. @@ -821,13 +941,16 @@ class LinuxDistribution(object): try: # Handle os_release specially since distros might purposefully set # this to empty string to have no codename - return self._os_release_info['codename'] + return self._os_release_info["codename"] except KeyError: - return self.lsb_release_attr('codename') \ - or self.distro_release_attr('codename') \ - or '' + return ( + self.lsb_release_attr("codename") + or self.distro_release_attr("codename") + or "" + ) def info(self, pretty=False, best=False): + # type: (bool, bool) -> InfoDict """ Return certain machine-readable information about the OS distribution. @@ -840,13 +963,14 @@ class LinuxDistribution(object): version_parts=dict( major=self.major_version(best), minor=self.minor_version(best), - build_number=self.build_number(best) + build_number=self.build_number(best), ), like=self.like(), codename=self.codename(), ) def os_release_info(self): + # type: () -> Dict[str, str] """ Return a dictionary containing key-value pairs for the information items from the os-release file data source of the OS distribution. @@ -856,6 +980,7 @@ class LinuxDistribution(object): return self._os_release_info def lsb_release_info(self): + # type: () -> Dict[str, str] """ Return a dictionary containing key-value pairs for the information items from the lsb_release command data source of the OS @@ -866,6 +991,7 @@ class LinuxDistribution(object): return self._lsb_release_info def distro_release_info(self): + # type: () -> Dict[str, str] """ Return a dictionary containing key-value pairs for the information items from the distro release file data source of the OS @@ -876,6 +1002,7 @@ class LinuxDistribution(object): return self._distro_release_info def uname_info(self): + # type: () -> Dict[str, str] """ Return a dictionary containing key-value pairs for the information items from the uname command data source of the OS distribution. @@ -885,43 +1012,48 @@ class LinuxDistribution(object): return self._uname_info def os_release_attr(self, attribute): + # type: (str) -> str """ Return a single named information item from the os-release file data source of the OS distribution. For details, see :func:`distro.os_release_attr`. """ - return self._os_release_info.get(attribute, '') + return self._os_release_info.get(attribute, "") def lsb_release_attr(self, attribute): + # type: (str) -> str """ Return a single named information item from the lsb_release command output data source of the OS distribution. For details, see :func:`distro.lsb_release_attr`. """ - return self._lsb_release_info.get(attribute, '') + return self._lsb_release_info.get(attribute, "") def distro_release_attr(self, attribute): + # type: (str) -> str """ Return a single named information item from the distro release file data source of the OS distribution. For details, see :func:`distro.distro_release_attr`. """ - return self._distro_release_info.get(attribute, '') + return self._distro_release_info.get(attribute, "") def uname_attr(self, attribute): + # type: (str) -> str """ Return a single named information item from the uname command output data source of the OS distribution. - For details, see :func:`distro.uname_release_attr`. + For details, see :func:`distro.uname_attr`. """ - return self._uname_info.get(attribute, '') + return self._uname_info.get(attribute, "") @cached_property def _os_release_info(self): + # type: () -> Dict[str, str] """ Get the information items from the specified os-release file. @@ -935,6 +1067,7 @@ class LinuxDistribution(object): @staticmethod def _parse_os_release_content(lines): + # type: (TextIO) -> Dict[str, str] """ Parse the lines of an os-release file. @@ -959,7 +1092,7 @@ class LinuxDistribution(object): # parsed content is a unicode object. The following fix resolves that # (... but it should be fixed in shlex...): if sys.version_info[0] == 2 and isinstance(lexer.wordchars, bytes): - lexer.wordchars = lexer.wordchars.decode('iso-8859-1') + lexer.wordchars = lexer.wordchars.decode("iso-8859-1") tokens = list(lexer) for token in tokens: @@ -969,37 +1102,38 @@ class LinuxDistribution(object): # stripped, etc.), so the tokens are now either: # * variable assignments: var=value # * commands or their arguments (not allowed in os-release) - if '=' in token: - k, v = token.split('=', 1) + if "=" in token: + k, v = token.split("=", 1) props[k.lower()] = v else: # Ignore any tokens that are not variable assignments pass - if 'version_codename' in props: + if "version_codename" in props: # os-release added a version_codename field. Use that in # preference to anything else Note that some distros purposefully # do not have code names. They should be setting # version_codename="" - props['codename'] = props['version_codename'] - elif 'ubuntu_codename' in props: + props["codename"] = props["version_codename"] + elif "ubuntu_codename" in props: # Same as above but a non-standard field name used on older Ubuntus - props['codename'] = props['ubuntu_codename'] - elif 'version' in props: + props["codename"] = props["ubuntu_codename"] + elif "version" in props: # If there is no version_codename, parse it from the version - codename = re.search(r'(\(\D+\))|,(\s+)?\D+', props['version']) - if codename: - codename = codename.group() - codename = codename.strip('()') - codename = codename.strip(',') + match = re.search(r"(\(\D+\))|,(\s+)?\D+", props["version"]) + if match: + codename = match.group() + codename = codename.strip("()") + codename = codename.strip(",") codename = codename.strip() # codename appears within paranthese. - props['codename'] = codename + props["codename"] = codename return props @cached_property def _lsb_release_info(self): + # type: () -> Dict[str, str] """ Get the information items from the lsb_release command output. @@ -1008,17 +1142,19 @@ class LinuxDistribution(object): """ if not self.include_lsb: return {} - with open(os.devnull, 'w') as devnull: + with open(os.devnull, "wb") as devnull: try: - cmd = ('lsb_release', '-a') + cmd = ("lsb_release", "-a") stdout = subprocess.check_output(cmd, stderr=devnull) - except OSError: # Command not found + # Command not found or lsb_release returned error + except (OSError, subprocess.CalledProcessError): return {} content = self._to_str(stdout).splitlines() return self._parse_lsb_release_content(content) @staticmethod def _parse_lsb_release_content(lines): + # type: (Iterable[str]) -> Dict[str, str] """ Parse the output of the lsb_release command. @@ -1033,19 +1169,20 @@ class LinuxDistribution(object): """ props = {} for line in lines: - kv = line.strip('\n').split(':', 1) + kv = line.strip("\n").split(":", 1) if len(kv) != 2: # Ignore lines without colon. continue k, v = kv - props.update({k.replace(' ', '_').lower(): v.strip()}) + props.update({k.replace(" ", "_").lower(): v.strip()}) return props @cached_property def _uname_info(self): - with open(os.devnull, 'w') as devnull: + # type: () -> Dict[str, str] + with open(os.devnull, "wb") as devnull: try: - cmd = ('uname', '-rs') + cmd = ("uname", "-rs") stdout = subprocess.check_output(cmd, stderr=devnull) except OSError: return {} @@ -1054,25 +1191,27 @@ class LinuxDistribution(object): @staticmethod def _parse_uname_content(lines): + # type: (Sequence[str]) -> Dict[str, str] props = {} - match = re.search(r'^([^\s]+)\s+([\d\.]+)', lines[0].strip()) + match = re.search(r"^([^\s]+)\s+([\d\.]+)", lines[0].strip()) if match: name, version = match.groups() # This is to prevent the Linux kernel version from # appearing as the 'best' version on otherwise # identifiable distributions. - if name == 'Linux': + if name == "Linux": return {} - props['id'] = name.lower() - props['name'] = name - props['release'] = version + props["id"] = name.lower() + props["name"] = name + props["release"] = version return props @staticmethod def _to_str(text): + # type: (Union[bytes, str]) -> str encoding = sys.getfilesystemencoding() - encoding = 'utf-8' if encoding == 'ascii' else encoding + encoding = "utf-8" if encoding == "ascii" else encoding if sys.version_info[0] >= 3: if isinstance(text, bytes): @@ -1085,6 +1224,7 @@ class LinuxDistribution(object): @cached_property def _distro_release_info(self): + # type: () -> Dict[str, str] """ Get the information items from the specified distro release file. @@ -1094,23 +1234,21 @@ class LinuxDistribution(object): if self.distro_release_file: # If it was specified, we use it and parse what we can, even if # its file name or content does not match the expected pattern. - distro_info = self._parse_distro_release_file( - self.distro_release_file) + distro_info = self._parse_distro_release_file(self.distro_release_file) basename = os.path.basename(self.distro_release_file) # The file name pattern for user-specified distro release files # is somewhat more tolerant (compared to when searching for the # file), because we want to use what was specified as best as # possible. match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename) - if 'name' in distro_info \ - and 'cloudlinux' in distro_info['name'].lower(): - distro_info['id'] = 'cloudlinux' + if "name" in distro_info and "cloudlinux" in distro_info["name"].lower(): + distro_info["id"] = "cloudlinux" elif match: - distro_info['id'] = match.group(1) + distro_info["id"] = match.group(1) return distro_info else: try: - basenames = os.listdir(_UNIXCONFDIR) + basenames = os.listdir(self.etc_dir) # We sort for repeatability in cases where there are multiple # distro specific files; e.g. CentOS, Oracle, Enterprise all # containing `redhat-release` on top of their own. @@ -1120,38 +1258,41 @@ class LinuxDistribution(object): # sure about the *-release files. Check common entries of # /etc for information. If they turn out to not be there the # error is handled in `_parse_distro_release_file()`. - basenames = ['SuSE-release', - 'arch-release', - 'base-release', - 'centos-release', - 'fedora-release', - 'gentoo-release', - 'mageia-release', - 'mandrake-release', - 'mandriva-release', - 'mandrivalinux-release', - 'manjaro-release', - 'oracle-release', - 'redhat-release', - 'sl-release', - 'slackware-version'] + basenames = [ + "SuSE-release", + "arch-release", + "base-release", + "centos-release", + "fedora-release", + "gentoo-release", + "mageia-release", + "mandrake-release", + "mandriva-release", + "mandrivalinux-release", + "manjaro-release", + "oracle-release", + "redhat-release", + "sl-release", + "slackware-version", + ] for basename in basenames: if basename in _DISTRO_RELEASE_IGNORE_BASENAMES: continue match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename) if match: - filepath = os.path.join(_UNIXCONFDIR, basename) + filepath = os.path.join(self.etc_dir, basename) distro_info = self._parse_distro_release_file(filepath) - if 'name' in distro_info: + if "name" in distro_info: # The name is always present if the pattern matches self.distro_release_file = filepath - distro_info['id'] = match.group(1) - if 'cloudlinux' in distro_info['name'].lower(): - distro_info['id'] = 'cloudlinux' + distro_info["id"] = match.group(1) + if "cloudlinux" in distro_info["name"].lower(): + distro_info["id"] = "cloudlinux" return distro_info return {} def _parse_distro_release_file(self, filepath): + # type: (str) -> Dict[str, str] """ Parse a distro release file. @@ -1170,11 +1311,12 @@ class LinuxDistribution(object): except (OSError, IOError): # Ignore not being able to read a specific, seemingly version # related file. - # See https://github.com/nir0s/distro/issues/162 + # See https://github.com/python-distro/distro/issues/162 return {} @staticmethod def _parse_distro_release_content(line): + # type: (str) -> Dict[str, str] """ Parse a line from a distro release file. @@ -1185,18 +1327,17 @@ class LinuxDistribution(object): Returns: A dictionary containing all information items. """ - matches = _DISTRO_RELEASE_CONTENT_REVERSED_PATTERN.match( - line.strip()[::-1]) + matches = _DISTRO_RELEASE_CONTENT_REVERSED_PATTERN.match(line.strip()[::-1]) distro_info = {} if matches: # regexp ensures non-None - distro_info['name'] = matches.group(3)[::-1] + distro_info["name"] = matches.group(3)[::-1] if matches.group(2): - distro_info['version_id'] = matches.group(2)[::-1] + distro_info["version_id"] = matches.group(2)[::-1] if matches.group(1): - distro_info['codename'] = matches.group(1)[::-1] + distro_info["codename"] = matches.group(1)[::-1] elif line: - distro_info['name'] = line.strip() + distro_info["name"] = line.strip() return distro_info @@ -1204,27 +1345,42 @@ _distro = LinuxDistribution() def main(): + # type: () -> None logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) logger.addHandler(logging.StreamHandler(sys.stdout)) parser = argparse.ArgumentParser(description="OS distro info tool") parser.add_argument( - '--json', - '-j', - help="Output in machine readable format", - action="store_true") + "--json", "-j", help="Output in machine readable format", action="store_true" + ) + + parser.add_argument( + "--root-dir", + "-r", + type=str, + dest="root_dir", + help="Path to the root filesystem directory (defaults to /)", + ) + args = parser.parse_args() + if args.root_dir: + dist = LinuxDistribution( + include_lsb=False, include_uname=False, root_dir=args.root_dir + ) + else: + dist = _distro + if args.json: - logger.info(json.dumps(info(), indent=4, sort_keys=True)) + logger.info(json.dumps(dist.info(), indent=4, sort_keys=True)) else: - logger.info('Name: %s', name(pretty=True)) - distribution_version = version(pretty=True) - logger.info('Version: %s', distribution_version) - distribution_codename = codename() - logger.info('Codename: %s', distribution_codename) + logger.info("Name: %s", dist.name(pretty=True)) + distribution_version = dist.version(pretty=True) + logger.info("Version: %s", distribution_version) + distribution_codename = dist.codename() + logger.info("Codename: %s", distribution_codename) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/src/pip/_vendor/distro.pyi b/src/pip/_vendor/distro.pyi deleted file mode 100644 index c7ea94b37..000000000 --- a/src/pip/_vendor/distro.pyi +++ /dev/null @@ -1 +0,0 @@ -from distro import *
\ No newline at end of file diff --git a/src/pip/_vendor/vendor.txt b/src/pip/_vendor/vendor.txt index 73e493434..ea0c213e7 100644 --- a/src/pip/_vendor/vendor.txt +++ b/src/pip/_vendor/vendor.txt @@ -2,7 +2,7 @@ appdirs==1.4.4 CacheControl==0.12.6 colorama==0.4.4 distlib==0.3.2 -distro==1.5.0 +distro==1.6.0 html5lib==1.1 msgpack==1.0.2 packaging==21.0 |