Update platform from CPython 3.10.6

This commit is contained in:
CPython Developers authored and Jeong YunWon committed 2022-08-15 02:36:46 +09:00
1 parent 79f3e5470b
commit 2daddd913d
2 files changed
+562 -157

No files matched your search

+380 -114
View File
@@ -116,6 +116,9 @@ import collections
import os
import re
import sys
import subprocess
import functools
import itertools
### Globals & Constants
@@ -171,9 +174,7 @@ def libc_ver(executable=None, lib='', version='', chunksize=16384):
The file is read and scanned in chunks of chunksize bytes.
"""
# TODO: fix RustPython
return (lib, version)
if executable is None:
if not executable:
try:
ver = os.confstr('CS_GNU_LIBC_VERSION')
# parse 'glibc 2.28' as ('glibc', '2.28')
@@ -238,11 +239,9 @@ def _norm_version(version, build=''):
if build:
l.append(build)
try:
ints = map(int, l)
strings = list(map(str, map(int, l)))
except ValueError:
strings = l
else:
strings = list(map(str, ints))
version = '.'.join(strings[:3])
return version
@@ -281,6 +280,7 @@ def _syscmd_ver(system='', release='', version='',
for cmd in ('ver', 'command /c ver', 'cmd /c ver'):
try:
info = subprocess.check_output(cmd,
stdin=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
text=True,
shell=True)
@@ -364,17 +364,20 @@ def win32_ver(release='', version='', csd='', ptype=''):
return release, version, csd, ptype
winver = getwindowsversion()
maj, min, build = winver.platform_version or winver[:3]
version = '{0}.{1}.{2}'.format(maj, min, build)
try:
major, minor, build = map(int, _syscmd_ver()[2].split('.'))
except ValueError:
major, minor, build = winver.platform_version or winver[:3]
version = '{0}.{1}.{2}'.format(major, minor, build)
release = (_WIN32_CLIENT_RELEASES.get((maj, min)) or
_WIN32_CLIENT_RELEASES.get((maj, None)) or
release = (_WIN32_CLIENT_RELEASES.get((major, minor)) or
_WIN32_CLIENT_RELEASES.get((major, None)) or
release)
# getwindowsversion() reflect the compatibility mode Python is
# running under, and so the service pack value is only going to be
# valid if the versions match.
if winver[:2] == (maj, min):
if winver[:2] == (major, minor):
try:
csd = 'SP{}'.format(winver.service_pack_major)
except AttributeError:
@@ -383,8 +386,8 @@ def win32_ver(release='', version='', csd='', ptype=''):
# VER_NT_SERVER = 3
if getattr(winver, 'product_type', None) == 3:
release = (_WIN32_SERVER_RELEASES.get((maj, min)) or
_WIN32_SERVER_RELEASES.get((maj, None)) or
release = (_WIN32_SERVER_RELEASES.get((major, minor)) or
_WIN32_SERVER_RELEASES.get((major, None)) or
release)
try:
@@ -397,9 +400,9 @@ def win32_ver(release='', version='', csd='', ptype=''):
else:
try:
cvkey = r'SOFTWARE\Microsoft\Windows NT\CurrentVersion'
with winreg.OpenKeyEx(HKEY_LOCAL_MACHINE, cvkey) as key:
ptype = QueryValueEx(key, 'CurrentType')[0]
except:
with winreg.OpenKeyEx(winreg.HKEY_LOCAL_MACHINE, cvkey) as key:
ptype = winreg.QueryValueEx(key, 'CurrentType')[0]
except OSError:
pass
return release, version, csd, ptype
@@ -523,16 +526,6 @@ def system_alias(system, release, version):
# XXX Whatever the new SunOS marketing name is...
system = 'Solaris'
elif system == 'IRIX64':
# IRIX reports IRIX64 on platforms with 64-bit support; yet it
# is really a version and not a different platform, since 32-bit
# apps are also supported..
system = 'IRIX'
if version:
version = version + ' (64bit)'
else:
version = '64bit'
elif system in ('win32', 'win16'):
# In case one of the other tricks
system = 'Windows'
@@ -602,24 +595,6 @@ def _follow_symlinks(filepath):
os.path.join(os.path.dirname(filepath), os.readlink(filepath)))
return filepath
def _syscmd_uname(option, default=''):
""" Interface to the system's uname command.
"""
# TODO: fix RustPython
return default
# if sys.platform in ('dos', 'win32', 'win16'):
# # XXX Others too ?
# return default
import subprocess
try:
output = subprocess.check_output(('uname', option),
stderr=subprocess.DEVNULL,
text=True)
except (OSError, subprocess.CalledProcessError):
return default
return (output.strip() or default)
def _syscmd_file(target, default=''):
@@ -715,9 +690,6 @@ def architecture(executable=sys.executable, bits='', linkage=''):
# Bits
if '32-bit' in fileout:
bits = '32bit'
elif 'N32' in fileout:
# On Irix only
bits = 'n32bit'
elif '64-bit' in fileout:
bits = '64bit'
@@ -740,13 +712,103 @@ def architecture(executable=sys.executable, bits='', linkage=''):
return bits, linkage
def _get_machine_win32():
# Try to use the PROCESSOR_* environment variables
# available on Win XP and later; see
# http://support.microsoft.com/kb/888731 and
# http://www.geocities.com/rick_lively/MANUALS/ENV/MSWIN/PROCESSI.HTM
# WOW64 processes mask the native architecture
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():
return os.environ.get('PROCESSOR_IDENTIFIER', _get_machine_win32())
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 from_subprocess():
"""
Fall back to `uname -p`
"""
try:
return subprocess.check_output(
['uname', '-p'],
stderr=subprocess.DEVNULL,
text=True,
).strip()
except (OSError, subprocess.CalledProcessError):
pass
def _unknown_as_blank(val):
return '' if val == 'unknown' else val
### Portable uname() interface
uname_result = collections.namedtuple("uname_result",
"system node release version machine processor")
class uname_result(
collections.namedtuple(
"uname_result_base",
"system node release version machine")
):
"""
A uname_result that's largely compatible with a
simple namedtuple except that 'processor' is
resolved late and cached to avoid calling "uname"
except when needed.
"""
@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):
# override factory to affect length check
num_fields = len(cls._fields)
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)]
_uname_cache = None
def uname():
""" Fairly portable uname interface. Returns a tuple
@@ -760,52 +822,30 @@ def uname():
"""
global _uname_cache
no_os_uname = 0
if _uname_cache is not None:
return _uname_cache
processor = ''
# Get some infos from the builtin os.uname API...
try:
system, node, release, version, machine = os.uname()
system, node, release, version, machine = infos = os.uname()
except AttributeError:
no_os_uname = 1
system = sys.platform
node = _node()
release = version = machine = ''
infos = ()
if no_os_uname or not list(filter(None, (system, node, release, version, machine))):
# Hmm, no there is either no uname or uname has returned
#'unknowns'... we'll have to poke around the system then.
if no_os_uname:
system = sys.platform
release = ''
version = ''
node = _node()
machine = ''
use_syscmd_ver = 1
if not any(infos):
# uname is not available
# Try win32_ver() on win32 platforms
if system == 'win32':
release, version, csd, ptype = win32_ver()
if release and version:
use_syscmd_ver = 0
# Try to use the PROCESSOR_* environment variables
# available on Win XP and later; see
# http://support.microsoft.com/kb/888731 and
# http://www.geocities.com/rick_lively/MANUALS/ENV/MSWIN/PROCESSI.HTM
if not machine:
# WOW64 processes mask the native architecture
if "PROCESSOR_ARCHITEW6432" in os.environ:
machine = os.environ.get("PROCESSOR_ARCHITEW6432", '')
else:
machine = os.environ.get('PROCESSOR_ARCHITECTURE', '')
if not processor:
processor = os.environ.get('PROCESSOR_IDENTIFIER', machine)
machine = machine or _get_machine_win32()
# Try the 'ver' system command available on some
# platforms
if use_syscmd_ver:
if not (release and version):
system, release, version = _syscmd_ver(system)
# Normalize system to what win32_ver() normally returns
# (_syscmd_ver() tends to return the vendor name as well)
@@ -845,42 +885,15 @@ def uname():
if not release or release == '0':
release = version
version = ''
# Get processor information
try:
import vms_lib
except ImportError:
pass
else:
csid, cpu_number = vms_lib.getsyi('SYI$_CPU', 0)
if (cpu_number >= 128):
processor = 'Alpha'
else:
processor = 'VAX'
if not processor:
# Get processor information from the uname system command
processor = _syscmd_uname('-p', '')
#If any unknowns still exist, replace them with ''s, which are more portable
if system == 'unknown':
system = ''
if node == 'unknown':
node = ''
if release == 'unknown':
release = ''
if version == 'unknown':
version = ''
if machine == 'unknown':
machine = ''
if processor == 'unknown':
processor = ''
# normalize name
if system == 'Microsoft' and release == 'Windows':
system = 'Windows'
release = 'Vista'
_uname_cache = uname_result(system, node, release, version,
machine, processor)
vals = system, node, release, version, machine
# Replace 'unknown' values with the more portable ''
_uname_cache = uname_result(*map(_unknown_as_blank, vals))
return _uname_cache
### Direct interfaces to some of the uname() return values
@@ -943,9 +956,162 @@ def processor():
"""
return uname().processor
### Various APIs for extracting information from sys.version
# RustPython specific
from _platform import *
_sys_version_parser = re.compile(
r'([\w.+]+)\s*' # "version<space>"
r'\(#?([^,]+)' # "(#buildno"
r'(?:,\s*([\w ]*)' # ", builddate"
r'(?:,\s*([\w :]*))?)?\)\s*' # ", buildtime)<space>"
r'\[([^\]]+)\]?', re.ASCII) # "[compiler]"
_ironpython_sys_version_parser = re.compile(
r'IronPython\s*'
r'([\d\.]+)'
r'(?: \(([\d\.]+)\))?'
r' on (.NET [\d\.]+)', re.ASCII)
# IronPython covering 2.6 and 2.7
_ironpython26_sys_version_parser = re.compile(
r'([\d.]+)\s*'
r'\(IronPython\s*'
r'[\d.]+\s*'
r'\(([\d.]+)\) on ([\w.]+ [\d.]+(?: \(\d+-bit\))?)\)'
)
_pypy_sys_version_parser = re.compile(
r'([\w.+]+)\s*'
r'\(#?([^,]+),\s*([\w ]+),\s*([\w :]+)\)\s*'
r'\[PyPy [^\]]+\]?')
_sys_version_cache = {}
def _sys_version(sys_version=None):
""" Returns a parsed version of Python's sys.version as tuple
(name, version, branch, revision, buildno, builddate, compiler)
referring to the Python implementation name, version, branch,
revision, build number, build date/time as string and the compiler
identification string.
Note that unlike the Python sys.version, the returned value
for the Python version will always include the patchlevel (it
defaults to '.0').
The function returns empty strings for tuple entries that
cannot be determined.
sys_version may be given to parse an alternative version
string, e.g. if the version was read from a different Python
interpreter.
"""
# Get the Python version
if sys_version is None:
sys_version = sys.version
# Try the cache first
result = _sys_version_cache.get(sys_version, None)
if result is not None:
return result
# Parse it
if 'IronPython' in sys_version:
# IronPython
name = 'IronPython'
if sys_version.startswith('IronPython'):
match = _ironpython_sys_version_parser.match(sys_version)
else:
match = _ironpython26_sys_version_parser.match(sys_version)
if match is None:
raise ValueError(
'failed to parse IronPython sys.version: %s' %
repr(sys_version))
version, alt_version, compiler = match.groups()
buildno = ''
builddate = ''
elif sys.platform.startswith('java'):
# Jython
name = 'Jython'
match = _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
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
match = _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()
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 = ''
# Add the patchlevel version if missing
l = version.split('.')
if len(l) == 2:
l.append('0')
version = '.'.join(l)
# Build and cache the result
result = (name, version, branch, revision, buildno, builddate, compiler)
_sys_version_cache[sys_version] = result
return result
def python_implementation():
""" Returns a string identifying the Python implementation.
Currently, the following implementations are identified:
'CPython' (C implementation of Python),
'IronPython' (.NET implementation of Python),
'Jython' (Java implementation of Python),
'PyPy' (Python implementation of Python).
"""
return _sys_version()[0]
def python_version():
""" Returns the Python version as string 'major.minor.patchlevel'
Note that unlike the Python sys.version, the returned value
will always include the patchlevel (it defaults to 0).
"""
return _sys_version()[1]
def python_version_tuple():
@@ -956,7 +1122,50 @@ def python_version_tuple():
will always include the patchlevel (it defaults to 0).
"""
return tuple(python_version().split('.'))
return tuple(_sys_version()[1].split('.'))
def python_branch():
""" Returns a string identifying the Python implementation
branch.
For CPython this is the SCM branch from which the
Python binary was built.
If not available, an empty string is returned.
"""
return _sys_version()[2]
def python_revision():
""" Returns a string identifying the Python implementation
revision.
For CPython this is the SCM revision from which the
Python binary was built.
If not available, an empty string is returned.
"""
return _sys_version()[3]
def python_build():
""" Returns a tuple (buildno, builddate) stating the Python
build number and date as strings.
"""
return _sys_version()[4:6]
def python_compiler():
""" Returns a string identifying the compiler used for compiling
Python.
"""
return _sys_version()[6]
### The Opus Magnum of platform strings :-)
@@ -1010,7 +1219,7 @@ def platform(aliased=0, terse=0):
elif system in ('Linux',):
# check for libc vs. glibc
libcname, libcversion = libc_ver(sys.executable)
libcname, libcversion = libc_ver()
platform = _platform(system, release, machine, processor,
'with',
libcname+libcversion)
@@ -1036,6 +1245,63 @@ def platform(aliased=0, terse=0):
_platform_cache[(aliased, terse)] = platform
return platform
### freedesktop.org os-release standard
# https://www.freedesktop.org/software/systemd/man/os-release.html
# NAME=value with optional quotes (' or "). The regular expression is less
# strict than shell lexer, but that's ok.
_os_release_line = re.compile(
"^(?P<name>[a-zA-Z0-9_]+)=(?P<quote>[\"\']?)(?P<value>.*)(?P=quote)$"
)
# unescape five special characters mentioned in the standard
_os_release_unescape = re.compile(r"\\([\\\$\"\'`])")
# /etc takes precedence over /usr/lib
_os_release_candidates = ("/etc/os-release", "/usr/lib/os-release")
_os_release_cache = None
def _parse_os_release(lines):
# These fields are mandatory fields with well-known defaults
# in practice all Linux distributions override NAME, ID, and PRETTY_NAME.
info = {
"NAME": "Linux",
"ID": "linux",
"PRETTY_NAME": "Linux",
}
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():
"""Return operation system identification from freedesktop.org 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()
### Command line interface
if __name__ == '__main__':
+182 -43
View File
@@ -1,59 +1,89 @@
import os
import copy
import pickle
import platform
import subprocess
import sys
import sysconfig
import tempfile
import unittest
from unittest import mock
from test import support
from test.support import os_helper
FEDORA_OS_RELEASE = """\
NAME=Fedora
VERSION="32 (Thirty Two)"
ID=fedora
VERSION_ID=32
VERSION_CODENAME=""
PLATFORM_ID="platform:f32"
PRETTY_NAME="Fedora 32 (Thirty Two)"
ANSI_COLOR="0;34"
LOGO=fedora-logo-icon
CPE_NAME="cpe:/o:fedoraproject:fedora:32"
HOME_URL="https://fedoraproject.org/"
DOCUMENTATION_URL="https://docs.fedoraproject.org/en-US/fedora/f32/system-administrators-guide/"
SUPPORT_URL="https://fedoraproject.org/wiki/Communicating_and_getting_help"
BUG_REPORT_URL="https://bugzilla.redhat.com/"
REDHAT_BUGZILLA_PRODUCT="Fedora"
REDHAT_BUGZILLA_PRODUCT_VERSION=32
REDHAT_SUPPORT_PRODUCT="Fedora"
REDHAT_SUPPORT_PRODUCT_VERSION=32
PRIVACY_POLICY_URL="https://fedoraproject.org/wiki/Legal:PrivacyPolicy"
"""
UBUNTU_OS_RELEASE = """\
NAME="Ubuntu"
VERSION="20.04.1 LTS (Focal Fossa)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 20.04.1 LTS"
VERSION_ID="20.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=focal
UBUNTU_CODENAME=focal
"""
TEST_OS_RELEASE = r"""
# test data
ID_LIKE="egg spam viking"
EMPTY=
# comments and empty lines are ignored
SINGLE_QUOTE='single'
EMPTY_SINGLE=''
DOUBLE_QUOTE="double"
EMPTY_DOUBLE=""
QUOTES="double\'s"
SPECIALS="\$\`\\\'\""
# invalid lines
=invalid
=
INVALID
IN-VALID=value
IN VALID=value
"""
class PlatformTest(unittest.TestCase):
def clear_caches(self):
platform._platform_cache.clear()
platform._sys_version_cache.clear()
platform._uname_cache = None
platform._os_release_cache = None
def test_architecture(self):
res = platform.architecture()
@os_helper.skip_unless_symlink
def test_architecture_via_symlink(self): # issue3762
# On Windows, the EXE needs to know where pythonXY.dll and *.pyd is at
# so we add the directory to the path, PYTHONHOME and PYTHONPATH.
env = None
if sys.platform == "win32":
env = {k.upper(): os.environ[k] for k in os.environ}
env["PATH"] = "{};{}".format(
os.path.dirname(sys.executable), env.get("PATH", ""))
env["PYTHONHOME"] = os.path.dirname(sys.executable)
if sysconfig.is_python_build(True):
env["PYTHONPATH"] = os.path.dirname(os.__file__)
with support.PythonSymlink() as py:
cmd = "-c", "import platform; print(platform.architecture())"
self.assertEqual(py.call_real(*cmd), py.call_link(*cmd))
def get(python, env=None):
cmd = [python, '-c',
'import platform; print(platform.architecture())']
p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, env=env)
r = p.communicate()
if p.returncode:
print(repr(r[0]))
print(repr(r[1]), file=sys.stderr)
self.fail('unexpected return code: {0} (0x{0:08X})'
.format(p.returncode))
return r
real = os.path.realpath(sys.executable)
link = os.path.abspath(os_helper.TESTFN)
os.symlink(real, link)
try:
self.assertEqual(get(real), get(link, env=env))
finally:
os.remove(link)
@unittest.skipUnless(sys.platform == 'linux', "TODO: RUSTPYTHON, only works on Linux")
def test_platform(self):
for aliased in (False, True):
for terse in (False, True):
@@ -87,7 +117,6 @@ class PlatformTest(unittest.TestCase):
sys._git = self.save_git
sys.platform = self.save_platform
@support.cpython_only
def test_sys_version(self):
# Old test.
for input, output in (
@@ -186,11 +215,71 @@ class PlatformTest(unittest.TestCase):
res = platform.uname()
self.assertTrue(any(res))
self.assertEqual(res[0], res.system)
self.assertEqual(res[-6], res.system)
self.assertEqual(res[1], res.node)
self.assertEqual(res[-5], res.node)
self.assertEqual(res[2], res.release)
self.assertEqual(res[-4], res.release)
self.assertEqual(res[3], res.version)
self.assertEqual(res[-3], res.version)
self.assertEqual(res[4], res.machine)
self.assertEqual(res[-2], res.machine)
self.assertEqual(res[5], res.processor)
self.assertEqual(res[-1], res.processor)
self.assertEqual(len(res), 6)
def test_uname_cast_to_tuple(self):
res = platform.uname()
expected = (
res.system, res.node, res.release, res.version, res.machine,
res.processor,
)
self.assertEqual(tuple(res), expected)
def test_uname_replace(self):
res = platform.uname()
new = res._replace(
system='system', node='node', release='release',
version='version', machine='machine')
self.assertEqual(new.system, 'system')
self.assertEqual(new.node, 'node')
self.assertEqual(new.release, 'release')
self.assertEqual(new.version, 'version')
self.assertEqual(new.machine, 'machine')
# processor cannot be replaced
self.assertEqual(new.processor, res.processor)
def test_uname_copy(self):
uname = platform.uname()
self.assertEqual(copy.copy(uname), uname)
self.assertEqual(copy.deepcopy(uname), uname)
def test_uname_pickle(self):
orig = platform.uname()
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
with self.subTest(protocol=proto):
pickled = pickle.dumps(orig, proto)
restored = pickle.loads(pickled)
self.assertEqual(restored, orig)
def test_uname_slices(self):
res = platform.uname()
expected = tuple(res)
self.assertEqual(res[:], expected)
self.assertEqual(res[:5], expected[:5])
@unittest.skipIf(sys.platform in ['win32', 'OpenVMS'], "uname -p not used")
def test_uname_processor(self):
"""
On some systems, the processor must match the output
of 'uname -p'. See Issue 35967 for rationale.
"""
try:
proc_res = subprocess.check_output(['uname', '-p'], text=True).strip()
expect = platform._unknown_as_blank(proc_res)
except (OSError, subprocess.CalledProcessError):
expect = ''
self.assertEqual(platform.uname().processor, expect)
@unittest.skipUnless(sys.platform.startswith('win'), "windows only test")
def test_uname_win32_ARCHITEW6432(self):
@@ -241,7 +330,10 @@ class PlatformTest(unittest.TestCase):
# On Snow Leopard, sw_vers reports 10.6.0 as 10.6
if len_diff > 0:
expect_list.extend(['0'] * len_diff)
self.assertEqual(result_list, expect_list)
# For compatibility with older binaries, macOS 11.x may report
# itself as '10.16' rather than '11.x.y'.
if result_list != ['10', '16']:
self.assertEqual(result_list, expect_list)
# res[1] claims to contain
# (version, dev_stage, non_release_version)
@@ -253,7 +345,6 @@ class PlatformTest(unittest.TestCase):
else:
self.assertEqual(res[2], 'PowerPC')
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.skipUnless(sys.platform == 'darwin', "OSX only test")
@@ -270,18 +361,19 @@ class PlatformTest(unittest.TestCase):
else:
# parent
cpid, sts = os.waitpid(pid, 0)
self.assertEqual(cpid, pid)
self.assertEqual(sts, 0)
support.wait_process(pid, exitcode=0)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_libc_ver(self):
# check that libc_ver(executable) doesn't raise an exception
if os.path.isdir(sys.executable) and \
os.path.exists(sys.executable+'.exe'):
# Cygwin horror
executable = sys.executable + '.exe'
elif sys.platform == "win32" and not os.path.exists(sys.executable):
# App symlink appears to not exist, but we want the
# real executable here anyway
import _winapi
executable = _winapi.GetModuleFileName(0)
else:
executable = sys.executable
platform.libc_ver(executable)
@@ -356,7 +448,6 @@ class PlatformTest(unittest.TestCase):
self.assertLess(V('0.960923'), V('2.2beta29'))
@unittest.skip("TODO: RUSTPYTHON, AttributeError: module 'platform' has no attribute '_sys_version_cache'")
def test_macos(self):
self.addCleanup(self.clear_caches)
@@ -384,6 +475,54 @@ class PlatformTest(unittest.TestCase):
self.assertEqual(platform.platform(terse=1), expected_terse)
self.assertEqual(platform.platform(), expected)
def test_freedesktop_os_release(self):
self.addCleanup(self.clear_caches)
self.clear_caches()
if any(os.path.isfile(fn) for fn in platform._os_release_candidates):
info = platform.freedesktop_os_release()
self.assertIn("NAME", info)
self.assertIn("ID", info)
info["CPYTHON_TEST"] = "test"
self.assertNotIn(
"CPYTHON_TEST",
platform.freedesktop_os_release()
)
else:
with self.assertRaises(OSError):
platform.freedesktop_os_release()
def test_parse_os_release(self):
info = platform._parse_os_release(FEDORA_OS_RELEASE.splitlines())
self.assertEqual(info["NAME"], "Fedora")
self.assertEqual(info["ID"], "fedora")
self.assertNotIn("ID_LIKE", info)
self.assertEqual(info["VERSION_CODENAME"], "")
info = platform._parse_os_release(UBUNTU_OS_RELEASE.splitlines())
self.assertEqual(info["NAME"], "Ubuntu")
self.assertEqual(info["ID"], "ubuntu")
self.assertEqual(info["ID_LIKE"], "debian")
self.assertEqual(info["VERSION_CODENAME"], "focal")
info = platform._parse_os_release(TEST_OS_RELEASE.splitlines())
expected = {
"ID": "linux",
"NAME": "Linux",
"PRETTY_NAME": "Linux",
"ID_LIKE": "egg spam viking",
"EMPTY": "",
"DOUBLE_QUOTE": "double",
"EMPTY_DOUBLE": "",
"SINGLE_QUOTE": "single",
"EMPTY_SINGLE": "",
"QUOTES": "double's",
"SPECIALS": "$`\\'\"",
}
self.assertEqual(info, expected)
self.assertEqual(len(info["SPECIALS"]), 5)
if __name__ == '__main__':
unittest.main()