|
|
|
@ -41,40 +41,39 @@ import shlex
|
|
|
|
|
import subprocess
|
|
|
|
|
import sys
|
|
|
|
|
import warnings
|
|
|
|
|
from typing import (
|
|
|
|
|
Any,
|
|
|
|
|
Callable,
|
|
|
|
|
Dict,
|
|
|
|
|
Iterable,
|
|
|
|
|
Optional,
|
|
|
|
|
Sequence,
|
|
|
|
|
TextIO,
|
|
|
|
|
Tuple,
|
|
|
|
|
Type,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
__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,
|
|
|
|
|
)
|
|
|
|
|
try:
|
|
|
|
|
from typing import TypedDict
|
|
|
|
|
except ImportError:
|
|
|
|
|
# Python 3.7
|
|
|
|
|
TypedDict = dict
|
|
|
|
|
|
|
|
|
|
VersionDict = TypedDict(
|
|
|
|
|
"VersionDict", {"major": str, "minor": str, "build_number": str}
|
|
|
|
|
)
|
|
|
|
|
InfoDict = TypedDict(
|
|
|
|
|
"InfoDict",
|
|
|
|
|
{
|
|
|
|
|
"id": str,
|
|
|
|
|
"version": str,
|
|
|
|
|
"version_parts": VersionDict,
|
|
|
|
|
"like": str,
|
|
|
|
|
"codename": str,
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
__version__ = "1.8.0"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VersionDict(TypedDict):
|
|
|
|
|
major: str
|
|
|
|
|
minor: str
|
|
|
|
|
build_number: str
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class InfoDict(TypedDict):
|
|
|
|
|
id: str
|
|
|
|
|
version: str
|
|
|
|
|
version_parts: VersionDict
|
|
|
|
|
like: str
|
|
|
|
|
codename: str
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_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
|
|
|
|
|
_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
|
|
|
|
|
_DISTRO_RELEASE_IGNORE_BASENAMES = (
|
|
|
|
|
"debian_version",
|
|
|
|
@ -139,8 +158,7 @@ _DISTRO_RELEASE_IGNORE_BASENAMES = (
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def linux_distribution(full_distribution_name=True):
|
|
|
|
|
# type: (bool) -> Tuple[str, str, str]
|
|
|
|
|
def linux_distribution(full_distribution_name: bool = True) -> Tuple[str, str, str]:
|
|
|
|
|
"""
|
|
|
|
|
.. deprecated:: 1.6.0
|
|
|
|
|
|
|
|
|
@ -183,8 +201,7 @@ def linux_distribution(full_distribution_name=True):
|
|
|
|
|
return _distro.linux_distribution(full_distribution_name)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def id():
|
|
|
|
|
# type: () -> str
|
|
|
|
|
def id() -> str:
|
|
|
|
|
"""
|
|
|
|
|
Return the distro ID of the current distribution, as a
|
|
|
|
|
machine-readable string.
|
|
|
|
@ -228,6 +245,7 @@ def id():
|
|
|
|
|
"freebsd" FreeBSD
|
|
|
|
|
"midnightbsd" MidnightBSD
|
|
|
|
|
"rocky" Rocky Linux
|
|
|
|
|
"aix" AIX
|
|
|
|
|
"guix" Guix System
|
|
|
|
|
============== =========================================
|
|
|
|
|
|
|
|
|
@ -266,8 +284,7 @@ def id():
|
|
|
|
|
return _distro.id()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def name(pretty=False):
|
|
|
|
|
# type: (bool) -> str
|
|
|
|
|
def name(pretty: bool = False) -> str:
|
|
|
|
|
"""
|
|
|
|
|
Return the name of the current OS distribution, as a human-readable
|
|
|
|
|
string.
|
|
|
|
@ -306,8 +323,7 @@ def name(pretty=False):
|
|
|
|
|
return _distro.name(pretty)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def version(pretty=False, best=False):
|
|
|
|
|
# type: (bool, bool) -> str
|
|
|
|
|
def version(pretty: bool = False, best: bool = False) -> str:
|
|
|
|
|
"""
|
|
|
|
|
Return the version of the current OS distribution, as a human-readable
|
|
|
|
|
string.
|
|
|
|
@ -355,8 +371,7 @@ def version(pretty=False, best=False):
|
|
|
|
|
return _distro.version(pretty, best)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def version_parts(best=False):
|
|
|
|
|
# type: (bool) -> Tuple[str, str, str]
|
|
|
|
|
def version_parts(best: bool = False) -> Tuple[str, str, str]:
|
|
|
|
|
"""
|
|
|
|
|
Return the version of the current OS distribution as a tuple
|
|
|
|
|
``(major, minor, build_number)`` with items as follows:
|
|
|
|
@ -373,8 +388,7 @@ def version_parts(best=False):
|
|
|
|
|
return _distro.version_parts(best)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def major_version(best=False):
|
|
|
|
|
# type: (bool) -> str
|
|
|
|
|
def major_version(best: bool = False) -> str:
|
|
|
|
|
"""
|
|
|
|
|
Return the major version of the current OS distribution, as a string,
|
|
|
|
|
if provided.
|
|
|
|
@ -387,8 +401,7 @@ def major_version(best=False):
|
|
|
|
|
return _distro.major_version(best)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def minor_version(best=False):
|
|
|
|
|
# type: (bool) -> str
|
|
|
|
|
def minor_version(best: bool = False) -> str:
|
|
|
|
|
"""
|
|
|
|
|
Return the minor version of the current OS distribution, as a string,
|
|
|
|
|
if provided.
|
|
|
|
@ -401,8 +414,7 @@ def minor_version(best=False):
|
|
|
|
|
return _distro.minor_version(best)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def build_number(best=False):
|
|
|
|
|
# type: (bool) -> str
|
|
|
|
|
def build_number(best: bool = False) -> str:
|
|
|
|
|
"""
|
|
|
|
|
Return the build number of the current OS distribution, as a string,
|
|
|
|
|
if provided.
|
|
|
|
@ -415,8 +427,7 @@ def build_number(best=False):
|
|
|
|
|
return _distro.build_number(best)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def like():
|
|
|
|
|
# type: () -> str
|
|
|
|
|
def like() -> str:
|
|
|
|
|
"""
|
|
|
|
|
Return a space-separated list of distro IDs of distributions that are
|
|
|
|
|
closely related to the current OS distribution in regards to packaging
|
|
|
|
@ -433,8 +444,7 @@ def like():
|
|
|
|
|
return _distro.like()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def codename():
|
|
|
|
|
# type: () -> str
|
|
|
|
|
def codename() -> str:
|
|
|
|
|
"""
|
|
|
|
|
Return the codename for the release of the current OS distribution,
|
|
|
|
|
as a string.
|
|
|
|
@ -458,8 +468,7 @@ def codename():
|
|
|
|
|
return _distro.codename()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def info(pretty=False, best=False):
|
|
|
|
|
# type: (bool, bool) -> InfoDict
|
|
|
|
|
def info(pretty: bool = False, best: bool = False) -> InfoDict:
|
|
|
|
|
"""
|
|
|
|
|
Return certain machine-readable information items about the current OS
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def os_release_info():
|
|
|
|
|
# type: () -> Dict[str, str]
|
|
|
|
|
def os_release_info() -> 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.
|
|
|
|
@ -514,8 +522,7 @@ def os_release_info():
|
|
|
|
|
return _distro.os_release_info()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def lsb_release_info():
|
|
|
|
|
# type: () -> Dict[str, str]
|
|
|
|
|
def lsb_release_info() -> 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.
|
|
|
|
@ -526,8 +533,7 @@ def lsb_release_info():
|
|
|
|
|
return _distro.lsb_release_info()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def distro_release_info():
|
|
|
|
|
# type: () -> Dict[str, str]
|
|
|
|
|
def distro_release_info() -> 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.
|
|
|
|
@ -537,8 +543,7 @@ def distro_release_info():
|
|
|
|
|
return _distro.distro_release_info()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def uname_info():
|
|
|
|
|
# type: () -> Dict[str, str]
|
|
|
|
|
def uname_info() -> 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.
|
|
|
|
@ -546,8 +551,7 @@ def uname_info():
|
|
|
|
|
return _distro.uname_info()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def os_release_attr(attribute):
|
|
|
|
|
# type: (str) -> str
|
|
|
|
|
def os_release_attr(attribute: str) -> str:
|
|
|
|
|
"""
|
|
|
|
|
Return a single named information item from the os-release file data source
|
|
|
|
|
of the current OS distribution.
|
|
|
|
@ -566,8 +570,7 @@ def os_release_attr(attribute):
|
|
|
|
|
return _distro.os_release_attr(attribute)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def lsb_release_attr(attribute):
|
|
|
|
|
# type: (str) -> str
|
|
|
|
|
def lsb_release_attr(attribute: str) -> str:
|
|
|
|
|
"""
|
|
|
|
|
Return a single named information item from the lsb_release command output
|
|
|
|
|
data source of the current OS distribution.
|
|
|
|
@ -587,8 +590,7 @@ def lsb_release_attr(attribute):
|
|
|
|
|
return _distro.lsb_release_attr(attribute)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def distro_release_attr(attribute):
|
|
|
|
|
# type: (str) -> str
|
|
|
|
|
def distro_release_attr(attribute: str) -> str:
|
|
|
|
|
"""
|
|
|
|
|
Return a single named information item from the distro release file
|
|
|
|
|
data source of the current OS distribution.
|
|
|
|
@ -607,8 +609,7 @@ def distro_release_attr(attribute):
|
|
|
|
|
return _distro.distro_release_attr(attribute)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def uname_attr(attribute):
|
|
|
|
|
# type: (str) -> str
|
|
|
|
|
def uname_attr(attribute: str) -> str:
|
|
|
|
|
"""
|
|
|
|
|
Return a single named information item from the distro release file
|
|
|
|
|
data source of the current OS distribution.
|
|
|
|
@ -629,25 +630,23 @@ try:
|
|
|
|
|
from functools import cached_property
|
|
|
|
|
except ImportError:
|
|
|
|
|
# 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
|
|
|
|
|
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
|
|
|
|
|
def __init__(self, f: Callable[[Any], Any]) -> None:
|
|
|
|
|
self._fname = f.__name__
|
|
|
|
|
self._f = f
|
|
|
|
|
|
|
|
|
|
def __get__(self, obj, owner):
|
|
|
|
|
# type: (Any, Type[Any]) -> Any
|
|
|
|
|
assert obj is not None, "call {} on an instance".format(self._fname)
|
|
|
|
|
def __get__(self, obj: Any, owner: Type[Any]) -> Any:
|
|
|
|
|
assert obj is not None, f"call {self._fname} on an instance"
|
|
|
|
|
ret = obj.__dict__[self._fname] = self._f(obj)
|
|
|
|
|
return ret
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class LinuxDistribution(object):
|
|
|
|
|
class LinuxDistribution:
|
|
|
|
|
"""
|
|
|
|
|
Provides information about a OS distribution.
|
|
|
|
|
|
|
|
|
@ -667,13 +666,13 @@ class LinuxDistribution(object):
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
include_lsb: Optional[bool] = None,
|
|
|
|
|
os_release_file: str = "",
|
|
|
|
|
distro_release_file: str = "",
|
|
|
|
|
include_uname: Optional[bool] = None,
|
|
|
|
|
root_dir: Optional[str] = None,
|
|
|
|
|
include_oslevel: Optional[bool] = None,
|
|
|
|
|
) -> None:
|
|
|
|
|
"""
|
|
|
|
|
The initialization method of this class gathers information from the
|
|
|
|
|
available data sources, and stores that in private instance attributes.
|
|
|
|
@ -713,7 +712,13 @@ class LinuxDistribution(object):
|
|
|
|
|
be empty.
|
|
|
|
|
|
|
|
|
|
* ``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:
|
|
|
|
|
|
|
|
|
@ -732,9 +737,20 @@ class LinuxDistribution(object):
|
|
|
|
|
parameter. This controls whether the uname information will
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
* :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.
|
|
|
|
|
|
|
|
|
|
* :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.distro_release_file = distro_release_file or "" # updated later
|
|
|
|
|
self.include_lsb = include_lsb
|
|
|
|
|
self.include_uname = include_uname
|
|
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
|
# type: () -> str
|
|
|
|
|
is_root_dir_defined = root_dir is not None
|
|
|
|
|
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 (
|
|
|
|
|
"LinuxDistribution("
|
|
|
|
@ -776,14 +805,18 @@ class LinuxDistribution(object):
|
|
|
|
|
"distro_release_file={self.distro_release_file!r}, "
|
|
|
|
|
"include_lsb={self.include_lsb!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}, "
|
|
|
|
|
"_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)
|
|
|
|
|
"_uname_info={self._uname_info!r}, "
|
|
|
|
|
"_oslevel_info={self._oslevel_info!r})".format(self=self)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def linux_distribution(self, full_distribution_name=True):
|
|
|
|
|
# type: (bool) -> Tuple[str, str, str]
|
|
|
|
|
def linux_distribution(
|
|
|
|
|
self, full_distribution_name: bool = True
|
|
|
|
|
) -> Tuple[str, str, str]:
|
|
|
|
|
"""
|
|
|
|
|
Return information about the OS distribution that is compatible
|
|
|
|
|
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(),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def id(self):
|
|
|
|
|
# type: () -> str
|
|
|
|
|
def id(self) -> str:
|
|
|
|
|
"""Return the distro ID of the OS distribution, as a string.
|
|
|
|
|
|
|
|
|
|
For details, see :func:`distro.id`.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def normalize(distro_id, table):
|
|
|
|
|
# type: (str, Dict[str, str]) -> str
|
|
|
|
|
def normalize(distro_id: str, table: Dict[str, str]) -> str:
|
|
|
|
|
distro_id = distro_id.lower().replace(" ", "_")
|
|
|
|
|
return table.get(distro_id, distro_id)
|
|
|
|
|
|
|
|
|
@ -827,8 +858,7 @@ class LinuxDistribution(object):
|
|
|
|
|
|
|
|
|
|
return ""
|
|
|
|
|
|
|
|
|
|
def name(self, pretty=False):
|
|
|
|
|
# type: (bool) -> str
|
|
|
|
|
def name(self, pretty: bool = False) -> str:
|
|
|
|
|
"""
|
|
|
|
|
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")
|
|
|
|
|
version = self.version(pretty=True)
|
|
|
|
|
if version:
|
|
|
|
|
name = name + " " + version
|
|
|
|
|
name = f"{name} {version}"
|
|
|
|
|
return name or ""
|
|
|
|
|
|
|
|
|
|
def version(self, pretty=False, best=False):
|
|
|
|
|
# type: (bool, bool) -> str
|
|
|
|
|
def version(self, pretty: bool = False, best: bool = False) -> str:
|
|
|
|
|
"""
|
|
|
|
|
Return the version of the OS distribution, as a string.
|
|
|
|
|
|
|
|
|
@ -870,7 +899,10 @@ class LinuxDistribution(object):
|
|
|
|
|
).get("version_id", ""),
|
|
|
|
|
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.
|
|
|
|
|
versions.append(self._debian_version)
|
|
|
|
|
version = ""
|
|
|
|
@ -888,11 +920,10 @@ class LinuxDistribution(object):
|
|
|
|
|
version = v
|
|
|
|
|
break
|
|
|
|
|
if pretty and version and self.codename():
|
|
|
|
|
version = "{0} ({1})".format(version, self.codename())
|
|
|
|
|
version = f"{version} ({self.codename()})"
|
|
|
|
|
return version
|
|
|
|
|
|
|
|
|
|
def version_parts(self, best=False):
|
|
|
|
|
# type: (bool) -> Tuple[str, str, str]
|
|
|
|
|
def version_parts(self, best: bool = False) -> Tuple[str, str, str]:
|
|
|
|
|
"""
|
|
|
|
|
Return the version of the OS distribution, as a tuple of version
|
|
|
|
|
numbers.
|
|
|
|
@ -908,8 +939,7 @@ class LinuxDistribution(object):
|
|
|
|
|
return major, minor or "", build_number or ""
|
|
|
|
|
return "", "", ""
|
|
|
|
|
|
|
|
|
|
def major_version(self, best=False):
|
|
|
|
|
# type: (bool) -> str
|
|
|
|
|
def major_version(self, best: bool = False) -> str:
|
|
|
|
|
"""
|
|
|
|
|
Return the major version number of the current distribution.
|
|
|
|
|
|
|
|
|
@ -917,8 +947,7 @@ class LinuxDistribution(object):
|
|
|
|
|
"""
|
|
|
|
|
return self.version_parts(best)[0]
|
|
|
|
|
|
|
|
|
|
def minor_version(self, best=False):
|
|
|
|
|
# type: (bool) -> str
|
|
|
|
|
def minor_version(self, best: bool = False) -> str:
|
|
|
|
|
"""
|
|
|
|
|
Return the minor version number of the current distribution.
|
|
|
|
|
|
|
|
|
@ -926,8 +955,7 @@ class LinuxDistribution(object):
|
|
|
|
|
"""
|
|
|
|
|
return self.version_parts(best)[1]
|
|
|
|
|
|
|
|
|
|
def build_number(self, best=False):
|
|
|
|
|
# type: (bool) -> str
|
|
|
|
|
def build_number(self, best: bool = False) -> str:
|
|
|
|
|
"""
|
|
|
|
|
Return the build number of the current distribution.
|
|
|
|
|
|
|
|
|
@ -935,8 +963,7 @@ class LinuxDistribution(object):
|
|
|
|
|
"""
|
|
|
|
|
return self.version_parts(best)[2]
|
|
|
|
|
|
|
|
|
|
def like(self):
|
|
|
|
|
# type: () -> str
|
|
|
|
|
def like(self) -> str:
|
|
|
|
|
"""
|
|
|
|
|
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 ""
|
|
|
|
|
|
|
|
|
|
def codename(self):
|
|
|
|
|
# type: () -> str
|
|
|
|
|
def codename(self) -> str:
|
|
|
|
|
"""
|
|
|
|
|
Return the codename of the OS distribution.
|
|
|
|
|
|
|
|
|
@ -962,8 +988,7 @@ class LinuxDistribution(object):
|
|
|
|
|
or ""
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def info(self, pretty=False, best=False):
|
|
|
|
|
# type: (bool, bool) -> InfoDict
|
|
|
|
|
def info(self, pretty: bool = False, best: bool = False) -> InfoDict:
|
|
|
|
|
"""
|
|
|
|
|
Return certain machine-readable information about the OS
|
|
|
|
|
distribution.
|
|
|
|
@ -982,8 +1007,7 @@ class LinuxDistribution(object):
|
|
|
|
|
codename=self.codename(),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def os_release_info(self):
|
|
|
|
|
# type: () -> Dict[str, str]
|
|
|
|
|
def os_release_info(self) -> 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.
|
|
|
|
@ -992,8 +1016,7 @@ class LinuxDistribution(object):
|
|
|
|
|
"""
|
|
|
|
|
return self._os_release_info
|
|
|
|
|
|
|
|
|
|
def lsb_release_info(self):
|
|
|
|
|
# type: () -> Dict[str, str]
|
|
|
|
|
def lsb_release_info(self) -> Dict[str, str]:
|
|
|
|
|
"""
|
|
|
|
|
Return a dictionary containing key-value pairs for the information
|
|
|
|
|
items from the lsb_release command data source of the OS
|
|
|
|
@ -1003,8 +1026,7 @@ class LinuxDistribution(object):
|
|
|
|
|
"""
|
|
|
|
|
return self._lsb_release_info
|
|
|
|
|
|
|
|
|
|
def distro_release_info(self):
|
|
|
|
|
# type: () -> Dict[str, str]
|
|
|
|
|
def distro_release_info(self) -> Dict[str, str]:
|
|
|
|
|
"""
|
|
|
|
|
Return a dictionary containing key-value pairs for the information
|
|
|
|
|
items from the distro release file data source of the OS
|
|
|
|
@ -1014,8 +1036,7 @@ class LinuxDistribution(object):
|
|
|
|
|
"""
|
|
|
|
|
return self._distro_release_info
|
|
|
|
|
|
|
|
|
|
def uname_info(self):
|
|
|
|
|
# type: () -> Dict[str, str]
|
|
|
|
|
def uname_info(self) -> Dict[str, str]:
|
|
|
|
|
"""
|
|
|
|
|
Return a dictionary containing key-value pairs for the information
|
|
|
|
|
items from the uname command data source of the OS distribution.
|
|
|
|
@ -1024,8 +1045,13 @@ class LinuxDistribution(object):
|
|
|
|
|
"""
|
|
|
|
|
return self._uname_info
|
|
|
|
|
|
|
|
|
|
def os_release_attr(self, attribute):
|
|
|
|
|
# type: (str) -> str
|
|
|
|
|
def oslevel_info(self) -> 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
|
|
|
|
|
source of the OS distribution.
|
|
|
|
@ -1034,8 +1060,7 @@ class LinuxDistribution(object):
|
|
|
|
|
"""
|
|
|
|
|
return self._os_release_info.get(attribute, "")
|
|
|
|
|
|
|
|
|
|
def lsb_release_attr(self, attribute):
|
|
|
|
|
# type: (str) -> str
|
|
|
|
|
def lsb_release_attr(self, attribute: str) -> str:
|
|
|
|
|
"""
|
|
|
|
|
Return a single named information item from the lsb_release command
|
|
|
|
|
output data source of the OS distribution.
|
|
|
|
@ -1044,8 +1069,7 @@ class LinuxDistribution(object):
|
|
|
|
|
"""
|
|
|
|
|
return self._lsb_release_info.get(attribute, "")
|
|
|
|
|
|
|
|
|
|
def distro_release_attr(self, attribute):
|
|
|
|
|
# type: (str) -> str
|
|
|
|
|
def distro_release_attr(self, attribute: str) -> str:
|
|
|
|
|
"""
|
|
|
|
|
Return a single named information item from the distro release file
|
|
|
|
|
data source of the OS distribution.
|
|
|
|
@ -1054,8 +1078,7 @@ class LinuxDistribution(object):
|
|
|
|
|
"""
|
|
|
|
|
return self._distro_release_info.get(attribute, "")
|
|
|
|
|
|
|
|
|
|
def uname_attr(self, attribute):
|
|
|
|
|
# type: (str) -> str
|
|
|
|
|
def uname_attr(self, attribute: str) -> str:
|
|
|
|
|
"""
|
|
|
|
|
Return a single named information item from the uname command
|
|
|
|
|
output data source of the OS distribution.
|
|
|
|
@ -1065,8 +1088,7 @@ class LinuxDistribution(object):
|
|
|
|
|
return self._uname_info.get(attribute, "")
|
|
|
|
|
|
|
|
|
|
@cached_property
|
|
|
|
|
def _os_release_info(self):
|
|
|
|
|
# type: () -> Dict[str, str]
|
|
|
|
|
def _os_release_info(self) -> Dict[str, str]:
|
|
|
|
|
"""
|
|
|
|
|
Get the information items from the specified os-release file.
|
|
|
|
|
|
|
|
|
@ -1074,13 +1096,12 @@ class LinuxDistribution(object):
|
|
|
|
|
A dictionary containing all information items.
|
|
|
|
|
"""
|
|
|
|
|
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 {}
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def _parse_os_release_content(lines):
|
|
|
|
|
# type: (TextIO) -> Dict[str, str]
|
|
|
|
|
def _parse_os_release_content(lines: TextIO) -> Dict[str, str]:
|
|
|
|
|
"""
|
|
|
|
|
Parse the lines of an os-release file.
|
|
|
|
|
|
|
|
|
@ -1097,16 +1118,6 @@ class LinuxDistribution(object):
|
|
|
|
|
lexer = shlex.shlex(lines, posix=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)
|
|
|
|
|
for token in tokens:
|
|
|
|
|
# At this point, all shell-like parsing has been done (i.e.
|
|
|
|
@ -1140,8 +1151,7 @@ class LinuxDistribution(object):
|
|
|
|
|
return props
|
|
|
|
|
|
|
|
|
|
@cached_property
|
|
|
|
|
def _lsb_release_info(self):
|
|
|
|
|
# type: () -> Dict[str, str]
|
|
|
|
|
def _lsb_release_info(self) -> Dict[str, str]:
|
|
|
|
|
"""
|
|
|
|
|
Get the information items from the lsb_release command output.
|
|
|
|
|
|
|
|
|
@ -1150,19 +1160,17 @@ class LinuxDistribution(object):
|
|
|
|
|
"""
|
|
|
|
|
if not self.include_lsb:
|
|
|
|
|
return {}
|
|
|
|
|
with open(os.devnull, "wb") as devnull:
|
|
|
|
|
try:
|
|
|
|
|
cmd = ("lsb_release", "-a")
|
|
|
|
|
stdout = subprocess.check_output(cmd, stderr=devnull)
|
|
|
|
|
# Command not found or lsb_release returned error
|
|
|
|
|
except (OSError, subprocess.CalledProcessError):
|
|
|
|
|
return {}
|
|
|
|
|
try:
|
|
|
|
|
cmd = ("lsb_release", "-a")
|
|
|
|
|
stdout = subprocess.check_output(cmd, stderr=subprocess.DEVNULL)
|
|
|
|
|
# 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]
|
|
|
|
|
def _parse_lsb_release_content(lines: Iterable[str]) -> Dict[str, str]:
|
|
|
|
|
"""
|
|
|
|
|
Parse the output of the lsb_release command.
|
|
|
|
|
|
|
|
|
@ -1186,31 +1194,39 @@ class LinuxDistribution(object):
|
|
|
|
|
return props
|
|
|
|
|
|
|
|
|
|
@cached_property
|
|
|
|
|
def _uname_info(self):
|
|
|
|
|
# type: () -> Dict[str, str]
|
|
|
|
|
def _uname_info(self) -> Dict[str, str]:
|
|
|
|
|
if not self.include_uname:
|
|
|
|
|
return {}
|
|
|
|
|
with open(os.devnull, "wb") as devnull:
|
|
|
|
|
try:
|
|
|
|
|
cmd = ("uname", "-rs")
|
|
|
|
|
stdout = subprocess.check_output(cmd, stderr=devnull)
|
|
|
|
|
except OSError:
|
|
|
|
|
return {}
|
|
|
|
|
try:
|
|
|
|
|
cmd = ("uname", "-rs")
|
|
|
|
|
stdout = subprocess.check_output(cmd, stderr=subprocess.DEVNULL)
|
|
|
|
|
except OSError:
|
|
|
|
|
return {}
|
|
|
|
|
content = self._to_str(stdout).splitlines()
|
|
|
|
|
return self._parse_uname_content(content)
|
|
|
|
|
|
|
|
|
|
@cached_property
|
|
|
|
|
def _debian_version(self):
|
|
|
|
|
# type: () -> str
|
|
|
|
|
def _oslevel_info(self) -> 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:
|
|
|
|
|
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()
|
|
|
|
|
except (OSError, IOError):
|
|
|
|
|
except FileNotFoundError:
|
|
|
|
|
return ""
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def _parse_uname_content(lines):
|
|
|
|
|
# type: (Sequence[str]) -> Dict[str, str]
|
|
|
|
|
def _parse_uname_content(lines: Sequence[str]) -> Dict[str, str]:
|
|
|
|
|
if not lines:
|
|
|
|
|
return {}
|
|
|
|
|
props = {}
|
|
|
|
@ -1229,23 +1245,12 @@ class LinuxDistribution(object):
|
|
|
|
|
return props
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def _to_str(text):
|
|
|
|
|
# type: (Union[bytes, str]) -> str
|
|
|
|
|
def _to_str(bytestring: bytes) -> str:
|
|
|
|
|
encoding = sys.getfilesystemencoding()
|
|
|
|
|
encoding = "utf-8" if encoding == "ascii" else 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
|
|
|
|
|
return bytestring.decode(encoding)
|
|
|
|
|
|
|
|
|
|
@cached_property
|
|
|
|
|
def _distro_release_info(self):
|
|
|
|
|
# type: () -> Dict[str, str]
|
|
|
|
|
def _distro_release_info(self) -> Dict[str, str]:
|
|
|
|
|
"""
|
|
|
|
|
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
|
|
|
|
|
# possible.
|
|
|
|
|
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:
|
|
|
|
|
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
|
|
|
|
|
# distro specific files; e.g. CentOS, Oracle, Enterprise all
|
|
|
|
|
# containing `redhat-release` on top of their own.
|
|
|
|
@ -1279,42 +1284,31 @@ 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",
|
|
|
|
|
"rocky-release",
|
|
|
|
|
"sl-release",
|
|
|
|
|
"slackware-version",
|
|
|
|
|
]
|
|
|
|
|
basenames = _DISTRO_RELEASE_BASENAMES
|
|
|
|
|
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(self.etc_dir, basename)
|
|
|
|
|
distro_info = self._parse_distro_release_file(filepath)
|
|
|
|
|
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"
|
|
|
|
|
return distro_info
|
|
|
|
|
return {}
|
|
|
|
|
if match is None:
|
|
|
|
|
continue
|
|
|
|
|
filepath = os.path.join(self.etc_dir, basename)
|
|
|
|
|
distro_info = self._parse_distro_release_file(filepath)
|
|
|
|
|
# The name is always present if the pattern matches.
|
|
|
|
|
if "name" not in distro_info:
|
|
|
|
|
continue
|
|
|
|
|
self.distro_release_file = filepath
|
|
|
|
|
break
|
|
|
|
|
else: # the loop didn't "break": no candidate.
|
|
|
|
|
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):
|
|
|
|
|
# type: (str) -> Dict[str, str]
|
|
|
|
|
def _parse_distro_release_file(self, filepath: str) -> Dict[str, str]:
|
|
|
|
|
"""
|
|
|
|
|
Parse a distro release file.
|
|
|
|
|
|
|
|
|
@ -1326,19 +1320,18 @@ class LinuxDistribution(object):
|
|
|
|
|
A dictionary containing all information items.
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
with open(filepath) as fp:
|
|
|
|
|
with open(filepath, encoding="utf-8") as fp:
|
|
|
|
|
# Only parse the first line. For instance, on SLES there
|
|
|
|
|
# are multiple lines. We don't want them...
|
|
|
|
|
return self._parse_distro_release_content(fp.readline())
|
|
|
|
|
except (OSError, IOError):
|
|
|
|
|
except OSError:
|
|
|
|
|
# Ignore not being able to read a specific, seemingly version
|
|
|
|
|
# related file.
|
|
|
|
|
# See https://github.com/python-distro/distro/issues/162
|
|
|
|
|
return {}
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def _parse_distro_release_content(line):
|
|
|
|
|
# type: (str) -> Dict[str, str]
|
|
|
|
|
def _parse_distro_release_content(line: str) -> Dict[str, str]:
|
|
|
|
|
"""
|
|
|
|
|
Parse a line from a distro release file.
|
|
|
|
|
|
|
|
|
@ -1366,8 +1359,7 @@ class LinuxDistribution(object):
|
|
|
|
|
_distro = LinuxDistribution()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
|
# type: () -> None
|
|
|
|
|
def main() -> None:
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
logger.setLevel(logging.DEBUG)
|
|
|
|
|
logger.addHandler(logging.StreamHandler(sys.stdout))
|
|
|
|
@ -1389,7 +1381,10 @@ def main():
|
|
|
|
|
|
|
|
|
|
if args.root_dir:
|
|
|
|
|
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:
|
|
|
|
|
dist = _distro
|
|
|
|
|