|
|
@ -24,24 +24,26 @@ machine-readable distro ID, or version information.
|
|
|
|
It is the recommended replacement for Python's original
|
|
|
|
It is the recommended replacement for Python's original
|
|
|
|
:py:func:`platform.linux_distribution` function, but it provides much more
|
|
|
|
:py:func:`platform.linux_distribution` function, but it provides much more
|
|
|
|
functionality. An alternative implementation became necessary because Python
|
|
|
|
functionality. An alternative implementation became necessary because Python
|
|
|
|
3.5 deprecated this function, and Python 3.8 will remove it altogether.
|
|
|
|
3.5 deprecated this function, and Python 3.8 removed it altogether. Its
|
|
|
|
Its predecessor function :py:func:`platform.dist` was already
|
|
|
|
predecessor function :py:func:`platform.dist` was already deprecated since
|
|
|
|
deprecated since Python 2.6 and will also be removed in Python 3.8.
|
|
|
|
Python 2.6 and removed in Python 3.8. Still, there are many cases in which
|
|
|
|
Still, there are many cases in which access to OS distribution information
|
|
|
|
access to OS distribution information is needed. See `Python issue 1322
|
|
|
|
is needed. See `Python issue 1322 <https://bugs.python.org/issue1322>`_ for
|
|
|
|
<https://bugs.python.org/issue1322>`_ for more information.
|
|
|
|
more information.
|
|
|
|
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import logging
|
|
|
|
import os
|
|
|
|
import os
|
|
|
|
import re
|
|
|
|
import re
|
|
|
|
import sys
|
|
|
|
|
|
|
|
import shlex
|
|
|
|
import shlex
|
|
|
|
import logging
|
|
|
|
|
|
|
|
import subprocess
|
|
|
|
import subprocess
|
|
|
|
|
|
|
|
import sys
|
|
|
|
|
|
|
|
import warnings
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
__version__ = "1.6.0"
|
|
|
|
|
|
|
|
|
|
|
|
_UNIXCONFDIR = os.environ.get('UNIXCONFDIR', '/etc')
|
|
|
|
_UNIXCONFDIR = os.environ.get("UNIXCONFDIR", "/etc")
|
|
|
|
_OS_RELEASE_BASENAME = 'os-release'
|
|
|
|
_UNIXUSRLIBDIR = os.environ.get("UNIXUSRLIBDIR", "/usr/lib")
|
|
|
|
|
|
|
|
_OS_RELEASE_BASENAME = "os-release"
|
|
|
|
|
|
|
|
|
|
|
|
#: Translation table for normalizing the "ID" attribute defined in os-release
|
|
|
|
#: Translation table for normalizing the "ID" attribute defined in os-release
|
|
|
|
#: files, for use by the :func:`distro.id` method.
|
|
|
|
#: files, for use by the :func:`distro.id` method.
|
|
|
@ -51,7 +53,7 @@ _OS_RELEASE_BASENAME = 'os-release'
|
|
|
|
#:
|
|
|
|
#:
|
|
|
|
#: * Value: Normalized value.
|
|
|
|
#: * Value: Normalized value.
|
|
|
|
NORMALIZED_OS_ID = {
|
|
|
|
NORMALIZED_OS_ID = {
|
|
|
|
'ol': 'oracle', # Oracle Linux
|
|
|
|
"ol": "oracle", # Oracle Linux
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#: Translation table for normalizing the "Distributor ID" attribute returned by
|
|
|
|
#: Translation table for normalizing the "Distributor ID" attribute returned by
|
|
|
@ -62,11 +64,11 @@ NORMALIZED_OS_ID = {
|
|
|
|
#:
|
|
|
|
#:
|
|
|
|
#: * Value: Normalized value.
|
|
|
|
#: * Value: Normalized value.
|
|
|
|
NORMALIZED_LSB_ID = {
|
|
|
|
NORMALIZED_LSB_ID = {
|
|
|
|
'enterpriseenterpriseas': 'oracle', # Oracle Enterprise Linux 4
|
|
|
|
"enterpriseenterpriseas": "oracle", # Oracle Enterprise Linux 4
|
|
|
|
'enterpriseenterpriseserver': 'oracle', # Oracle Linux 5
|
|
|
|
"enterpriseenterpriseserver": "oracle", # Oracle Linux 5
|
|
|
|
'redhatenterpriseworkstation': 'rhel', # RHEL 6, 7 Workstation
|
|
|
|
"redhatenterpriseworkstation": "rhel", # RHEL 6, 7 Workstation
|
|
|
|
'redhatenterpriseserver': 'rhel', # RHEL 6, 7 Server
|
|
|
|
"redhatenterpriseserver": "rhel", # RHEL 6, 7 Server
|
|
|
|
'redhatenterprisecomputenode': 'rhel', # RHEL 6 ComputeNode
|
|
|
|
"redhatenterprisecomputenode": "rhel", # RHEL 6 ComputeNode
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#: Translation table for normalizing the distro ID derived from the file name
|
|
|
|
#: Translation table for normalizing the distro ID derived from the file name
|
|
|
@ -77,25 +79,26 @@ NORMALIZED_LSB_ID = {
|
|
|
|
#:
|
|
|
|
#:
|
|
|
|
#: * Value: Normalized value.
|
|
|
|
#: * Value: Normalized value.
|
|
|
|
NORMALIZED_DISTRO_ID = {
|
|
|
|
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)
|
|
|
|
# Pattern for content of distro release file (reversed)
|
|
|
|
_DISTRO_RELEASE_CONTENT_REVERSED_PATTERN = re.compile(
|
|
|
|
_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
|
|
|
|
# Pattern for base file name of distro release file
|
|
|
|
_DISTRO_RELEASE_BASENAME_PATTERN = re.compile(
|
|
|
|
_DISTRO_RELEASE_BASENAME_PATTERN = re.compile(r"(\w+)[-_](release|version)$")
|
|
|
|
r'(\w+)[-_](release|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",
|
|
|
|
'lsb-release',
|
|
|
|
"lsb-release",
|
|
|
|
'oem-release',
|
|
|
|
"oem-release",
|
|
|
|
_OS_RELEASE_BASENAME,
|
|
|
|
_OS_RELEASE_BASENAME,
|
|
|
|
'system-release',
|
|
|
|
"system-release",
|
|
|
|
'plesk-release',
|
|
|
|
"plesk-release",
|
|
|
|
|
|
|
|
"iredmail-release",
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -150,7 +153,15 @@ except AttributeError:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def linux_distribution(full_distribution_name=True):
|
|
|
|
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
|
|
|
|
Return information about the current OS distribution as a tuple
|
|
|
|
``(id_name, version, codename)`` with items as follows:
|
|
|
|
``(id_name, version, codename)`` with items as follows:
|
|
|
|
|
|
|
|
|
|
|
@ -174,10 +185,18 @@ def linux_distribution(full_distribution_name=True):
|
|
|
|
method normalizes the distro ID string to a reliable machine-readable value
|
|
|
|
method normalizes the distro ID string to a reliable machine-readable value
|
|
|
|
for a number of popular OS distributions.
|
|
|
|
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)
|
|
|
|
return _distro.linux_distribution(full_distribution_name)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def id():
|
|
|
|
def id():
|
|
|
|
|
|
|
|
# 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.
|
|
|
@ -257,6 +276,7 @@ def id():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def name(pretty=False):
|
|
|
|
def name(pretty=False):
|
|
|
|
|
|
|
|
# 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.
|
|
|
@ -296,6 +316,7 @@ def name(pretty=False):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def version(pretty=False, best=False):
|
|
|
|
def version(pretty=False, best=False):
|
|
|
|
|
|
|
|
# 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.
|
|
|
@ -340,6 +361,7 @@ def version(pretty=False, best=False):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def version_parts(best=False):
|
|
|
|
def version_parts(best=False):
|
|
|
|
|
|
|
|
# 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:
|
|
|
@ -357,6 +379,7 @@ def version_parts(best=False):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def major_version(best=False):
|
|
|
|
def major_version(best=False):
|
|
|
|
|
|
|
|
# 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.
|
|
|
@ -370,6 +393,7 @@ def major_version(best=False):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def minor_version(best=False):
|
|
|
|
def minor_version(best=False):
|
|
|
|
|
|
|
|
# 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.
|
|
|
@ -383,6 +407,7 @@ def minor_version(best=False):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def build_number(best=False):
|
|
|
|
def build_number(best=False):
|
|
|
|
|
|
|
|
# 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.
|
|
|
@ -396,6 +421,7 @@ def build_number(best=False):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def like():
|
|
|
|
def like():
|
|
|
|
|
|
|
|
# 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
|
|
|
@ -413,6 +439,7 @@ def like():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def codename():
|
|
|
|
def codename():
|
|
|
|
|
|
|
|
# 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.
|
|
|
@ -437,6 +464,7 @@ def codename():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def info(pretty=False, best=False):
|
|
|
|
def info(pretty=False, best=False):
|
|
|
|
|
|
|
|
# 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:
|
|
|
@ -481,6 +509,7 @@ def info(pretty=False, best=False):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def os_release_info():
|
|
|
|
def os_release_info():
|
|
|
|
|
|
|
|
# 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.
|
|
|
@ -491,6 +520,7 @@ def os_release_info():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def lsb_release_info():
|
|
|
|
def lsb_release_info():
|
|
|
|
|
|
|
|
# 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.
|
|
|
@ -502,6 +532,7 @@ def lsb_release_info():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def distro_release_info():
|
|
|
|
def distro_release_info():
|
|
|
|
|
|
|
|
# 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.
|
|
|
@ -512,6 +543,7 @@ def distro_release_info():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def uname_info():
|
|
|
|
def uname_info():
|
|
|
|
|
|
|
|
# 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.
|
|
|
@ -520,6 +552,7 @@ def uname_info():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def os_release_attr(attribute):
|
|
|
|
def os_release_attr(attribute):
|
|
|
|
|
|
|
|
# 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.
|
|
|
@ -539,6 +572,7 @@ def os_release_attr(attribute):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def lsb_release_attr(attribute):
|
|
|
|
def lsb_release_attr(attribute):
|
|
|
|
|
|
|
|
# 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.
|
|
|
@ -559,6 +593,7 @@ def lsb_release_attr(attribute):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def distro_release_attr(attribute):
|
|
|
|
def distro_release_attr(attribute):
|
|
|
|
|
|
|
|
# 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.
|
|
|
@ -578,6 +613,7 @@ def distro_release_attr(attribute):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def uname_attr(attribute):
|
|
|
|
def uname_attr(attribute):
|
|
|
|
|
|
|
|
# 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.
|
|
|
@ -594,19 +630,26 @@ def uname_attr(attribute):
|
|
|
|
return _distro.uname_attr(attribute)
|
|
|
|
return _distro.uname_attr(attribute)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class cached_property(object):
|
|
|
|
try:
|
|
|
|
"""A version of @property which caches the value. On access, it calls the
|
|
|
|
from functools import cached_property
|
|
|
|
underlying function and sets the value in `__dict__` so future accesses
|
|
|
|
except ImportError:
|
|
|
|
will not re-call the property.
|
|
|
|
# Python < 3.8
|
|
|
|
"""
|
|
|
|
class cached_property(object): # type: ignore
|
|
|
|
def __init__(self, f):
|
|
|
|
"""A version of @property which caches the value. On access, it calls the
|
|
|
|
self._fname = f.__name__
|
|
|
|
underlying function and sets the value in `__dict__` so future accesses
|
|
|
|
self._f = f
|
|
|
|
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):
|
|
|
|
def __get__(self, obj, owner):
|
|
|
|
assert obj is not None, 'call {0} on an instance'.format(self._fname)
|
|
|
|
# type: (Any, Type[Any]) -> Any
|
|
|
|
ret = obj.__dict__[self._fname] = self._f(obj)
|
|
|
|
assert obj is not None, "call {0} on an instance".format(self._fname)
|
|
|
|
return ret
|
|
|
|
ret = obj.__dict__[self._fname] = self._f(obj)
|
|
|
|
|
|
|
|
return ret
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class LinuxDistribution(object):
|
|
|
|
class LinuxDistribution(object):
|
|
|
@ -627,11 +670,15 @@ class LinuxDistribution(object):
|
|
|
|
lsb_release command.
|
|
|
|
lsb_release command.
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(self,
|
|
|
|
def __init__(
|
|
|
|
include_lsb=True,
|
|
|
|
self,
|
|
|
|
os_release_file='',
|
|
|
|
include_lsb=True,
|
|
|
|
distro_release_file='',
|
|
|
|
os_release_file="",
|
|
|
|
include_uname=True):
|
|
|
|
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
|
|
|
|
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.
|
|
|
@ -670,6 +717,9 @@ class LinuxDistribution(object):
|
|
|
|
the program execution path the data source for the uname command will
|
|
|
|
the program execution path the data source for the uname command will
|
|
|
|
be empty.
|
|
|
|
be empty.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
* ``root_dir`` (string): The absolute path to the root directory to use
|
|
|
|
|
|
|
|
to find distro-related information files.
|
|
|
|
|
|
|
|
|
|
|
|
Public instance attributes:
|
|
|
|
Public instance attributes:
|
|
|
|
|
|
|
|
|
|
|
|
* ``os_release_file`` (string): The path name of the
|
|
|
|
* ``os_release_file`` (string): The path name of the
|
|
|
@ -699,28 +749,50 @@ class LinuxDistribution(object):
|
|
|
|
* :py:exc:`UnicodeError`: A data source has unexpected characters or
|
|
|
|
* :py:exc:`UnicodeError`: A data source has unexpected characters or
|
|
|
|
uses an unexpected encoding.
|
|
|
|
uses an unexpected encoding.
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
self.os_release_file = os_release_file or \
|
|
|
|
self.root_dir = root_dir
|
|
|
|
os.path.join(_UNIXCONFDIR, _OS_RELEASE_BASENAME)
|
|
|
|
self.etc_dir = os.path.join(root_dir, "etc") if root_dir else _UNIXCONFDIR
|
|
|
|
self.distro_release_file = distro_release_file or '' # updated later
|
|
|
|
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_lsb = include_lsb
|
|
|
|
self.include_uname = include_uname
|
|
|
|
self.include_uname = include_uname
|
|
|
|
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
def __repr__(self):
|
|
|
|
"""Return repr of all info
|
|
|
|
# type: () -> str
|
|
|
|
"""
|
|
|
|
"""Return repr of all info"""
|
|
|
|
return \
|
|
|
|
return (
|
|
|
|
"LinuxDistribution(" \
|
|
|
|
"LinuxDistribution("
|
|
|
|
"os_release_file={self.os_release_file!r}, " \
|
|
|
|
"os_release_file={self.os_release_file!r}, "
|
|
|
|
"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}, "
|
|
|
|
"_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(
|
|
|
|
"_uname_info={self._uname_info!r})".format(self=self)
|
|
|
|
self=self)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def linux_distribution(self, full_distribution_name=True):
|
|
|
|
def linux_distribution(self, full_distribution_name=True):
|
|
|
|
|
|
|
|
# type: (bool) -> 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
|
|
|
@ -731,92 +803,102 @@ class LinuxDistribution(object):
|
|
|
|
return (
|
|
|
|
return (
|
|
|
|
self.name() if full_distribution_name else self.id(),
|
|
|
|
self.name() if full_distribution_name else self.id(),
|
|
|
|
self.version(),
|
|
|
|
self.version(),
|
|
|
|
self.codename()
|
|
|
|
self.codename(),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def id(self):
|
|
|
|
def id(self):
|
|
|
|
|
|
|
|
# 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, 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)
|
|
|
|
return table.get(distro_id, distro_id)
|
|
|
|
|
|
|
|
|
|
|
|
distro_id = self.os_release_attr('id')
|
|
|
|
distro_id = self.os_release_attr("id")
|
|
|
|
if distro_id:
|
|
|
|
if distro_id:
|
|
|
|
return normalize(distro_id, NORMALIZED_OS_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:
|
|
|
|
if distro_id:
|
|
|
|
return normalize(distro_id, NORMALIZED_LSB_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:
|
|
|
|
if distro_id:
|
|
|
|
return normalize(distro_id, NORMALIZED_DISTRO_ID)
|
|
|
|
return normalize(distro_id, NORMALIZED_DISTRO_ID)
|
|
|
|
|
|
|
|
|
|
|
|
distro_id = self.uname_attr('id')
|
|
|
|
distro_id = self.uname_attr("id")
|
|
|
|
if distro_id:
|
|
|
|
if distro_id:
|
|
|
|
return normalize(distro_id, NORMALIZED_DISTRO_ID)
|
|
|
|
return normalize(distro_id, NORMALIZED_DISTRO_ID)
|
|
|
|
|
|
|
|
|
|
|
|
return ''
|
|
|
|
return ""
|
|
|
|
|
|
|
|
|
|
|
|
def name(self, pretty=False):
|
|
|
|
def name(self, pretty=False):
|
|
|
|
|
|
|
|
# type: (bool) -> str
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
Return the name of the OS distribution, as a string.
|
|
|
|
Return the name of the OS distribution, as a string.
|
|
|
|
|
|
|
|
|
|
|
|
For details, see :func:`distro.name`.
|
|
|
|
For details, see :func:`distro.name`.
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
name = self.os_release_attr('name') \
|
|
|
|
name = (
|
|
|
|
or self.lsb_release_attr('distributor_id') \
|
|
|
|
self.os_release_attr("name")
|
|
|
|
or self.distro_release_attr('name') \
|
|
|
|
or self.lsb_release_attr("distributor_id")
|
|
|
|
or self.uname_attr('name')
|
|
|
|
or self.distro_release_attr("name")
|
|
|
|
|
|
|
|
or self.uname_attr("name")
|
|
|
|
|
|
|
|
)
|
|
|
|
if pretty:
|
|
|
|
if pretty:
|
|
|
|
name = self.os_release_attr('pretty_name') \
|
|
|
|
name = self.os_release_attr("pretty_name") or self.lsb_release_attr(
|
|
|
|
or self.lsb_release_attr('description')
|
|
|
|
"description"
|
|
|
|
|
|
|
|
)
|
|
|
|
if not name:
|
|
|
|
if not name:
|
|
|
|
name = self.distro_release_attr('name') \
|
|
|
|
name = self.distro_release_attr("name") or self.uname_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 = name + " " + version
|
|
|
|
return name or ''
|
|
|
|
return name or ""
|
|
|
|
|
|
|
|
|
|
|
|
def version(self, pretty=False, best=False):
|
|
|
|
def version(self, pretty=False, best=False):
|
|
|
|
|
|
|
|
# type: (bool, bool) -> str
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
Return the version of the OS distribution, as a string.
|
|
|
|
Return the version of the OS distribution, as a string.
|
|
|
|
|
|
|
|
|
|
|
|
For details, see :func:`distro.version`.
|
|
|
|
For details, see :func:`distro.version`.
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
versions = [
|
|
|
|
versions = [
|
|
|
|
self.os_release_attr('version_id'),
|
|
|
|
self.os_release_attr("version_id"),
|
|
|
|
self.lsb_release_attr('release'),
|
|
|
|
self.lsb_release_attr("release"),
|
|
|
|
self.distro_release_attr('version_id'),
|
|
|
|
self.distro_release_attr("version_id"),
|
|
|
|
self._parse_distro_release_content(
|
|
|
|
self._parse_distro_release_content(self.os_release_attr("pretty_name")).get(
|
|
|
|
self.os_release_attr('pretty_name')).get('version_id', ''),
|
|
|
|
"version_id", ""
|
|
|
|
|
|
|
|
),
|
|
|
|
self._parse_distro_release_content(
|
|
|
|
self._parse_distro_release_content(
|
|
|
|
self.lsb_release_attr('description')).get('version_id', ''),
|
|
|
|
self.lsb_release_attr("description")
|
|
|
|
self.uname_attr('release')
|
|
|
|
).get("version_id", ""),
|
|
|
|
|
|
|
|
self.uname_attr("release"),
|
|
|
|
]
|
|
|
|
]
|
|
|
|
version = ''
|
|
|
|
version = ""
|
|
|
|
if best:
|
|
|
|
if best:
|
|
|
|
# This algorithm uses the last version in priority order that has
|
|
|
|
# This algorithm uses the last version in priority order that has
|
|
|
|
# the best precision. If the versions are not in conflict, that
|
|
|
|
# the best precision. If the versions are not in conflict, that
|
|
|
|
# does not matter; otherwise, using the last one instead of the
|
|
|
|
# does not matter; otherwise, using the last one instead of the
|
|
|
|
# first one might be considered a surprise.
|
|
|
|
# first one might be considered a surprise.
|
|
|
|
for v in versions:
|
|
|
|
for v in versions:
|
|
|
|
if v.count(".") > version.count(".") or version == '':
|
|
|
|
if v.count(".") > version.count(".") or version == "":
|
|
|
|
version = v
|
|
|
|
version = v
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
for v in versions:
|
|
|
|
for v in versions:
|
|
|
|
if v != '':
|
|
|
|
if v != "":
|
|
|
|
version = v
|
|
|
|
version = v
|
|
|
|
break
|
|
|
|
break
|
|
|
|
if pretty and version and self.codename():
|
|
|
|
if pretty and version and self.codename():
|
|
|
|
version = u'{0} ({1})'.format(version, self.codename())
|
|
|
|
version = "{0} ({1})".format(version, self.codename())
|
|
|
|
return version
|
|
|
|
return version
|
|
|
|
|
|
|
|
|
|
|
|
def version_parts(self, best=False):
|
|
|
|
def version_parts(self, best=False):
|
|
|
|
|
|
|
|
# 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.
|
|
|
@ -825,14 +907,15 @@ class LinuxDistribution(object):
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
version_str = self.version(best=best)
|
|
|
|
version_str = self.version(best=best)
|
|
|
|
if version_str:
|
|
|
|
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)
|
|
|
|
matches = version_regex.match(version_str)
|
|
|
|
if matches:
|
|
|
|
if matches:
|
|
|
|
major, minor, build_number = matches.groups()
|
|
|
|
major, minor, build_number = matches.groups()
|
|
|
|
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=False):
|
|
|
|
|
|
|
|
# type: (bool) -> str
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
Return the major version number of the current distribution.
|
|
|
|
Return the major version number of the current distribution.
|
|
|
|
|
|
|
|
|
|
|
@ -841,6 +924,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=False):
|
|
|
|
|
|
|
|
# type: (bool) -> str
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
Return the minor version number of the current distribution.
|
|
|
|
Return the minor version number of the current distribution.
|
|
|
|
|
|
|
|
|
|
|
@ -849,6 +933,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=False):
|
|
|
|
|
|
|
|
# type: (bool) -> str
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
Return the build number of the current distribution.
|
|
|
|
Return the build number of the current distribution.
|
|
|
|
|
|
|
|
|
|
|
@ -857,14 +942,16 @@ class LinuxDistribution(object):
|
|
|
|
return self.version_parts(best)[2]
|
|
|
|
return self.version_parts(best)[2]
|
|
|
|
|
|
|
|
|
|
|
|
def like(self):
|
|
|
|
def like(self):
|
|
|
|
|
|
|
|
# type: () -> str
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
Return the IDs of distributions that are like the OS distribution.
|
|
|
|
Return the IDs of distributions that are like the OS distribution.
|
|
|
|
|
|
|
|
|
|
|
|
For details, see :func:`distro.like`.
|
|
|
|
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):
|
|
|
|
def codename(self):
|
|
|
|
|
|
|
|
# type: () -> str
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
Return the codename of the OS distribution.
|
|
|
|
Return the codename of the OS distribution.
|
|
|
|
|
|
|
|
|
|
|
@ -873,13 +960,16 @@ class LinuxDistribution(object):
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
# Handle os_release specially since distros might purposefully set
|
|
|
|
# Handle os_release specially since distros might purposefully set
|
|
|
|
# this to empty string to have no codename
|
|
|
|
# this to empty string to have no codename
|
|
|
|
return self._os_release_info['codename']
|
|
|
|
return self._os_release_info["codename"]
|
|
|
|
except KeyError:
|
|
|
|
except KeyError:
|
|
|
|
return self.lsb_release_attr('codename') \
|
|
|
|
return (
|
|
|
|
or self.distro_release_attr('codename') \
|
|
|
|
self.lsb_release_attr("codename")
|
|
|
|
or ''
|
|
|
|
or self.distro_release_attr("codename")
|
|
|
|
|
|
|
|
or ""
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def info(self, pretty=False, best=False):
|
|
|
|
def info(self, pretty=False, best=False):
|
|
|
|
|
|
|
|
# type: (bool, bool) -> InfoDict
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
Return certain machine-readable information about the OS
|
|
|
|
Return certain machine-readable information about the OS
|
|
|
|
distribution.
|
|
|
|
distribution.
|
|
|
@ -892,13 +982,14 @@ class LinuxDistribution(object):
|
|
|
|
version_parts=dict(
|
|
|
|
version_parts=dict(
|
|
|
|
major=self.major_version(best),
|
|
|
|
major=self.major_version(best),
|
|
|
|
minor=self.minor_version(best),
|
|
|
|
minor=self.minor_version(best),
|
|
|
|
build_number=self.build_number(best)
|
|
|
|
build_number=self.build_number(best),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
like=self.like(),
|
|
|
|
like=self.like(),
|
|
|
|
codename=self.codename(),
|
|
|
|
codename=self.codename(),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def os_release_info(self):
|
|
|
|
def os_release_info(self):
|
|
|
|
|
|
|
|
# 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.
|
|
|
@ -908,6 +999,7 @@ class LinuxDistribution(object):
|
|
|
|
return self._os_release_info
|
|
|
|
return self._os_release_info
|
|
|
|
|
|
|
|
|
|
|
|
def lsb_release_info(self):
|
|
|
|
def lsb_release_info(self):
|
|
|
|
|
|
|
|
# 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
|
|
|
@ -918,6 +1010,7 @@ class LinuxDistribution(object):
|
|
|
|
return self._lsb_release_info
|
|
|
|
return self._lsb_release_info
|
|
|
|
|
|
|
|
|
|
|
|
def distro_release_info(self):
|
|
|
|
def distro_release_info(self):
|
|
|
|
|
|
|
|
# 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
|
|
|
@ -928,6 +1021,7 @@ class LinuxDistribution(object):
|
|
|
|
return self._distro_release_info
|
|
|
|
return self._distro_release_info
|
|
|
|
|
|
|
|
|
|
|
|
def uname_info(self):
|
|
|
|
def uname_info(self):
|
|
|
|
|
|
|
|
# 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.
|
|
|
@ -937,43 +1031,48 @@ class LinuxDistribution(object):
|
|
|
|
return self._uname_info
|
|
|
|
return self._uname_info
|
|
|
|
|
|
|
|
|
|
|
|
def os_release_attr(self, attribute):
|
|
|
|
def os_release_attr(self, attribute):
|
|
|
|
|
|
|
|
# type: (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.
|
|
|
|
|
|
|
|
|
|
|
|
For details, see :func:`distro.os_release_attr`.
|
|
|
|
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):
|
|
|
|
def lsb_release_attr(self, attribute):
|
|
|
|
|
|
|
|
# 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.
|
|
|
|
|
|
|
|
|
|
|
|
For details, see :func:`distro.lsb_release_attr`.
|
|
|
|
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):
|
|
|
|
def distro_release_attr(self, attribute):
|
|
|
|
|
|
|
|
# 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.
|
|
|
|
|
|
|
|
|
|
|
|
For details, see :func:`distro.distro_release_attr`.
|
|
|
|
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):
|
|
|
|
def uname_attr(self, attribute):
|
|
|
|
|
|
|
|
# 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.
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
@cached_property
|
|
|
|
def _os_release_info(self):
|
|
|
|
def _os_release_info(self):
|
|
|
|
|
|
|
|
# type: () -> Dict[str, str]
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
Get the information items from the specified os-release file.
|
|
|
|
Get the information items from the specified os-release file.
|
|
|
|
|
|
|
|
|
|
|
@ -987,6 +1086,7 @@ class LinuxDistribution(object):
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
@staticmethod
|
|
|
|
def _parse_os_release_content(lines):
|
|
|
|
def _parse_os_release_content(lines):
|
|
|
|
|
|
|
|
# type: (TextIO) -> Dict[str, str]
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
Parse the lines of an os-release file.
|
|
|
|
Parse the lines of an os-release file.
|
|
|
|
|
|
|
|
|
|
|
@ -1011,7 +1111,7 @@ class LinuxDistribution(object):
|
|
|
|
# parsed content is a unicode object. The following fix resolves that
|
|
|
|
# parsed content is a unicode object. The following fix resolves that
|
|
|
|
# (... but it should be fixed in shlex...):
|
|
|
|
# (... but it should be fixed in shlex...):
|
|
|
|
if sys.version_info[0] == 2 and isinstance(lexer.wordchars, bytes):
|
|
|
|
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)
|
|
|
|
tokens = list(lexer)
|
|
|
|
for token in tokens:
|
|
|
|
for token in tokens:
|
|
|
@ -1021,37 +1121,38 @@ class LinuxDistribution(object):
|
|
|
|
# stripped, etc.), so the tokens are now either:
|
|
|
|
# stripped, etc.), so the tokens are now either:
|
|
|
|
# * variable assignments: var=value
|
|
|
|
# * variable assignments: var=value
|
|
|
|
# * commands or their arguments (not allowed in os-release)
|
|
|
|
# * commands or their arguments (not allowed in os-release)
|
|
|
|
if '=' in token:
|
|
|
|
if "=" in token:
|
|
|
|
k, v = token.split('=', 1)
|
|
|
|
k, v = token.split("=", 1)
|
|
|
|
props[k.lower()] = v
|
|
|
|
props[k.lower()] = v
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
# Ignore any tokens that are not variable assignments
|
|
|
|
# Ignore any tokens that are not variable assignments
|
|
|
|
pass
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
if 'version_codename' in props:
|
|
|
|
if "version_codename" in props:
|
|
|
|
# os-release added a version_codename field. Use that in
|
|
|
|
# os-release added a version_codename field. Use that in
|
|
|
|
# preference to anything else Note that some distros purposefully
|
|
|
|
# preference to anything else Note that some distros purposefully
|
|
|
|
# do not have code names. They should be setting
|
|
|
|
# do not have code names. They should be setting
|
|
|
|
# version_codename=""
|
|
|
|
# version_codename=""
|
|
|
|
props['codename'] = props['version_codename']
|
|
|
|
props["codename"] = props["version_codename"]
|
|
|
|
elif 'ubuntu_codename' in props:
|
|
|
|
elif "ubuntu_codename" in props:
|
|
|
|
# Same as above but a non-standard field name used on older Ubuntus
|
|
|
|
# Same as above but a non-standard field name used on older Ubuntus
|
|
|
|
props['codename'] = props['ubuntu_codename']
|
|
|
|
props["codename"] = props["ubuntu_codename"]
|
|
|
|
elif 'version' in props:
|
|
|
|
elif "version" in props:
|
|
|
|
# If there is no version_codename, parse it from the version
|
|
|
|
# If there is no version_codename, parse it from the version
|
|
|
|
codename = re.search(r'(\(\D+\))|,(\s+)?\D+', props['version'])
|
|
|
|
match = re.search(r"(\(\D+\))|,(\s+)?\D+", props["version"])
|
|
|
|
if codename:
|
|
|
|
if match:
|
|
|
|
codename = codename.group()
|
|
|
|
codename = match.group()
|
|
|
|
codename = codename.strip('()')
|
|
|
|
codename = codename.strip("()")
|
|
|
|
codename = codename.strip(',')
|
|
|
|
codename = codename.strip(",")
|
|
|
|
codename = codename.strip()
|
|
|
|
codename = codename.strip()
|
|
|
|
# codename appears within paranthese.
|
|
|
|
# codename appears within paranthese.
|
|
|
|
props['codename'] = codename
|
|
|
|
props["codename"] = codename
|
|
|
|
|
|
|
|
|
|
|
|
return props
|
|
|
|
return props
|
|
|
|
|
|
|
|
|
|
|
|
@cached_property
|
|
|
|
@cached_property
|
|
|
|
def _lsb_release_info(self):
|
|
|
|
def _lsb_release_info(self):
|
|
|
|
|
|
|
|
# type: () -> Dict[str, str]
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
Get the information items from the lsb_release command output.
|
|
|
|
Get the information items from the lsb_release command output.
|
|
|
|
|
|
|
|
|
|
|
@ -1060,17 +1161,19 @@ class LinuxDistribution(object):
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
if not self.include_lsb:
|
|
|
|
if not self.include_lsb:
|
|
|
|
return {}
|
|
|
|
return {}
|
|
|
|
with open(os.devnull, 'w') as devnull:
|
|
|
|
with open(os.devnull, "wb") as devnull:
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
cmd = ('lsb_release', '-a')
|
|
|
|
cmd = ("lsb_release", "-a")
|
|
|
|
stdout = _check_output(cmd, stderr=devnull)
|
|
|
|
stdout = _check_output(cmd, stderr=devnull)
|
|
|
|
except OSError: # Command not found
|
|
|
|
# Command not found or lsb_release returned error
|
|
|
|
|
|
|
|
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):
|
|
|
|
|
|
|
|
# type: (Iterable[str]) -> Dict[str, str]
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
Parse the output of the lsb_release command.
|
|
|
|
Parse the output of the lsb_release command.
|
|
|
|
|
|
|
|
|
|
|
@ -1085,19 +1188,20 @@ class LinuxDistribution(object):
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
props = {}
|
|
|
|
props = {}
|
|
|
|
for line in lines:
|
|
|
|
for line in lines:
|
|
|
|
kv = line.strip('\n').split(':', 1)
|
|
|
|
kv = line.strip("\n").split(":", 1)
|
|
|
|
if len(kv) != 2:
|
|
|
|
if len(kv) != 2:
|
|
|
|
# Ignore lines without colon.
|
|
|
|
# Ignore lines without colon.
|
|
|
|
continue
|
|
|
|
continue
|
|
|
|
k, v = kv
|
|
|
|
k, v = kv
|
|
|
|
props.update({k.replace(' ', '_').lower(): v.strip()})
|
|
|
|
props.update({k.replace(" ", "_").lower(): v.strip()})
|
|
|
|
return props
|
|
|
|
return props
|
|
|
|
|
|
|
|
|
|
|
|
@cached_property
|
|
|
|
@cached_property
|
|
|
|
def _uname_info(self):
|
|
|
|
def _uname_info(self):
|
|
|
|
with open(os.devnull, 'w') as devnull:
|
|
|
|
# type: () -> Dict[str, str]
|
|
|
|
|
|
|
|
with open(os.devnull, "wb") as devnull:
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
cmd = ('uname', '-rs')
|
|
|
|
cmd = ("uname", "-rs")
|
|
|
|
stdout = _check_output(cmd, stderr=devnull)
|
|
|
|
stdout = _check_output(cmd, stderr=devnull)
|
|
|
|
except OSError:
|
|
|
|
except OSError:
|
|
|
|
return {}
|
|
|
|
return {}
|
|
|
@ -1106,25 +1210,27 @@ class LinuxDistribution(object):
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
@staticmethod
|
|
|
|
def _parse_uname_content(lines):
|
|
|
|
def _parse_uname_content(lines):
|
|
|
|
|
|
|
|
# type: (Sequence[str]) -> Dict[str, str]
|
|
|
|
props = {}
|
|
|
|
props = {}
|
|
|
|
match = re.search(r'^([^\s]+)\s+([\d\.]+)', lines[0].strip())
|
|
|
|
match = re.search(r"^([^\s]+)\s+([\d\.]+)", lines[0].strip())
|
|
|
|
if match:
|
|
|
|
if match:
|
|
|
|
name, version = match.groups()
|
|
|
|
name, version = match.groups()
|
|
|
|
|
|
|
|
|
|
|
|
# This is to prevent the Linux kernel version from
|
|
|
|
# This is to prevent the Linux kernel version from
|
|
|
|
# appearing as the 'best' version on otherwise
|
|
|
|
# appearing as the 'best' version on otherwise
|
|
|
|
# identifiable distributions.
|
|
|
|
# identifiable distributions.
|
|
|
|
if name == 'Linux':
|
|
|
|
if name == "Linux":
|
|
|
|
return {}
|
|
|
|
return {}
|
|
|
|
props['id'] = name.lower()
|
|
|
|
props["id"] = name.lower()
|
|
|
|
props['name'] = name
|
|
|
|
props["name"] = name
|
|
|
|
props['release'] = version
|
|
|
|
props["release"] = version
|
|
|
|
return props
|
|
|
|
return props
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
@staticmethod
|
|
|
|
def _to_str(text):
|
|
|
|
def _to_str(text):
|
|
|
|
|
|
|
|
# type: (Union[bytes, str]) -> str
|
|
|
|
encoding = sys.getfilesystemencoding()
|
|
|
|
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 sys.version_info[0] >= 3:
|
|
|
|
if isinstance(text, bytes):
|
|
|
|
if isinstance(text, bytes):
|
|
|
@ -1137,6 +1243,7 @@ class LinuxDistribution(object):
|
|
|
|
|
|
|
|
|
|
|
|
@cached_property
|
|
|
|
@cached_property
|
|
|
|
def _distro_release_info(self):
|
|
|
|
def _distro_release_info(self):
|
|
|
|
|
|
|
|
# type: () -> Dict[str, str]
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
Get the information items from the specified distro release file.
|
|
|
|
Get the information items from the specified distro release file.
|
|
|
|
|
|
|
|
|
|
|
@ -1146,23 +1253,21 @@ class LinuxDistribution(object):
|
|
|
|
if self.distro_release_file:
|
|
|
|
if self.distro_release_file:
|
|
|
|
# If it was specified, we use it and parse what we can, even if
|
|
|
|
# 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.
|
|
|
|
# its file name or content does not match the expected pattern.
|
|
|
|
distro_info = self._parse_distro_release_file(
|
|
|
|
distro_info = self._parse_distro_release_file(self.distro_release_file)
|
|
|
|
self.distro_release_file)
|
|
|
|
|
|
|
|
basename = os.path.basename(self.distro_release_file)
|
|
|
|
basename = os.path.basename(self.distro_release_file)
|
|
|
|
# The file name pattern for user-specified distro release files
|
|
|
|
# The file name pattern for user-specified distro release files
|
|
|
|
# is somewhat more tolerant (compared to when searching for the
|
|
|
|
# is somewhat more tolerant (compared to when searching for the
|
|
|
|
# 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 \
|
|
|
|
if "name" in distro_info and "cloudlinux" in distro_info["name"].lower():
|
|
|
|
and 'cloudlinux' in distro_info['name'].lower():
|
|
|
|
distro_info["id"] = "cloudlinux"
|
|
|
|
distro_info['id'] = 'cloudlinux'
|
|
|
|
|
|
|
|
elif match:
|
|
|
|
elif match:
|
|
|
|
distro_info['id'] = match.group(1)
|
|
|
|
distro_info["id"] = match.group(1)
|
|
|
|
return distro_info
|
|
|
|
return distro_info
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
basenames = os.listdir(_UNIXCONFDIR)
|
|
|
|
basenames = os.listdir(self.etc_dir)
|
|
|
|
# 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.
|
|
|
@ -1172,38 +1277,41 @@ 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 = ['SuSE-release',
|
|
|
|
basenames = [
|
|
|
|
'arch-release',
|
|
|
|
"SuSE-release",
|
|
|
|
'base-release',
|
|
|
|
"arch-release",
|
|
|
|
'centos-release',
|
|
|
|
"base-release",
|
|
|
|
'fedora-release',
|
|
|
|
"centos-release",
|
|
|
|
'gentoo-release',
|
|
|
|
"fedora-release",
|
|
|
|
'mageia-release',
|
|
|
|
"gentoo-release",
|
|
|
|
'mandrake-release',
|
|
|
|
"mageia-release",
|
|
|
|
'mandriva-release',
|
|
|
|
"mandrake-release",
|
|
|
|
'mandrivalinux-release',
|
|
|
|
"mandriva-release",
|
|
|
|
'manjaro-release',
|
|
|
|
"mandrivalinux-release",
|
|
|
|
'oracle-release',
|
|
|
|
"manjaro-release",
|
|
|
|
'redhat-release',
|
|
|
|
"oracle-release",
|
|
|
|
'sl-release',
|
|
|
|
"redhat-release",
|
|
|
|
'slackware-version']
|
|
|
|
"sl-release",
|
|
|
|
|
|
|
|
"slackware-version",
|
|
|
|
|
|
|
|
]
|
|
|
|
for basename in basenames:
|
|
|
|
for basename in basenames:
|
|
|
|
if basename in _DISTRO_RELEASE_IGNORE_BASENAMES:
|
|
|
|
if basename in _DISTRO_RELEASE_IGNORE_BASENAMES:
|
|
|
|
continue
|
|
|
|
continue
|
|
|
|
match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename)
|
|
|
|
match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename)
|
|
|
|
if match:
|
|
|
|
if match:
|
|
|
|
filepath = os.path.join(_UNIXCONFDIR, basename)
|
|
|
|
filepath = os.path.join(self.etc_dir, basename)
|
|
|
|
distro_info = self._parse_distro_release_file(filepath)
|
|
|
|
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
|
|
|
|
# The name is always present if the pattern matches
|
|
|
|
self.distro_release_file = filepath
|
|
|
|
self.distro_release_file = filepath
|
|
|
|
distro_info['id'] = match.group(1)
|
|
|
|
distro_info["id"] = match.group(1)
|
|
|
|
if 'cloudlinux' in distro_info['name'].lower():
|
|
|
|
if "cloudlinux" in distro_info["name"].lower():
|
|
|
|
distro_info['id'] = 'cloudlinux'
|
|
|
|
distro_info["id"] = "cloudlinux"
|
|
|
|
return distro_info
|
|
|
|
return distro_info
|
|
|
|
return {}
|
|
|
|
return {}
|
|
|
|
|
|
|
|
|
|
|
|
def _parse_distro_release_file(self, filepath):
|
|
|
|
def _parse_distro_release_file(self, filepath):
|
|
|
|
|
|
|
|
# type: (str) -> Dict[str, str]
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
Parse a distro release file.
|
|
|
|
Parse a distro release file.
|
|
|
|
|
|
|
|
|
|
|
@ -1222,11 +1330,12 @@ class LinuxDistribution(object):
|
|
|
|
except (OSError, IOError):
|
|
|
|
except (OSError, IOError):
|
|
|
|
# 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/nir0s/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):
|
|
|
|
|
|
|
|
# type: (str) -> Dict[str, str]
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
Parse a line from a distro release file.
|
|
|
|
Parse a line from a distro release file.
|
|
|
|
|
|
|
|
|
|
|
@ -1237,18 +1346,17 @@ class LinuxDistribution(object):
|
|
|
|
Returns:
|
|
|
|
Returns:
|
|
|
|
A dictionary containing all information items.
|
|
|
|
A dictionary containing all information items.
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
matches = _DISTRO_RELEASE_CONTENT_REVERSED_PATTERN.match(
|
|
|
|
matches = _DISTRO_RELEASE_CONTENT_REVERSED_PATTERN.match(line.strip()[::-1])
|
|
|
|
line.strip()[::-1])
|
|
|
|
|
|
|
|
distro_info = {}
|
|
|
|
distro_info = {}
|
|
|
|
if matches:
|
|
|
|
if matches:
|
|
|
|
# regexp ensures non-None
|
|
|
|
# regexp ensures non-None
|
|
|
|
distro_info['name'] = matches.group(3)[::-1]
|
|
|
|
distro_info["name"] = matches.group(3)[::-1]
|
|
|
|
if matches.group(2):
|
|
|
|
if matches.group(2):
|
|
|
|
distro_info['version_id'] = matches.group(2)[::-1]
|
|
|
|
distro_info["version_id"] = matches.group(2)[::-1]
|
|
|
|
if matches.group(1):
|
|
|
|
if matches.group(1):
|
|
|
|
distro_info['codename'] = matches.group(1)[::-1]
|
|
|
|
distro_info["codename"] = matches.group(1)[::-1]
|
|
|
|
elif line:
|
|
|
|
elif line:
|
|
|
|
distro_info['name'] = line.strip()
|
|
|
|
distro_info["name"] = line.strip()
|
|
|
|
return distro_info
|
|
|
|
return distro_info
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -1256,16 +1364,19 @@ _distro = LinuxDistribution()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
def main():
|
|
|
|
|
|
|
|
# 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))
|
|
|
|
|
|
|
|
|
|
|
|
logger.info('Name: %s', name(pretty=True))
|
|
|
|
dist = _distro
|
|
|
|
distribution_version = version(pretty=True)
|
|
|
|
|
|
|
|
logger.info('Version: %s', distribution_version)
|
|
|
|
logger.info("Name: %s", dist.name(pretty=True))
|
|
|
|
distribution_codename = codename()
|
|
|
|
distribution_version = dist.version(pretty=True)
|
|
|
|
logger.info('Codename: %s', distribution_codename)
|
|
|
|
logger.info("Version: %s", distribution_version)
|
|
|
|
|
|
|
|
distribution_codename = dist.codename()
|
|
|
|
|
|
|
|
logger.info("Codename: %s", distribution_codename)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|
|
|
|
main()
|
|
|
|