__copyright__ = """
Copyright (c) 1999-2000, Marc-Andre Lemburg; mailto:mal@lemburg.com
Copyright (c) 2000-2010, eGenix.com Software GmbH; mailto:info@egenix.com
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee or royalty is hereby granted,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation or portions thereof, including modifications,
that you make.
EGENIX.COM SOFTWARE GMBH DISCLAIMS ALL WARRANTIES WITH REGARD TO
THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL,
INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
WITH THE USE OR PERFORMANCE OF THIS SOFTWARE !
"""
__version__ = '1.0.8'
import collections
import os
import re
import sys
import functools
import itertools
try:
import _wmi
except ImportError:
_wmi = None
_ver_stages = {
'dev': 10,
'alpha': 20, 'a': 20,
'beta': 30, 'b': 30,
'c': 40,
'RC': 50, 'rc': 50,
'pl': 200, 'p': 200,
}
def _comparable_version(version):
component_re = re.compile(r'([0-9]+|[._+-])')
result = []
for v in component_re.split(version):
if v not in '._+-':
try:
v = int(v, 10)
t = 100
except ValueError:
t = _ver_stages.get(v, 0)
result.extend((t, v))
return result
def libc_ver(executable=None, lib='', version='', chunksize=16384):
if not executable:
try:
ver = os.confstr('CS_GNU_LIBC_VERSION')
parts = ver.split(maxsplit=1)
if len(parts) == 2:
return tuple(parts)
except (AttributeError, ValueError, OSError):
pass
executable = sys.executable
if not executable:
return lib, version
libc_search = re.compile(b'(__libc_init)'
b'|'
b'(GLIBC_([0-9.]+))'
b'|'
br'(libc(_\w+)?\.so(?:\.(\d[0-9.]*))?)', re.ASCII)
V = _comparable_version
executable = os.path.realpath(executable)
with open(executable, 'rb') as f:
binary = f.read(chunksize)
pos = 0
while pos < len(binary):
if b'libc' in binary or b'GLIBC' in binary:
m = libc_search.search(binary, pos)
else:
m = None
if not m or m.end() == len(binary):
chunk = f.read(chunksize)
if chunk:
binary = binary[max(pos, len(binary) - 1000):] + chunk
pos = 0
continue
if not m:
break
libcinit, glibc, glibcversion, so, threads, soversion = [
s.decode('latin1') if s is not None else s
for s in m.groups()]
if libcinit and not lib:
lib = 'libc'
elif glibc:
if lib != 'glibc':
lib = 'glibc'
version = glibcversion
elif V(glibcversion) > V(version):
version = glibcversion
elif so:
if lib != 'glibc':
lib = 'libc'
if soversion and (not version or V(soversion) > V(version)):
version = soversion
if threads and version[-len(threads):] != threads:
version = version + threads
pos = m.end()
return lib, version
def _norm_version(version, build=''):
l = version.split('.')
if build:
l.append(build)
try:
strings = list(map(str, map(int, l)))
except ValueError:
strings = l
version = '.'.join(strings[:3])
return version
def _syscmd_ver(system='', release='', version='',
supported_platforms=('win32', 'win16', 'dos')):
if sys.platform not in supported_platforms:
return system, release, version
import subprocess
for cmd in ('ver', 'command /c ver', 'cmd /c ver'):
try:
info = subprocess.check_output(cmd,
stdin=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
text=True,
encoding="locale",
shell=True)
except (OSError, subprocess.CalledProcessError) as why:
continue
else:
break
else:
return system, release, version
ver_output = re.compile(r'(?:([\w ]+) ([\w.]+) '
r'.*'
r'\[.* ([\d.]+)\])')
info = info.strip()
m = ver_output.match(info)
if m is not None:
system, release, version = m.groups()
if release[-1] == '.':
release = release[:-1]
if version[-1] == '.':
version = version[:-1]
version = _norm_version(version)
return system, release, version
def _wmi_query(table, *keys):
global _wmi
if not _wmi:
raise OSError("not supported")
table = {
"OS": "Win32_OperatingSystem",
"CPU": "Win32_Processor",
}[table]
try:
data = _wmi.exec_query("SELECT {} FROM {}".format(
",".join(keys),
table,
)).split("\0")
except OSError:
_wmi = None
raise OSError("not supported")
split_data = (i.partition("=") for i in data)
dict_data = {i[0]: i[2] for i in split_data}
return (dict_data[k] for k in keys)
_WIN32_CLIENT_RELEASES = [
((10, 1, 0), "post11"),
((10, 0, 22000), "11"),
((6, 4, 0), "10"),
((6, 3, 0), "8.1"),
((6, 2, 0), "8"),
((6, 1, 0), "7"),
((6, 0, 0), "Vista"),
((5, 2, 3790), "XP64"),
((5, 2, 0), "XPMedia"),
((5, 1, 0), "XP"),
((5, 0, 0), "2000"),
]
_WIN32_SERVER_RELEASES = [
((10, 1, 0), "post2025Server"),
((10, 0, 26100), "2025Server"),
((10, 0, 20348), "2022Server"),
((10, 0, 17763), "2019Server"),
((6, 4, 0), "2016Server"),
((6, 3, 0), "2012ServerR2"),
((6, 2, 0), "2012Server"),
((6, 1, 0), "2008ServerR2"),
((6, 0, 0), "2008Server"),
((5, 2, 0), "2003Server"),
((5, 0, 0), "2000Server"),
]
def win32_is_iot():
return win32_edition() in ('IoTUAP', 'NanoServer', 'WindowsCoreHeadless', 'IoTEdgeOS')
def win32_edition():
try:
import winreg
except ImportError:
pass
else:
try:
cvkey = r'SOFTWARE\Microsoft\Windows NT\CurrentVersion'
with winreg.OpenKeyEx(winreg.HKEY_LOCAL_MACHINE, cvkey) as key:
return winreg.QueryValueEx(key, 'EditionId')[0]
except OSError:
pass
return None
def _win32_ver(version, csd, ptype):
try:
(version, product_type, ptype, spmajor, spminor) = _wmi_query(
'OS',
'Version',
'ProductType',
'BuildType',
'ServicePackMajorVersion',
'ServicePackMinorVersion',
)
is_client = (int(product_type) == 1)
if spminor and spminor != '0':
csd = f'SP{spmajor}.{spminor}'
else:
csd = f'SP{spmajor}'
return version, csd, ptype, is_client
except OSError:
pass
try:
from sys import getwindowsversion
except ImportError:
return version, csd, ptype, True
winver = getwindowsversion()
is_client = (getattr(winver, 'product_type', 1) == 1)
try:
version = _syscmd_ver()[2]
major, minor, build = map(int, version.split('.'))
except ValueError:
major, minor, build = winver.platform_version or winver[:3]
version = '{0}.{1}.{2}'.format(major, minor, build)
if winver[:2] == (major, minor):
try:
csd = 'SP{}'.format(winver.service_pack_major)
except AttributeError:
if csd[:13] == 'Service Pack ':
csd = 'SP' + csd[13:]
try:
import winreg
except ImportError:
pass
else:
try:
cvkey = r'SOFTWARE\Microsoft\Windows NT\CurrentVersion'
with winreg.OpenKeyEx(winreg.HKEY_LOCAL_MACHINE, cvkey) as key:
ptype = winreg.QueryValueEx(key, 'CurrentType')[0]
except OSError:
pass
return version, csd, ptype, is_client
def win32_ver(release='', version='', csd='', ptype=''):
is_client = False
version, csd, ptype, is_client = _win32_ver(version, csd, ptype)
if version:
intversion = tuple(map(int, version.split('.')))
releases = _WIN32_CLIENT_RELEASES if is_client else _WIN32_SERVER_RELEASES
release = next((r for v, r in releases if v <= intversion), release)
return release, version, csd, ptype
def _mac_ver_xml():
fn = '/System/Library/CoreServices/SystemVersion.plist'
if not os.path.exists(fn):
return None
try:
import plistlib
except ImportError:
return None
with open(fn, 'rb') as f:
pl = plistlib.load(f)
release = pl['ProductVersion']
versioninfo = ('', '', '')
machine = os.uname().machine
if machine in ('ppc', 'Power Macintosh'):
machine = 'PowerPC'
return release, versioninfo, machine
def mac_ver(release='', versioninfo=('', '', ''), machine=''):
info = _mac_ver_xml()
if info is not None:
return info
return release, versioninfo, machine
IOSVersionInfo = collections.namedtuple(
"IOSVersionInfo",
["system", "release", "model", "is_simulator"]
)
def ios_ver(system="", release="", model="", is_simulator=False):
if sys.platform == "ios":
import _ios_support
result = _ios_support.get_platform_ios()
if result is not None:
return IOSVersionInfo(*result)
return IOSVersionInfo(system, release, model, is_simulator)
def _java_getprop(name, default):
from java.lang import System
try:
value = System.getProperty(name)
if value is None:
return default
return value
except AttributeError:
return default
def java_ver(release='', vendor='', vminfo=('', '', ''), osinfo=('', '', '')):
import warnings
warnings._deprecated('java_ver', remove=(3, 15))
try:
import java.lang
except ImportError:
return release, vendor, vminfo, osinfo
vendor = _java_getprop('java.vendor', vendor)
release = _java_getprop('java.version', release)
vm_name, vm_release, vm_vendor = vminfo
vm_name = _java_getprop('java.vm.name', vm_name)
vm_vendor = _java_getprop('java.vm.vendor', vm_vendor)
vm_release = _java_getprop('java.vm.version', vm_release)
vminfo = vm_name, vm_release, vm_vendor
os_name, os_version, os_arch = osinfo
os_arch = _java_getprop('java.os.arch', os_arch)
os_name = _java_getprop('java.os.name', os_name)
os_version = _java_getprop('java.os.version', os_version)
osinfo = os_name, os_version, os_arch
return release, vendor, vminfo, osinfo
AndroidVer = collections.namedtuple(
"AndroidVer", "release api_level manufacturer model device is_emulator")
def android_ver(release="", api_level=0, manufacturer="", model="", device="",
is_emulator=False):
if sys.platform == "android":
try:
from ctypes import CDLL, c_char_p, create_string_buffer
except ImportError:
pass
else:
system_property_get = getattr(CDLL("libc.so"), "__system_property_get")
system_property_get.argtypes = (c_char_p, c_char_p)
def getprop(name, default):
PROP_VALUE_MAX = 92
buffer = create_string_buffer(PROP_VALUE_MAX)
length = system_property_get(name.encode("UTF-8"), buffer)
if length == 0:
return default
else:
return buffer.value.decode("UTF-8", "backslashreplace")
release = getprop("ro.build.version.release", release)
api_level = int(getprop("ro.build.version.sdk", api_level))
manufacturer = getprop("ro.product.manufacturer", manufacturer)
model = getprop("ro.product.model", model)
device = getprop("ro.product.device", device)
is_emulator = getprop("ro.kernel.qemu", "0") == "1"
return AndroidVer(
release, api_level, manufacturer, model, device, is_emulator)
def system_alias(system, release, version):
if system == 'SunOS':
if release < '5':
return system, release, version
l = release.split('.')
if l:
try:
major = int(l[0])
except ValueError:
pass
else:
major = major - 3
l[0] = str(major)
release = '.'.join(l)
if release < '6':
system = 'Solaris'
else:
system = 'Solaris'
elif system in ('win32', 'win16'):
system = 'Windows'
return system, release, version
def _platform(*args):
platform = '-'.join(x.strip() for x in filter(len, args))
platform = platform.replace(' ', '_')
platform = platform.replace('/', '-')
platform = platform.replace('\\', '-')
platform = platform.replace(':', '-')
platform = platform.replace(';', '-')
platform = platform.replace('"', '-')
platform = platform.replace('(', '-')
platform = platform.replace(')', '-')
platform = platform.replace('unknown', '')
while True:
cleaned = platform.replace('--', '-')
if cleaned == platform:
break
platform = cleaned
while platform and platform[-1] == '-':
platform = platform[:-1]
return platform
def _node(default=''):
try:
import socket
except ImportError:
return default
try:
return socket.gethostname()
except OSError:
return default
def _follow_symlinks(filepath):
filepath = os.path.abspath(filepath)
while os.path.islink(filepath):
filepath = os.path.normpath(
os.path.join(os.path.dirname(filepath), os.readlink(filepath)))
return filepath
def _syscmd_file(target, default=''):
if sys.platform in {'dos', 'win32', 'win16', 'ios', 'tvos', 'watchos'}:
return default
try:
import subprocess
except ImportError:
return default
target = _follow_symlinks(target)
env = dict(os.environ, LC_ALL='C')
try:
output = subprocess.check_output(['file', '-b', target],
stderr=subprocess.DEVNULL,
env=env)
except (OSError, subprocess.CalledProcessError):
return default
if not output:
return default
return output.decode('latin-1')
_default_architecture = {
'win32': ('', 'WindowsPE'),
'win16': ('', 'Windows'),
'dos': ('', 'MSDOS'),
}
def architecture(executable=sys.executable, bits='', linkage=''):
if not bits:
import struct
size = struct.calcsize('P')
bits = str(size * 8) + 'bit'
if executable:
fileout = _syscmd_file(executable, '')
else:
fileout = ''
if not fileout and \
executable == sys.executable:
if sys.platform in _default_architecture:
b, l = _default_architecture[sys.platform]
if b:
bits = b
if l:
linkage = l
return bits, linkage
if 'executable' not in fileout and 'shared object' not in fileout:
return bits, linkage
if '32-bit' in fileout:
bits = '32bit'
elif '64-bit' in fileout:
bits = '64bit'
if 'ELF' in fileout:
linkage = 'ELF'
elif 'Mach-O' in fileout:
linkage = "Mach-O"
elif 'PE' in fileout:
if 'Windows' in fileout:
linkage = 'WindowsPE'
else:
linkage = 'PE'
elif 'COFF' in fileout:
linkage = 'COFF'
elif 'MS-DOS' in fileout:
linkage = 'MSDOS'
else:
pass
return bits, linkage
def _get_machine_win32():
try:
[arch, *_] = _wmi_query('CPU', 'Architecture')
except OSError:
pass
else:
try:
arch = ['x86', 'MIPS', 'Alpha', 'PowerPC', None,
'ARM', 'ia64', None, None,
'AMD64', None, None, 'ARM64',
][int(arch)]
except (ValueError, IndexError):
pass
else:
if arch:
return arch
return (
os.environ.get('PROCESSOR_ARCHITEW6432', '') or
os.environ.get('PROCESSOR_ARCHITECTURE', '')
)
class _Processor:
@classmethod
def get(cls):
func = getattr(cls, f'get_{sys.platform}', cls.from_subprocess)
return func() or ''
def get_win32():
try:
manufacturer, caption = _wmi_query('CPU', 'Manufacturer', 'Caption')
except OSError:
return os.environ.get('PROCESSOR_IDENTIFIER', _get_machine_win32())
else:
return f'{caption}, {manufacturer}'
def get_OpenVMS():
try:
import vms_lib
except ImportError:
pass
else:
csid, cpu_number = vms_lib.getsyi('SYI$_CPU', 0)
return 'Alpha' if cpu_number >= 128 else 'VAX'
def get_ios():
if sys.implementation._multiarch.endswith("simulator"):
return os.uname().machine
return 'arm64'
def from_subprocess():
try:
import subprocess
except ImportError:
return None
try:
return subprocess.check_output(
['uname', '-p'],
stderr=subprocess.DEVNULL,
text=True,
encoding="utf8",
).strip()
except (OSError, subprocess.CalledProcessError):
pass
def _unknown_as_blank(val):
return '' if val == 'unknown' else val
class uname_result(
collections.namedtuple(
"uname_result_base",
"system node release version machine")
):
_fields = ('system', 'node', 'release', 'version', 'machine', 'processor')
@functools.cached_property
def processor(self):
return _unknown_as_blank(_Processor.get())
def __iter__(self):
return itertools.chain(
super().__iter__(),
(self.processor,)
)
@classmethod
def _make(cls, iterable):
num_fields = len(cls._fields) - 1
result = cls.__new__(cls, *iterable)
if len(result) != num_fields + 1:
msg = f'Expected {num_fields} arguments, got {len(result)}'
raise TypeError(msg)
return result
def __getitem__(self, key):
return tuple(self)[key]
def __len__(self):
return len(tuple(iter(self)))
def __reduce__(self):
return uname_result, tuple(self)[:len(self._fields) - 1]
_uname_cache = None
def uname():
global _uname_cache
if _uname_cache is not None:
return _uname_cache
try:
system, node, release, version, machine = infos = os.uname()
except AttributeError:
system = sys.platform
node = _node()
release = version = machine = ''
infos = ()
if not any(infos):
if system == 'win32':
release, version, csd, ptype = win32_ver()
machine = machine or _get_machine_win32()
if not (release and version):
system, release, version = _syscmd_ver(system)
if system == 'Microsoft Windows':
system = 'Windows'
elif system == 'Microsoft' and release == 'Windows':
system = 'Windows'
if '6.0' == version[:3]:
release = 'Vista'
else:
release = ''
if system in ('win32', 'win16'):
if not version:
if system == 'win32':
version = '32bit'
else:
version = '16bit'
system = 'Windows'
elif system[:4] == 'java':
release, vendor, vminfo, osinfo = java_ver()
system = 'Java'
version = ', '.join(vminfo)
if not version:
version = vendor
if system == 'OpenVMS':
if not release or release == '0':
release = version
version = ''
if system == 'Microsoft' and release == 'Windows':
system = 'Windows'
release = 'Vista'
if sys.platform == 'android':
system = 'Android'
release = android_ver().release
if sys.platform == 'ios':
system, release, _, _ = ios_ver()
vals = system, node, release, version, machine
_uname_cache = uname_result(*map(_unknown_as_blank, vals))
return _uname_cache
def system():
return uname().system
def node():
return uname().node
def release():
return uname().release
def version():
return uname().version
def machine():
return uname().machine
def processor():
return uname().processor
_sys_version_cache = {}
def _sys_version(sys_version=None):
if sys_version is None:
sys_version = sys.version
result = _sys_version_cache.get(sys_version, None)
if result is not None:
return result
if sys.platform.startswith('java'):
jython_sys_version_parser = re.compile(
r'([\w.+]+)\s*' r'\(#?([^,]+)' r'(?:,\s*([\w ]*)' r'(?:,\s*([\w :]*))?)?\)\s*' r'\[([^\]]+)\]?', re.ASCII) name = 'Jython'
match = jython_sys_version_parser.match(sys_version)
if match is None:
raise ValueError(
'failed to parse Jython sys.version: %s' %
repr(sys_version))
version, buildno, builddate, buildtime, _ = match.groups()
if builddate is None:
builddate = ''
compiler = sys.platform
elif "PyPy" in sys_version:
pypy_sys_version_parser = re.compile(
r'([\w.+]+)\s*'
r'\(#?([^,]+),\s*([\w ]+),\s*([\w :]+)\)\s*'
r'\[PyPy [^\]]+\]?')
name = "PyPy"
match = pypy_sys_version_parser.match(sys_version)
if match is None:
raise ValueError("failed to parse PyPy sys.version: %s" %
repr(sys_version))
version, buildno, builddate, buildtime = match.groups()
compiler = ""
else:
cpython_sys_version_parser = re.compile(
r'([\w.+]+)\s*' r'(?:experimental free-threading build\s+)?' r'\(#?([^,]+)' r'(?:,\s*([\w ]*)' r'(?:,\s*([\w :]*))?)?\)\s*' r'\[([^\]]+)\]?', re.ASCII) match = cpython_sys_version_parser.match(sys_version)
if match is None:
raise ValueError(
'failed to parse CPython sys.version: %s' %
repr(sys_version))
version, buildno, builddate, buildtime, compiler = \
match.groups()
if "RustPython" in sys_version:
name = "RustPython"
else:
name = 'CPython'
if builddate is None:
builddate = ''
elif buildtime:
builddate = builddate + ' ' + buildtime
if hasattr(sys, '_git'):
_, branch, revision = sys._git
elif hasattr(sys, '_mercurial'):
_, branch, revision = sys._mercurial
else:
branch = ''
revision = ''
l = version.split('.')
if len(l) == 2:
l.append('0')
version = '.'.join(l)
result = (name, version, branch, revision, buildno, builddate, compiler)
_sys_version_cache[sys_version] = result
return result
def python_implementation():
return _sys_version()[0]
def python_version():
return _sys_version()[1]
def python_version_tuple():
return tuple(_sys_version()[1].split('.'))
def python_branch():
return _sys_version()[2]
def python_revision():
return _sys_version()[3]
def python_build():
return _sys_version()[4:6]
def python_compiler():
return _sys_version()[6]
_platform_cache = {}
def platform(aliased=False, terse=False):
result = _platform_cache.get((aliased, terse), None)
if result is not None:
return result
system, node, release, version, machine, processor = uname()
if machine == processor:
processor = ''
if aliased:
system, release, version = system_alias(system, release, version)
if system == 'Darwin':
if sys.platform == "ios":
system, release, _, _ = ios_ver()
else:
macos_release = mac_ver()[0]
if macos_release:
system = 'macOS'
release = macos_release
if system == 'Windows':
rel, vers, csd, ptype = win32_ver(version)
if terse:
platform = _platform(system, release)
else:
platform = _platform(system, release, version, csd)
elif system == 'Linux':
libcname, libcversion = libc_ver()
platform = _platform(system, release, machine, processor,
'with',
libcname+libcversion)
elif system == 'Java':
r, v, vminfo, (os_name, os_version, os_arch) = java_ver()
if terse or not os_name:
platform = _platform(system, release, version)
else:
platform = _platform(system, release, version,
'on',
os_name, os_version, os_arch)
else:
if terse:
platform = _platform(system, release)
else:
bits, linkage = architecture(sys.executable)
platform = _platform(system, release, machine,
processor, bits, linkage)
_platform_cache[(aliased, terse)] = platform
return platform
_os_release_candidates = ("/etc/os-release", "/usr/lib/os-release")
_os_release_cache = None
def _parse_os_release(lines):
info = {
"NAME": "Linux",
"ID": "linux",
"PRETTY_NAME": "Linux",
}
os_release_line = re.compile(
"^(?P<name>[a-zA-Z0-9_]+)=(?P<quote>[\"\']?)(?P<value>.*)(?P=quote)$"
)
os_release_unescape = re.compile(r"\\([\\\$\"\'`])")
for line in lines:
mo = os_release_line.match(line)
if mo is not None:
info[mo.group('name')] = os_release_unescape.sub(
r"\1", mo.group('value')
)
return info
def freedesktop_os_release():
global _os_release_cache
if _os_release_cache is None:
errno = None
for candidate in _os_release_candidates:
try:
with open(candidate, encoding="utf-8") as f:
_os_release_cache = _parse_os_release(f)
break
except OSError as e:
errno = e.errno
else:
raise OSError(
errno,
f"Unable to read files {', '.join(_os_release_candidates)}"
)
return _os_release_cache.copy()
if __name__ == '__main__':
terse = ('terse' in sys.argv or '--terse' in sys.argv)
aliased = (not 'nonaliased' in sys.argv and not '--nonaliased' in sys.argv)
print(platform(aliased, terse))
sys.exit(0)