Update vendored distro (#79227)

Commit bb35d41 in branch python2.7-support from 2022-10-10:
https://github.com/python-distro/distro/commit/bb35d41
This commit is contained in:
Christian Loos 2022-11-23 20:44:15 +01:00 committed by GitHub
parent 505b29b2a9
commit f79a54ae22
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 76 additions and 81 deletions

View File

@ -0,0 +1,2 @@
minor_changes:
- updated the vendored distro library to upstream version (https://github.com/ansible/ansible/pull/79227)

View File

@ -31,6 +31,8 @@ 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
@ -136,56 +138,6 @@ _DISTRO_RELEASE_IGNORE_BASENAMES = (
)
#
# Python 2.6 does not have subprocess.check_output so replicate it here
#
def _my_check_output(*popenargs, **kwargs):
r"""Run command with arguments and return its output as a byte string.
If the exit code was non-zero it raises a CalledProcessError. The
CalledProcessError object will have the return code in the returncode
attribute and output in the output attribute.
The arguments are the same as for the Popen constructor. Example:
>>> check_output(["ls", "-l", "/dev/null"])
'crw-rw-rw- 1 root root 1, 3 Oct 18 2007 /dev/null\n'
The stdout argument is not allowed as it is used internally.
To capture standard error in the result, use stderr=STDOUT.
>>> check_output(["/bin/sh", "-c",
... "ls -l non_existent_file ; exit 0"],
... stderr=STDOUT)
'ls: non_existent_file: No such file or directory\n'
This is a backport of Python-2.7's check output to Python-2.6
"""
if 'stdout' in kwargs:
raise ValueError(
'stdout argument not allowed, it will be overridden.'
)
process = subprocess.Popen(
stdout=subprocess.PIPE, *popenargs, **kwargs
)
output, unused_err = process.communicate()
retcode = process.poll()
if retcode:
cmd = kwargs.get("args")
if cmd is None:
cmd = popenargs[0]
# Deviation from Python-2.7: Python-2.6's CalledProcessError does not
# have an argument for the stdout so simply omit it.
raise subprocess.CalledProcessError(retcode, cmd)
return output
try:
_check_output = subprocess.check_output
except AttributeError:
_check_output = _my_check_output
def linux_distribution(full_distribution_name=True):
# type: (bool) -> Tuple[str, str, str]
"""
@ -204,7 +156,8 @@ def linux_distribution(full_distribution_name=True):
* ``version``: The result of :func:`distro.version`.
* ``codename``: The result of :func:`distro.codename`.
* ``codename``: The extra item (usually in parentheses) after the
os-release version number, or the result of :func:`distro.codename`.
The interface of this function is compatible with the original
:py:func:`platform.linux_distribution` function, supporting a subset of
@ -251,8 +204,9 @@ def id():
"fedora" Fedora
"sles" SUSE Linux Enterprise Server
"opensuse" openSUSE
"amazon" Amazon Linux
"amzn" Amazon Linux
"arch" Arch Linux
"buildroot" Buildroot
"cloudlinux" CloudLinux OS
"exherbo" Exherbo Linux
"gentoo" GenToo Linux
@ -272,6 +226,8 @@ def id():
"netbsd" NetBSD
"freebsd" FreeBSD
"midnightbsd" MidnightBSD
"rocky" Rocky Linux
"guix" Guix System
============== =========================================
If you have a need to get distros for reliable IDs added into this set,
@ -366,6 +322,10 @@ def version(pretty=False, best=False):
sources in a fixed priority order does not always yield the most precise
version (e.g. for Debian 8.2, or CentOS 7.1).
Some other distributions may not provide this kind of information. In these
cases, an empty string would be returned. This behavior can be observed
with rolling releases distributions (e.g. Arch Linux).
The *best* parameter can be used to control the approach for the returned
version:
@ -681,7 +641,7 @@ except ImportError:
def __get__(self, obj, owner):
# type: (Any, Type[Any]) -> Any
assert obj is not None, "call {0} on an instance".format(self._fname)
assert obj is not None, "call {} on an instance".format(self._fname)
ret = obj.__dict__[self._fname] = self._f(obj)
return ret
@ -776,10 +736,6 @@ class LinuxDistribution(object):
* :py:exc:`IOError`: Some I/O issue with an os-release file or distro
release file.
* :py:exc:`subprocess.CalledProcessError`: The lsb_release command had
some issue (other than not being available in the program execution
path).
* :py:exc:`UnicodeError`: A data source has unexpected characters or
uses an unexpected encoding.
"""
@ -837,7 +793,7 @@ class LinuxDistribution(object):
return (
self.name() if full_distribution_name else self.id(),
self.version(),
self.codename(),
self._os_release_info.get("release_codename") or self.codename(),
)
def id(self):
@ -913,6 +869,9 @@ class LinuxDistribution(object):
).get("version_id", ""),
self.uname_attr("release"),
]
if self.id() == "debian" or "debian" in self.like().split():
# On Debian-like, add debian_version file content to candidates list.
versions.append(self._debian_version)
version = ""
if best:
# This algorithm uses the last version in priority order that has
@ -1155,12 +1114,17 @@ class LinuxDistribution(object):
# stripped, etc.), so the tokens are now either:
# * variable assignments: var=value
# * commands or their arguments (not allowed in os-release)
# Ignore any tokens that are not variable assignments
if "=" in token:
k, v = token.split("=", 1)
props[k.lower()] = v
else:
# Ignore any tokens that are not variable assignments
pass
if "version" in props:
# extract release codename (if any) from version attribute
match = re.search(r"\((\D+)\)|,\s*(\D+)", props["version"])
if match:
release_codename = match.group(1) or match.group(2)
props["codename"] = props["release_codename"] = release_codename
if "version_codename" in props:
# os-release added a version_codename field. Use that in
@ -1171,16 +1135,6 @@ class LinuxDistribution(object):
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:
# If there is no version_codename, parse it from the version
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
return props
@ -1198,7 +1152,7 @@ class LinuxDistribution(object):
with open(os.devnull, "wb") as devnull:
try:
cmd = ("lsb_release", "-a")
stdout = _check_output(cmd, stderr=devnull)
stdout = subprocess.check_output(cmd, stderr=devnull)
# Command not found or lsb_release returned error
except (OSError, subprocess.CalledProcessError):
return {}
@ -1233,18 +1187,31 @@ class LinuxDistribution(object):
@cached_property
def _uname_info(self):
# type: () -> Dict[str, str]
if not self.include_uname:
return {}
with open(os.devnull, "wb") as devnull:
try:
cmd = ("uname", "-rs")
stdout = _check_output(cmd, stderr=devnull)
stdout = subprocess.check_output(cmd, stderr=devnull)
except OSError:
return {}
content = self._to_str(stdout).splitlines()
return self._parse_uname_content(content)
@cached_property
def _debian_version(self):
# type: () -> str
try:
with open(os.path.join(self.etc_dir, "debian_version")) as fp:
return fp.readline().rstrip()
except (OSError, IOError):
return ""
@staticmethod
def _parse_uname_content(lines):
# type: (Sequence[str]) -> Dict[str, str]
if not lines:
return {}
props = {}
match = re.search(r"^([^\s]+)\s+([\d\.]+)", lines[0].strip())
if match:
@ -1270,7 +1237,7 @@ class LinuxDistribution(object):
if isinstance(text, bytes):
return text.decode(encoding)
else:
if isinstance(text, unicode): # noqa pylint: disable=undefined-variable
if isinstance(text, unicode): # noqa
return text.encode(encoding)
return text
@ -1325,6 +1292,7 @@ class LinuxDistribution(object):
"manjaro-release",
"oracle-release",
"redhat-release",
"rocky-release",
"sl-release",
"slackware-version",
]
@ -1403,13 +1371,36 @@ def main():
logger.setLevel(logging.DEBUG)
logger.addHandler(logging.StreamHandler(sys.stdout))
dist = _distro
parser = argparse.ArgumentParser(description="OS distro info tool")
parser.add_argument(
"--json", "-j", help="Output in machine readable format", action="store_true"
)
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)
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(dist.info(), indent=4, sort_keys=True))
else:
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__":

View File

@ -72,8 +72,10 @@ lib/ansible/module_utils/compat/selinux.py import-3.9!skip # pass/fail depends o
lib/ansible/module_utils/distro/_distro.py future-import-boilerplate # ignore bundled
lib/ansible/module_utils/distro/_distro.py metaclass-boilerplate # ignore bundled
lib/ansible/module_utils/distro/_distro.py no-assert
lib/ansible/module_utils/distro/_distro.py pylint:using-constant-test # bundled code we don't want to modify
lib/ansible/module_utils/distro/_distro.py pep8!skip # bundled code we don't want to modify
lib/ansible/module_utils/distro/_distro.py pylint:ansible-format-automatic-specification # ignore bundled
lib/ansible/module_utils/distro/_distro.py pylint:undefined-variable # ignore bundled
lib/ansible/module_utils/distro/_distro.py pylint:using-constant-test # bundled code we don't want to modify
lib/ansible/module_utils/distro/__init__.py empty-init # breaks namespacing, bundled, do not override
lib/ansible/module_utils/facts/__init__.py empty-init # breaks namespacing, deprecate and eventually remove
lib/ansible/module_utils/facts/network/linux.py pylint:disallowed-name