Bump bundled distro version to 1.8.0 (#81765)

* Bump bundled distro version to 1.8.0

* Bump bundled distro version from to 1.8.0 from 1.6.0

Fixes: #81713

Signed-off-by: Abhijeet Kasurde <akasurde@redhat.com>

* Remove sanity entries

Signed-off-by: Abhijeet Kasurde <akasurde@redhat.com>

---------

Signed-off-by: Abhijeet Kasurde <akasurde@redhat.com>
pull/82912/head
Abhijeet Kasurde 3 months ago committed by GitHub
parent d86ad77d6f
commit a870e7d0c6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,3 @@
---
bugfixes:
- distro - bump bundled distro version from 1.6.0 to 1.8.0 (https://github.com/ansible/ansible/issues/81713).

@ -41,40 +41,39 @@ import shlex
import subprocess import subprocess
import sys import sys
import warnings import warnings
from typing import (
Any,
Callable,
Dict,
Iterable,
Optional,
Sequence,
TextIO,
Tuple,
Type,
)
__version__ = "1.6.0" try:
from typing import TypedDict
# Use `if False` to avoid an ImportError on Python 2. After dropping Python 2 except ImportError:
# support, can use typing.TYPE_CHECKING instead. See: # Python 3.7
# https://docs.python.org/3/library/typing.html#typing.TYPE_CHECKING TypedDict = dict
if False: # pragma: nocover
from typing import (
Any,
Callable,
Dict,
Iterable,
Optional,
Sequence,
TextIO,
Tuple,
Type,
TypedDict,
Union,
)
VersionDict = TypedDict( __version__ = "1.8.0"
"VersionDict", {"major": str, "minor": str, "build_number": str}
)
InfoDict = TypedDict( class VersionDict(TypedDict):
"InfoDict", major: str
{ minor: str
"id": str, build_number: str
"version": str,
"version_parts": VersionDict,
"like": str, class InfoDict(TypedDict):
"codename": str, id: str
}, version: str
) version_parts: VersionDict
like: str
codename: str
_UNIXCONFDIR = os.environ.get("UNIXCONFDIR", "/etc") _UNIXCONFDIR = os.environ.get("UNIXCONFDIR", "/etc")
@ -127,6 +126,26 @@ _DISTRO_RELEASE_CONTENT_REVERSED_PATTERN = re.compile(
# Pattern for base file name of distro release file # 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 looked up for if _UNIXCONFDIR is not readable.
_DISTRO_RELEASE_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",
"rocky-release",
"sl-release",
"slackware-version",
]
# Base file names to be ignored when searching for distro release file # Base file names to be ignored when searching for distro release file
_DISTRO_RELEASE_IGNORE_BASENAMES = ( _DISTRO_RELEASE_IGNORE_BASENAMES = (
"debian_version", "debian_version",
@ -139,8 +158,7 @@ _DISTRO_RELEASE_IGNORE_BASENAMES = (
) )
def linux_distribution(full_distribution_name=True): def linux_distribution(full_distribution_name: bool = True) -> Tuple[str, str, str]:
# type: (bool) -> Tuple[str, str, str]
""" """
.. deprecated:: 1.6.0 .. deprecated:: 1.6.0
@ -183,8 +201,7 @@ def linux_distribution(full_distribution_name=True):
return _distro.linux_distribution(full_distribution_name) return _distro.linux_distribution(full_distribution_name)
def id(): def id() -> str:
# type: () -> str
""" """
Return the distro ID of the current distribution, as a Return the distro ID of the current distribution, as a
machine-readable string. machine-readable string.
@ -228,6 +245,7 @@ def id():
"freebsd" FreeBSD "freebsd" FreeBSD
"midnightbsd" MidnightBSD "midnightbsd" MidnightBSD
"rocky" Rocky Linux "rocky" Rocky Linux
"aix" AIX
"guix" Guix System "guix" Guix System
============== ========================================= ============== =========================================
@ -266,8 +284,7 @@ def id():
return _distro.id() return _distro.id()
def name(pretty=False): def name(pretty: bool = False) -> str:
# type: (bool) -> str
""" """
Return the name of the current OS distribution, as a human-readable Return the name of the current OS distribution, as a human-readable
string. string.
@ -306,8 +323,7 @@ def name(pretty=False):
return _distro.name(pretty) return _distro.name(pretty)
def version(pretty=False, best=False): def version(pretty: bool = False, best: bool = False) -> str:
# type: (bool, bool) -> str
""" """
Return the version of the current OS distribution, as a human-readable Return the version of the current OS distribution, as a human-readable
string. string.
@ -355,8 +371,7 @@ def version(pretty=False, best=False):
return _distro.version(pretty, best) return _distro.version(pretty, best)
def version_parts(best=False): def version_parts(best: bool = False) -> Tuple[str, str, str]:
# type: (bool) -> Tuple[str, str, str]
""" """
Return the version of the current OS distribution as a tuple Return the version of the current OS distribution as a tuple
``(major, minor, build_number)`` with items as follows: ``(major, minor, build_number)`` with items as follows:
@ -373,8 +388,7 @@ def version_parts(best=False):
return _distro.version_parts(best) return _distro.version_parts(best)
def major_version(best=False): def major_version(best: bool = False) -> str:
# type: (bool) -> str
""" """
Return the major version of the current OS distribution, as a string, Return the major version of the current OS distribution, as a string,
if provided. if provided.
@ -387,8 +401,7 @@ def major_version(best=False):
return _distro.major_version(best) return _distro.major_version(best)
def minor_version(best=False): def minor_version(best: bool = False) -> str:
# type: (bool) -> str
""" """
Return the minor version of the current OS distribution, as a string, Return the minor version of the current OS distribution, as a string,
if provided. if provided.
@ -401,8 +414,7 @@ def minor_version(best=False):
return _distro.minor_version(best) return _distro.minor_version(best)
def build_number(best=False): def build_number(best: bool = False) -> str:
# type: (bool) -> str
""" """
Return the build number of the current OS distribution, as a string, Return the build number of the current OS distribution, as a string,
if provided. if provided.
@ -415,8 +427,7 @@ def build_number(best=False):
return _distro.build_number(best) return _distro.build_number(best)
def like(): def like() -> str:
# type: () -> str
""" """
Return a space-separated list of distro IDs of distributions that are Return a space-separated list of distro IDs of distributions that are
closely related to the current OS distribution in regards to packaging closely related to the current OS distribution in regards to packaging
@ -433,8 +444,7 @@ def like():
return _distro.like() return _distro.like()
def codename(): def codename() -> str:
# type: () -> str
""" """
Return the codename for the release of the current OS distribution, Return the codename for the release of the current OS distribution,
as a string. as a string.
@ -458,8 +468,7 @@ def codename():
return _distro.codename() return _distro.codename()
def info(pretty=False, best=False): def info(pretty: bool = False, best: bool = False) -> InfoDict:
# type: (bool, bool) -> InfoDict
""" """
Return certain machine-readable information items about the current OS Return certain machine-readable information items about the current OS
distribution in a dictionary, as shown in the following example: distribution in a dictionary, as shown in the following example:
@ -503,8 +512,7 @@ def info(pretty=False, best=False):
return _distro.info(pretty, best) return _distro.info(pretty, best)
def os_release_info(): def os_release_info() -> Dict[str, str]:
# type: () -> Dict[str, str]
""" """
Return a dictionary containing key-value pairs for the information items Return a dictionary containing key-value pairs for the information items
from the os-release file data source of the current OS distribution. from the os-release file data source of the current OS distribution.
@ -514,8 +522,7 @@ def os_release_info():
return _distro.os_release_info() return _distro.os_release_info()
def lsb_release_info(): def lsb_release_info() -> Dict[str, str]:
# type: () -> Dict[str, str]
""" """
Return a dictionary containing key-value pairs for the information items Return a dictionary containing key-value pairs for the information items
from the lsb_release command data source of the current OS distribution. from the lsb_release command data source of the current OS distribution.
@ -526,8 +533,7 @@ def lsb_release_info():
return _distro.lsb_release_info() return _distro.lsb_release_info()
def distro_release_info(): def distro_release_info() -> Dict[str, str]:
# type: () -> Dict[str, str]
""" """
Return a dictionary containing key-value pairs for the information items Return a dictionary containing key-value pairs for the information items
from the distro release file data source of the current OS distribution. from the distro release file data source of the current OS distribution.
@ -537,8 +543,7 @@ def distro_release_info():
return _distro.distro_release_info() return _distro.distro_release_info()
def uname_info(): def uname_info() -> Dict[str, str]:
# type: () -> Dict[str, str]
""" """
Return a dictionary containing key-value pairs for the information items Return a dictionary containing key-value pairs for the information items
from the distro release file data source of the current OS distribution. from the distro release file data source of the current OS distribution.
@ -546,8 +551,7 @@ def uname_info():
return _distro.uname_info() return _distro.uname_info()
def os_release_attr(attribute): def os_release_attr(attribute: str) -> str:
# type: (str) -> str
""" """
Return a single named information item from the os-release file data source Return a single named information item from the os-release file data source
of the current OS distribution. of the current OS distribution.
@ -566,8 +570,7 @@ def os_release_attr(attribute):
return _distro.os_release_attr(attribute) return _distro.os_release_attr(attribute)
def lsb_release_attr(attribute): def lsb_release_attr(attribute: str) -> str:
# type: (str) -> str
""" """
Return a single named information item from the lsb_release command output Return a single named information item from the lsb_release command output
data source of the current OS distribution. data source of the current OS distribution.
@ -587,8 +590,7 @@ def lsb_release_attr(attribute):
return _distro.lsb_release_attr(attribute) return _distro.lsb_release_attr(attribute)
def distro_release_attr(attribute): def distro_release_attr(attribute: str) -> str:
# type: (str) -> str
""" """
Return a single named information item from the distro release file Return a single named information item from the distro release file
data source of the current OS distribution. data source of the current OS distribution.
@ -607,8 +609,7 @@ def distro_release_attr(attribute):
return _distro.distro_release_attr(attribute) return _distro.distro_release_attr(attribute)
def uname_attr(attribute): def uname_attr(attribute: str) -> str:
# type: (str) -> str
""" """
Return a single named information item from the distro release file Return a single named information item from the distro release file
data source of the current OS distribution. data source of the current OS distribution.
@ -629,25 +630,23 @@ try:
from functools import cached_property from functools import cached_property
except ImportError: except ImportError:
# Python < 3.8 # Python < 3.8
class cached_property(object): # type: ignore class cached_property: # type: ignore
"""A version of @property which caches the value. On access, it calls the """A version of @property which caches the value. On access, it calls the
underlying function and sets the value in `__dict__` so future accesses underlying function and sets the value in `__dict__` so future accesses
will not re-call the property. will not re-call the property.
""" """
def __init__(self, f): def __init__(self, f: Callable[[Any], Any]) -> None:
# type: (Callable[[Any], Any]) -> None
self._fname = f.__name__ self._fname = f.__name__
self._f = f self._f = f
def __get__(self, obj, owner): def __get__(self, obj: Any, owner: Type[Any]) -> Any:
# type: (Any, Type[Any]) -> Any assert obj is not None, f"call {self._fname} on an instance"
assert obj is not None, "call {} on an instance".format(self._fname)
ret = obj.__dict__[self._fname] = self._f(obj) ret = obj.__dict__[self._fname] = self._f(obj)
return ret return ret
class LinuxDistribution(object): class LinuxDistribution:
""" """
Provides information about a OS distribution. Provides information about a OS distribution.
@ -667,13 +666,13 @@ class LinuxDistribution(object):
def __init__( def __init__(
self, self,
include_lsb=True, include_lsb: Optional[bool] = None,
os_release_file="", os_release_file: str = "",
distro_release_file="", distro_release_file: str = "",
include_uname=True, include_uname: Optional[bool] = None,
root_dir=None, root_dir: Optional[str] = None,
): include_oslevel: Optional[bool] = None,
# type: (bool, str, str, bool, Optional[str]) -> None ) -> None:
""" """
The initialization method of this class gathers information from the The initialization method of this class gathers information from the
available data sources, and stores that in private instance attributes. available data sources, and stores that in private instance attributes.
@ -713,7 +712,13 @@ class LinuxDistribution(object):
be empty. be empty.
* ``root_dir`` (string): The absolute path to the root directory to use * ``root_dir`` (string): The absolute path to the root directory to use
to find distro-related information files. to find distro-related information files. Note that ``include_*``
parameters must not be enabled in combination with ``root_dir``.
* ``include_oslevel`` (bool): Controls whether (AIX) oslevel command
output is included as a data source. If the oslevel command is not
available in the program execution path the data source will be
empty.
Public instance attributes: Public instance attributes:
@ -732,9 +737,20 @@ class LinuxDistribution(object):
parameter. This controls whether the uname information will parameter. This controls whether the uname information will
be loaded. be loaded.
* ``include_oslevel`` (bool): The result of the ``include_oslevel``
parameter. This controls whether (AIX) oslevel information will be
loaded.
* ``root_dir`` (string): The result of the ``root_dir`` parameter.
The absolute path to the root directory to use to find distro-related
information files.
Raises: Raises:
* :py:exc:`IOError`: Some I/O issue with an os-release file or distro * :py:exc:`ValueError`: Initialization parameters combination is not
supported.
* :py:exc:`OSError`: Some I/O issue with an os-release file or distro
release file. release file.
* :py:exc:`UnicodeError`: A data source has unexpected characters or * :py:exc:`UnicodeError`: A data source has unexpected characters or
@ -764,11 +780,24 @@ class LinuxDistribution(object):
self.os_release_file = usr_lib_os_release_file self.os_release_file = usr_lib_os_release_file
self.distro_release_file = distro_release_file or "" # updated later self.distro_release_file = distro_release_file or "" # updated later
self.include_lsb = include_lsb
self.include_uname = include_uname
def __repr__(self): is_root_dir_defined = root_dir is not None
# type: () -> str if is_root_dir_defined and (include_lsb or include_uname or include_oslevel):
raise ValueError(
"Including subprocess data sources from specific root_dir is disallowed"
" to prevent false information"
)
self.include_lsb = (
include_lsb if include_lsb is not None else not is_root_dir_defined
)
self.include_uname = (
include_uname if include_uname is not None else not is_root_dir_defined
)
self.include_oslevel = (
include_oslevel if include_oslevel is not None else not is_root_dir_defined
)
def __repr__(self) -> str:
"""Return repr of all info""" """Return repr of all info"""
return ( return (
"LinuxDistribution(" "LinuxDistribution("
@ -776,14 +805,18 @@ class LinuxDistribution(object):
"distro_release_file={self.distro_release_file!r}, " "distro_release_file={self.distro_release_file!r}, "
"include_lsb={self.include_lsb!r}, " "include_lsb={self.include_lsb!r}, "
"include_uname={self.include_uname!r}, " "include_uname={self.include_uname!r}, "
"include_oslevel={self.include_oslevel!r}, "
"root_dir={self.root_dir!r}, "
"_os_release_info={self._os_release_info!r}, " "_os_release_info={self._os_release_info!r}, "
"_lsb_release_info={self._lsb_release_info!r}, " "_lsb_release_info={self._lsb_release_info!r}, "
"_distro_release_info={self._distro_release_info!r}, " "_distro_release_info={self._distro_release_info!r}, "
"_uname_info={self._uname_info!r})".format(self=self) "_uname_info={self._uname_info!r}, "
"_oslevel_info={self._oslevel_info!r})".format(self=self)
) )
def linux_distribution(self, full_distribution_name=True): def linux_distribution(
# type: (bool) -> Tuple[str, str, str] self, full_distribution_name: bool = True
) -> Tuple[str, str, str]:
""" """
Return information about the OS distribution that is compatible Return information about the OS distribution that is compatible
with Python's :func:`platform.linux_distribution`, supporting a subset with Python's :func:`platform.linux_distribution`, supporting a subset
@ -797,15 +830,13 @@ class LinuxDistribution(object):
self._os_release_info.get("release_codename") or self.codename(), self._os_release_info.get("release_codename") or self.codename(),
) )
def id(self): def id(self) -> str:
# type: () -> str
"""Return the distro ID of the OS distribution, as a string. """Return the distro ID of the OS distribution, as a string.
For details, see :func:`distro.id`. For details, see :func:`distro.id`.
""" """
def normalize(distro_id, table): def normalize(distro_id: str, table: Dict[str, str]) -> str:
# type: (str, Dict[str, str]) -> str
distro_id = distro_id.lower().replace(" ", "_") distro_id = distro_id.lower().replace(" ", "_")
return table.get(distro_id, distro_id) return table.get(distro_id, distro_id)
@ -827,8 +858,7 @@ class LinuxDistribution(object):
return "" return ""
def name(self, pretty=False): def name(self, pretty: bool = False) -> str:
# type: (bool) -> str
""" """
Return the name of the OS distribution, as a string. Return the name of the OS distribution, as a string.
@ -848,11 +878,10 @@ class LinuxDistribution(object):
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) version = self.version(pretty=True)
if version: if version:
name = name + " " + version name = f"{name} {version}"
return name or "" return name or ""
def version(self, pretty=False, best=False): def version(self, pretty: bool = False, best: bool = False) -> str:
# type: (bool, bool) -> str
""" """
Return the version of the OS distribution, as a string. Return the version of the OS distribution, as a string.
@ -870,7 +899,10 @@ class LinuxDistribution(object):
).get("version_id", ""), ).get("version_id", ""),
self.uname_attr("release"), self.uname_attr("release"),
] ]
if self.id() == "debian" or "debian" in self.like().split(): if self.uname_attr("id").startswith("aix"):
# On AIX platforms, prefer oslevel command output.
versions.insert(0, self.oslevel_info())
elif self.id() == "debian" or "debian" in self.like().split():
# On Debian-like, add debian_version file content to candidates list. # On Debian-like, add debian_version file content to candidates list.
versions.append(self._debian_version) versions.append(self._debian_version)
version = "" version = ""
@ -888,11 +920,10 @@ class LinuxDistribution(object):
version = v version = v
break break
if pretty and version and self.codename(): if pretty and version and self.codename():
version = "{0} ({1})".format(version, self.codename()) version = f"{version} ({self.codename()})"
return version return version
def version_parts(self, best=False): def version_parts(self, best: bool = False) -> Tuple[str, str, str]:
# type: (bool) -> Tuple[str, str, str]
""" """
Return the version of the OS distribution, as a tuple of version Return the version of the OS distribution, as a tuple of version
numbers. numbers.
@ -908,8 +939,7 @@ class LinuxDistribution(object):
return major, minor or "", build_number or "" return major, minor or "", build_number or ""
return "", "", "" return "", "", ""
def major_version(self, best=False): def major_version(self, best: bool = False) -> str:
# type: (bool) -> str
""" """
Return the major version number of the current distribution. Return the major version number of the current distribution.
@ -917,8 +947,7 @@ class LinuxDistribution(object):
""" """
return self.version_parts(best)[0] return self.version_parts(best)[0]
def minor_version(self, best=False): def minor_version(self, best: bool = False) -> str:
# type: (bool) -> str
""" """
Return the minor version number of the current distribution. Return the minor version number of the current distribution.
@ -926,8 +955,7 @@ class LinuxDistribution(object):
""" """
return self.version_parts(best)[1] return self.version_parts(best)[1]
def build_number(self, best=False): def build_number(self, best: bool = False) -> str:
# type: (bool) -> str
""" """
Return the build number of the current distribution. Return the build number of the current distribution.
@ -935,8 +963,7 @@ class LinuxDistribution(object):
""" """
return self.version_parts(best)[2] return self.version_parts(best)[2]
def like(self): def like(self) -> str:
# type: () -> str
""" """
Return the IDs of distributions that are like the OS distribution. Return the IDs of distributions that are like the OS distribution.
@ -944,8 +971,7 @@ class LinuxDistribution(object):
""" """
return self.os_release_attr("id_like") or "" return self.os_release_attr("id_like") or ""
def codename(self): def codename(self) -> str:
# type: () -> str
""" """
Return the codename of the OS distribution. Return the codename of the OS distribution.
@ -962,8 +988,7 @@ class LinuxDistribution(object):
or "" or ""
) )
def info(self, pretty=False, best=False): def info(self, pretty: bool = False, best: bool = False) -> InfoDict:
# type: (bool, bool) -> InfoDict
""" """
Return certain machine-readable information about the OS Return certain machine-readable information about the OS
distribution. distribution.
@ -982,8 +1007,7 @@ class LinuxDistribution(object):
codename=self.codename(), codename=self.codename(),
) )
def os_release_info(self): def os_release_info(self) -> Dict[str, str]:
# type: () -> Dict[str, str]
""" """
Return a dictionary containing key-value pairs for the information Return a dictionary containing key-value pairs for the information
items from the os-release file data source of the OS distribution. items from the os-release file data source of the OS distribution.
@ -992,8 +1016,7 @@ class LinuxDistribution(object):
""" """
return self._os_release_info return self._os_release_info
def lsb_release_info(self): def lsb_release_info(self) -> Dict[str, str]:
# type: () -> Dict[str, str]
""" """
Return a dictionary containing key-value pairs for the information Return a dictionary containing key-value pairs for the information
items from the lsb_release command data source of the OS items from the lsb_release command data source of the OS
@ -1003,8 +1026,7 @@ class LinuxDistribution(object):
""" """
return self._lsb_release_info return self._lsb_release_info
def distro_release_info(self): def distro_release_info(self) -> Dict[str, str]:
# type: () -> Dict[str, str]
""" """
Return a dictionary containing key-value pairs for the information Return a dictionary containing key-value pairs for the information
items from the distro release file data source of the OS items from the distro release file data source of the OS
@ -1014,8 +1036,7 @@ class LinuxDistribution(object):
""" """
return self._distro_release_info return self._distro_release_info
def uname_info(self): def uname_info(self) -> Dict[str, str]:
# type: () -> Dict[str, str]
""" """
Return a dictionary containing key-value pairs for the information Return a dictionary containing key-value pairs for the information
items from the uname command data source of the OS distribution. items from the uname command data source of the OS distribution.
@ -1024,8 +1045,13 @@ class LinuxDistribution(object):
""" """
return self._uname_info return self._uname_info
def os_release_attr(self, attribute): def oslevel_info(self) -> str:
# type: (str) -> str """
Return AIX' oslevel command output.
"""
return self._oslevel_info
def os_release_attr(self, attribute: str) -> str:
""" """
Return a single named information item from the os-release file data Return a single named information item from the os-release file data
source of the OS distribution. source of the OS distribution.
@ -1034,8 +1060,7 @@ class LinuxDistribution(object):
""" """
return self._os_release_info.get(attribute, "") return self._os_release_info.get(attribute, "")
def lsb_release_attr(self, attribute): def lsb_release_attr(self, attribute: str) -> str:
# type: (str) -> str
""" """
Return a single named information item from the lsb_release command Return a single named information item from the lsb_release command
output data source of the OS distribution. output data source of the OS distribution.
@ -1044,8 +1069,7 @@ class LinuxDistribution(object):
""" """
return self._lsb_release_info.get(attribute, "") return self._lsb_release_info.get(attribute, "")
def distro_release_attr(self, attribute): def distro_release_attr(self, attribute: str) -> str:
# type: (str) -> str
""" """
Return a single named information item from the distro release file Return a single named information item from the distro release file
data source of the OS distribution. data source of the OS distribution.
@ -1054,8 +1078,7 @@ class LinuxDistribution(object):
""" """
return self._distro_release_info.get(attribute, "") return self._distro_release_info.get(attribute, "")
def uname_attr(self, attribute): def uname_attr(self, attribute: str) -> str:
# type: (str) -> str
""" """
Return a single named information item from the uname command Return a single named information item from the uname command
output data source of the OS distribution. output data source of the OS distribution.
@ -1065,8 +1088,7 @@ class LinuxDistribution(object):
return self._uname_info.get(attribute, "") return self._uname_info.get(attribute, "")
@cached_property @cached_property
def _os_release_info(self): def _os_release_info(self) -> Dict[str, str]:
# type: () -> Dict[str, str]
""" """
Get the information items from the specified os-release file. Get the information items from the specified os-release file.
@ -1074,13 +1096,12 @@ class LinuxDistribution(object):
A dictionary containing all information items. A dictionary containing all information items.
""" """
if os.path.isfile(self.os_release_file): if os.path.isfile(self.os_release_file):
with open(self.os_release_file) as release_file: with open(self.os_release_file, encoding="utf-8") as release_file:
return self._parse_os_release_content(release_file) return self._parse_os_release_content(release_file)
return {} return {}
@staticmethod @staticmethod
def _parse_os_release_content(lines): def _parse_os_release_content(lines: TextIO) -> Dict[str, str]:
# type: (TextIO) -> Dict[str, str]
""" """
Parse the lines of an os-release file. Parse the lines of an os-release file.
@ -1097,16 +1118,6 @@ class LinuxDistribution(object):
lexer = shlex.shlex(lines, posix=True) lexer = shlex.shlex(lines, posix=True)
lexer.whitespace_split = True lexer.whitespace_split = True
# The shlex module defines its `wordchars` variable using literals,
# making it dependent on the encoding of the Python source file.
# In Python 2.6 and 2.7, the shlex source file is encoded in
# 'iso-8859-1', and the `wordchars` variable is defined as a byte
# string. This causes a UnicodeDecodeError to be raised when the
# 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")
tokens = list(lexer) tokens = list(lexer)
for token in tokens: for token in tokens:
# At this point, all shell-like parsing has been done (i.e. # At this point, all shell-like parsing has been done (i.e.
@ -1140,8 +1151,7 @@ class LinuxDistribution(object):
return props return props
@cached_property @cached_property
def _lsb_release_info(self): def _lsb_release_info(self) -> Dict[str, str]:
# type: () -> Dict[str, str]
""" """
Get the information items from the lsb_release command output. Get the information items from the lsb_release command output.
@ -1150,19 +1160,17 @@ class LinuxDistribution(object):
""" """
if not self.include_lsb: if not self.include_lsb:
return {} return {}
with open(os.devnull, "wb") as devnull: try:
try: cmd = ("lsb_release", "-a")
cmd = ("lsb_release", "-a") stdout = subprocess.check_output(cmd, stderr=subprocess.DEVNULL)
stdout = subprocess.check_output(cmd, stderr=devnull) # Command not found or lsb_release returned error
# Command not found or lsb_release returned error except (OSError, subprocess.CalledProcessError):
except (OSError, subprocess.CalledProcessError): return {}
return {}
content = self._to_str(stdout).splitlines() content = self._to_str(stdout).splitlines()
return self._parse_lsb_release_content(content) return self._parse_lsb_release_content(content)
@staticmethod @staticmethod
def _parse_lsb_release_content(lines): def _parse_lsb_release_content(lines: Iterable[str]) -> Dict[str, str]:
# type: (Iterable[str]) -> Dict[str, str]
""" """
Parse the output of the lsb_release command. Parse the output of the lsb_release command.
@ -1186,31 +1194,39 @@ class LinuxDistribution(object):
return props return props
@cached_property @cached_property
def _uname_info(self): def _uname_info(self) -> Dict[str, str]:
# type: () -> Dict[str, str]
if not self.include_uname: if not self.include_uname:
return {} return {}
with open(os.devnull, "wb") as devnull: try:
try: cmd = ("uname", "-rs")
cmd = ("uname", "-rs") stdout = subprocess.check_output(cmd, stderr=subprocess.DEVNULL)
stdout = subprocess.check_output(cmd, stderr=devnull) except OSError:
except OSError: return {}
return {}
content = self._to_str(stdout).splitlines() content = self._to_str(stdout).splitlines()
return self._parse_uname_content(content) return self._parse_uname_content(content)
@cached_property @cached_property
def _debian_version(self): def _oslevel_info(self) -> str:
# type: () -> str if not self.include_oslevel:
return ""
try:
stdout = subprocess.check_output("oslevel", stderr=subprocess.DEVNULL)
except (OSError, subprocess.CalledProcessError):
return ""
return self._to_str(stdout).strip()
@cached_property
def _debian_version(self) -> str:
try: try:
with open(os.path.join(self.etc_dir, "debian_version")) as fp: with open(
os.path.join(self.etc_dir, "debian_version"), encoding="ascii"
) as fp:
return fp.readline().rstrip() return fp.readline().rstrip()
except (OSError, IOError): except FileNotFoundError:
return "" return ""
@staticmethod @staticmethod
def _parse_uname_content(lines): def _parse_uname_content(lines: Sequence[str]) -> Dict[str, str]:
# type: (Sequence[str]) -> Dict[str, str]
if not lines: if not lines:
return {} return {}
props = {} props = {}
@ -1229,23 +1245,12 @@ class LinuxDistribution(object):
return props return props
@staticmethod @staticmethod
def _to_str(text): def _to_str(bytestring: bytes) -> str:
# type: (Union[bytes, str]) -> str
encoding = sys.getfilesystemencoding() encoding = sys.getfilesystemencoding()
encoding = "utf-8" if encoding == "ascii" else encoding return bytestring.decode(encoding)
if sys.version_info[0] >= 3:
if isinstance(text, bytes):
return text.decode(encoding)
else:
if isinstance(text, unicode): # noqa
return text.encode(encoding)
return text
@cached_property @cached_property
def _distro_release_info(self): def _distro_release_info(self) -> Dict[str, str]:
# type: () -> Dict[str, str]
""" """
Get the information items from the specified distro release file. Get the information items from the specified distro release file.
@ -1262,14 +1267,14 @@ class LinuxDistribution(object):
# file), because we want to use what was specified as best as # file), because we want to use what was specified as best as
# possible. # possible.
match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename) match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename)
if "name" in distro_info and "cloudlinux" in distro_info["name"].lower():
distro_info["id"] = "cloudlinux"
elif match:
distro_info["id"] = match.group(1)
return distro_info
else: else:
try: try:
basenames = os.listdir(self.etc_dir) basenames = [
basename
for basename in os.listdir(self.etc_dir)
if basename not in _DISTRO_RELEASE_IGNORE_BASENAMES
and os.path.isfile(os.path.join(self.etc_dir, basename))
]
# We sort for repeatability in cases where there are multiple # We sort for repeatability in cases where there are multiple
# distro specific files; e.g. CentOS, Oracle, Enterprise all # distro specific files; e.g. CentOS, Oracle, Enterprise all
# containing `redhat-release` on top of their own. # containing `redhat-release` on top of their own.
@ -1279,42 +1284,31 @@ class LinuxDistribution(object):
# sure about the *-release files. Check common entries of # sure about the *-release files. Check common entries of
# /etc for information. If they turn out to not be there the # /etc for information. If they turn out to not be there the
# error is handled in `_parse_distro_release_file()`. # error is handled in `_parse_distro_release_file()`.
basenames = [ basenames = _DISTRO_RELEASE_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",
"rocky-release",
"sl-release",
"slackware-version",
]
for basename in basenames: for basename in basenames:
if basename in _DISTRO_RELEASE_IGNORE_BASENAMES:
continue
match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename) match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename)
if match: if match is None:
filepath = os.path.join(self.etc_dir, basename) continue
distro_info = self._parse_distro_release_file(filepath) filepath = os.path.join(self.etc_dir, basename)
if "name" in distro_info: distro_info = self._parse_distro_release_file(filepath)
# The name is always present if the pattern matches # The name is always present if the pattern matches.
self.distro_release_file = filepath if "name" not in distro_info:
distro_info["id"] = match.group(1) continue
if "cloudlinux" in distro_info["name"].lower(): self.distro_release_file = filepath
distro_info["id"] = "cloudlinux" break
return distro_info else: # the loop didn't "break": no candidate.
return {} return {}
if match is not None:
distro_info["id"] = match.group(1)
# CloudLinux < 7: manually enrich info with proper id.
if "cloudlinux" in distro_info.get("name", "").lower():
distro_info["id"] = "cloudlinux"
return distro_info
def _parse_distro_release_file(self, filepath): def _parse_distro_release_file(self, filepath: str) -> Dict[str, str]:
# type: (str) -> Dict[str, str]
""" """
Parse a distro release file. Parse a distro release file.
@ -1326,19 +1320,18 @@ class LinuxDistribution(object):
A dictionary containing all information items. A dictionary containing all information items.
""" """
try: try:
with open(filepath) as fp: with open(filepath, encoding="utf-8") as fp:
# Only parse the first line. For instance, on SLES there # Only parse the first line. For instance, on SLES there
# are multiple lines. We don't want them... # are multiple lines. We don't want them...
return self._parse_distro_release_content(fp.readline()) return self._parse_distro_release_content(fp.readline())
except (OSError, IOError): except OSError:
# Ignore not being able to read a specific, seemingly version # Ignore not being able to read a specific, seemingly version
# related file. # related file.
# See https://github.com/python-distro/distro/issues/162 # See https://github.com/python-distro/distro/issues/162
return {} return {}
@staticmethod @staticmethod
def _parse_distro_release_content(line): def _parse_distro_release_content(line: str) -> Dict[str, str]:
# type: (str) -> Dict[str, str]
""" """
Parse a line from a distro release file. Parse a line from a distro release file.
@ -1366,8 +1359,7 @@ class LinuxDistribution(object):
_distro = LinuxDistribution() _distro = LinuxDistribution()
def main(): def main() -> None:
# type: () -> None
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG) logger.setLevel(logging.DEBUG)
logger.addHandler(logging.StreamHandler(sys.stdout)) logger.addHandler(logging.StreamHandler(sys.stdout))
@ -1389,7 +1381,10 @@ def main():
if args.root_dir: if args.root_dir:
dist = LinuxDistribution( dist = LinuxDistribution(
include_lsb=False, include_uname=False, root_dir=args.root_dir include_lsb=False,
include_uname=False,
include_oslevel=False,
root_dir=args.root_dir,
) )
else: else:
dist = _distro dist = _distro

@ -58,8 +58,6 @@ lib/ansible/module_utils/compat/selinux.py import-3.12!skip # pass/fail depends
lib/ansible/module_utils/compat/selinux.py pylint:unidiomatic-typecheck lib/ansible/module_utils/compat/selinux.py pylint:unidiomatic-typecheck
lib/ansible/module_utils/distro/_distro.py no-assert lib/ansible/module_utils/distro/_distro.py no-assert
lib/ansible/module_utils/distro/_distro.py pep8!skip # 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: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/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/__init__.py empty-init # breaks namespacing, deprecate and eventually remove
lib/ansible/module_utils/powershell/Ansible.ModuleUtils.ArgvParser.psm1 pslint:PSUseApprovedVerbs lib/ansible/module_utils/powershell/Ansible.ModuleUtils.ArgvParser.psm1 pslint:PSUseApprovedVerbs

Loading…
Cancel
Save