mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-02 19:39:49 +09:00
Update platform.py to 3.14.4 (#7824)
This commit is contained in:
117
Lib/platform.py
vendored
Executable file → Normal file
117
Lib/platform.py
vendored
Executable file → Normal file
@@ -1,5 +1,3 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
""" This module tries to retrieve as much platform-identifying data as
|
||||
possible. It makes this information available via function APIs.
|
||||
|
||||
@@ -33,6 +31,7 @@
|
||||
#
|
||||
# <see CVS and SVN checkin messages for history>
|
||||
#
|
||||
# 1.0.9 - added invalidate_caches() function to invalidate cached values
|
||||
# 1.0.8 - changed Windows support to read version from kernel32.dll
|
||||
# 1.0.7 - added DEV_NULL
|
||||
# 1.0.6 - added linux_distribution()
|
||||
@@ -111,7 +110,7 @@ __copyright__ = """
|
||||
|
||||
"""
|
||||
|
||||
__version__ = '1.0.8'
|
||||
__version__ = '1.0.9'
|
||||
|
||||
import collections
|
||||
import os
|
||||
@@ -174,6 +173,11 @@ def libc_ver(executable=None, lib='', version='', chunksize=16384):
|
||||
|
||||
"""
|
||||
if not executable:
|
||||
if sys.platform == "emscripten":
|
||||
# Emscripten's os.confstr reports that it is glibc, so special case
|
||||
# it.
|
||||
ver = ".".join(str(x) for x in sys._emscripten_info.emscripten_version)
|
||||
return ("emscripten", ver)
|
||||
try:
|
||||
ver = os.confstr('CS_GNU_LIBC_VERSION')
|
||||
# parse 'glibc 2.28' as ('glibc', '2.28')
|
||||
@@ -190,22 +194,26 @@ def libc_ver(executable=None, lib='', version='', chunksize=16384):
|
||||
# sys.executable is not set.
|
||||
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)
|
||||
libc_search = re.compile(br"""
|
||||
(__libc_init)
|
||||
| (GLIBC_([0-9.]+))
|
||||
| (libc(_\w+)?\.so(?:\.(\d[0-9.]*))?)
|
||||
| (musl-([0-9.]+))
|
||||
| ((?:libc\.|ld-)musl(?:-\w+)?.so(?:\.(\d[0-9.]*))?)
|
||||
""",
|
||||
re.ASCII | re.VERBOSE)
|
||||
|
||||
V = _comparable_version
|
||||
# We use os.path.realpath()
|
||||
# here to work around problems with Cygwin not being
|
||||
# able to open symlinks for reading
|
||||
executable = os.path.realpath(executable)
|
||||
ver = None
|
||||
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:
|
||||
if b'libc' in binary or b'GLIBC' in binary or b'musl' in binary:
|
||||
m = libc_search.search(binary, pos)
|
||||
else:
|
||||
m = None
|
||||
@@ -217,26 +225,35 @@ def libc_ver(executable=None, lib='', version='', chunksize=16384):
|
||||
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()]
|
||||
decoded_groups = [s.decode('latin1') if s is not None else s
|
||||
for s in m.groups()]
|
||||
(libcinit, glibc, glibcversion, so, threads, soversion,
|
||||
musl, muslversion, musl_so, musl_sover) = decoded_groups
|
||||
if libcinit and not lib:
|
||||
lib = 'libc'
|
||||
elif glibc:
|
||||
if lib != 'glibc':
|
||||
lib = 'glibc'
|
||||
version = glibcversion
|
||||
elif V(glibcversion) > V(version):
|
||||
version = glibcversion
|
||||
ver = glibcversion
|
||||
elif V(glibcversion) > V(ver):
|
||||
ver = glibcversion
|
||||
elif so:
|
||||
if lib != 'glibc':
|
||||
if lib not in ('glibc', 'musl'):
|
||||
lib = 'libc'
|
||||
if soversion and (not version or V(soversion) > V(version)):
|
||||
version = soversion
|
||||
if threads and version[-len(threads):] != threads:
|
||||
version = version + threads
|
||||
if soversion and (not ver or V(soversion) > V(ver)):
|
||||
ver = soversion
|
||||
if threads and ver[-len(threads):] != threads:
|
||||
ver = ver + threads
|
||||
elif musl:
|
||||
lib = 'musl'
|
||||
if not ver or V(muslversion) > V(ver):
|
||||
ver = muslversion
|
||||
elif musl_so:
|
||||
lib = 'musl'
|
||||
if musl_sover and (not ver or V(musl_sover) > V(ver)):
|
||||
ver = musl_sover
|
||||
pos = m.end()
|
||||
return lib, version
|
||||
return lib, version if ver is None else ver
|
||||
|
||||
def _norm_version(version, build=''):
|
||||
|
||||
@@ -549,7 +566,7 @@ def java_ver(release='', vendor='', vminfo=('', '', ''), osinfo=('', '', '')):
|
||||
warnings._deprecated('java_ver', remove=(3, 15))
|
||||
# Import the needed APIs
|
||||
try:
|
||||
import java.lang
|
||||
import java.lang # noqa: F401
|
||||
except ImportError:
|
||||
return release, vendor, vminfo, osinfo
|
||||
|
||||
@@ -1192,7 +1209,7 @@ def _sys_version(sys_version=None):
|
||||
# CPython
|
||||
cpython_sys_version_parser = re.compile(
|
||||
r'([\w.+]+)\s*' # "version<space>"
|
||||
r'(?:experimental free-threading build\s+)?' # "free-threading-build<space>"
|
||||
r'(?:free-threading build\s+)?' # "free-threading-build<space>"
|
||||
r'\(#?([^,]+)' # "(#buildno"
|
||||
r'(?:,\s*([\w ]*)' # ", builddate"
|
||||
r'(?:,\s*([\w :]*))?)?\)\s*' # ", buildtime)<space>"
|
||||
@@ -1449,11 +1466,55 @@ def freedesktop_os_release():
|
||||
return _os_release_cache.copy()
|
||||
|
||||
|
||||
def invalidate_caches():
|
||||
"""Invalidate the cached results."""
|
||||
global _uname_cache
|
||||
_uname_cache = None
|
||||
|
||||
global _os_release_cache
|
||||
_os_release_cache = None
|
||||
|
||||
_sys_version_cache.clear()
|
||||
_platform_cache.clear()
|
||||
|
||||
|
||||
### Command line interface
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Default is to print the aliased verbose platform string
|
||||
terse = ('terse' in sys.argv or '--terse' in sys.argv)
|
||||
aliased = (not 'nonaliased' in sys.argv and not '--nonaliased' in sys.argv)
|
||||
def _parse_args(args: list[str] | None):
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(color=True)
|
||||
parser.add_argument("args", nargs="*", choices=["nonaliased", "terse"])
|
||||
parser.add_argument(
|
||||
"--terse",
|
||||
action="store_true",
|
||||
help=(
|
||||
"return only the absolute minimum information needed "
|
||||
"to identify the platform"
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--nonaliased",
|
||||
dest="aliased",
|
||||
action="store_false",
|
||||
help=(
|
||||
"disable system/OS name aliasing. If aliasing is enabled, "
|
||||
"some platforms report system names different from "
|
||||
"their common names, e.g. SunOS is reported as Solaris"
|
||||
),
|
||||
)
|
||||
|
||||
return parser.parse_args(args)
|
||||
|
||||
|
||||
def _main(args: list[str] | None = None):
|
||||
args = _parse_args(args)
|
||||
|
||||
terse = args.terse or ("terse" in args.args)
|
||||
aliased = args.aliased and ('nonaliased' not in args.args)
|
||||
|
||||
print(platform(aliased, terse))
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
_main()
|
||||
|
||||
151
Lib/test/test_platform.py
vendored
151
Lib/test/test_platform.py
vendored
@@ -1,5 +1,8 @@
|
||||
import os
|
||||
import contextlib
|
||||
import copy
|
||||
import io
|
||||
import itertools
|
||||
import os
|
||||
import pickle
|
||||
import platform
|
||||
import subprocess
|
||||
@@ -83,6 +86,38 @@ class PlatformTest(unittest.TestCase):
|
||||
platform._uname_cache = None
|
||||
platform._os_release_cache = None
|
||||
|
||||
def test_invalidate_caches(self):
|
||||
self.clear_caches()
|
||||
|
||||
self.assertDictEqual(platform._platform_cache, {})
|
||||
self.assertDictEqual(platform._sys_version_cache, {})
|
||||
self.assertIsNone(platform._uname_cache)
|
||||
self.assertIsNone(platform._os_release_cache)
|
||||
|
||||
# fill the cached entries (some have side effects on others)
|
||||
platform.platform() # for platform._platform_cache
|
||||
platform.python_implementation() # for platform._sys_version_cache
|
||||
platform.uname() # for platform._uname_cache
|
||||
|
||||
# check that the cache are filled
|
||||
self.assertNotEqual(platform._platform_cache, {})
|
||||
self.assertNotEqual(platform._sys_version_cache, {})
|
||||
self.assertIsNotNone(platform._uname_cache)
|
||||
|
||||
try:
|
||||
platform.freedesktop_os_release()
|
||||
except OSError:
|
||||
self.assertIsNone(platform._os_release_cache)
|
||||
else:
|
||||
self.assertIsNotNone(platform._os_release_cache)
|
||||
|
||||
with self.subTest('clear platform caches'):
|
||||
platform.invalidate_caches()
|
||||
self.assertDictEqual(platform._platform_cache, {})
|
||||
self.assertDictEqual(platform._sys_version_cache, {})
|
||||
self.assertIsNone(platform._uname_cache)
|
||||
self.assertIsNone(platform._os_release_cache)
|
||||
|
||||
def test_architecture(self):
|
||||
res = platform.architecture()
|
||||
|
||||
@@ -375,7 +410,7 @@ class PlatformTest(unittest.TestCase):
|
||||
for v in version.split('.'):
|
||||
int(v) # should not fail
|
||||
if csd:
|
||||
self.assertTrue(csd.startswith('SP'), msg=csd)
|
||||
self.assertStartsWith(csd, 'SP')
|
||||
if ptype:
|
||||
if os.cpu_count() > 1:
|
||||
self.assertIn('Multiprocessor', ptype)
|
||||
@@ -490,8 +525,10 @@ class PlatformTest(unittest.TestCase):
|
||||
self.assertEqual(override.model, "Whiz")
|
||||
self.assertTrue(override.is_simulator)
|
||||
|
||||
@unittest.skipIf(support.is_emscripten, "Does not apply to Emscripten")
|
||||
def test_libc_ver(self):
|
||||
if support.is_emscripten:
|
||||
assert platform.libc_ver() == ("emscripten", "4.0.12")
|
||||
return
|
||||
# check that libc_ver(executable) doesn't raise an exception
|
||||
if os.path.isdir(sys.executable) and \
|
||||
os.path.exists(sys.executable+'.exe'):
|
||||
@@ -519,6 +556,14 @@ class PlatformTest(unittest.TestCase):
|
||||
(b'GLIBC_2.9', ('glibc', '2.9')),
|
||||
(b'libc.so.1.2.5', ('libc', '1.2.5')),
|
||||
(b'libc_pthread.so.1.2.5', ('libc', '1.2.5_pthread')),
|
||||
(b'/aports/main/musl/src/musl-1.2.5', ('musl', '1.2.5')),
|
||||
# musl uses semver, but we accept some variations anyway:
|
||||
(b'/aports/main/musl/src/musl-12.5', ('musl', '12.5')),
|
||||
(b'/aports/main/musl/src/musl-1.2.5.7', ('musl', '1.2.5.7')),
|
||||
(b'libc.musl.so.1', ('musl', '1')),
|
||||
(b'libc.musl-x86_64.so.1.2.5', ('musl', '1.2.5')),
|
||||
(b'ld-musl.so.1', ('musl', '1')),
|
||||
(b'ld-musl-x86_64.so.1.2.5', ('musl', '1.2.5')),
|
||||
(b'', ('', '')),
|
||||
):
|
||||
with open(filename, 'wb') as fp:
|
||||
@@ -530,14 +575,37 @@ class PlatformTest(unittest.TestCase):
|
||||
expected)
|
||||
|
||||
# binary containing multiple versions: get the most recent,
|
||||
# make sure that 1.9 is seen as older than 1.23.4
|
||||
chunksize = 16384
|
||||
with open(filename, 'wb') as f:
|
||||
# test match at chunk boundary
|
||||
f.write(b'x'*(chunksize - 10))
|
||||
f.write(b'GLIBC_1.23.4\0GLIBC_1.9\0GLIBC_1.21\0')
|
||||
self.assertEqual(platform.libc_ver(filename, chunksize=chunksize),
|
||||
('glibc', '1.23.4'))
|
||||
# make sure that eg 1.9 is seen as older than 1.23.4, and that
|
||||
# the arguments don't count even if they are set.
|
||||
chunksize = 200
|
||||
for data, expected in (
|
||||
(b'GLIBC_1.23.4\0GLIBC_1.9\0GLIBC_1.21\0', ('glibc', '1.23.4')),
|
||||
(b'libc.so.2.4\0libc.so.9\0libc.so.23.1\0', ('libc', '23.1')),
|
||||
(b'musl-1.4.1\0musl-2.1.1\0musl-2.0.1\0', ('musl', '2.1.1')),
|
||||
(
|
||||
b'libc.musl-x86_64.so.1.4.1\0libc.musl-x86_64.so.2.1.1\0libc.musl-x86_64.so.2.0.1',
|
||||
('musl', '2.1.1'),
|
||||
),
|
||||
(
|
||||
b'ld-musl-x86_64.so.1.4.1\0ld-musl-x86_64.so.2.1.1\0ld-musl-x86_64.so.2.0.1',
|
||||
('musl', '2.1.1'),
|
||||
),
|
||||
(b'no match here, so defaults are used', ('test', '100.1.0')),
|
||||
):
|
||||
with open(filename, 'wb') as f:
|
||||
# test match at chunk boundary
|
||||
f.write(b'x'*(chunksize - 10))
|
||||
f.write(data)
|
||||
self.assertEqual(
|
||||
expected,
|
||||
platform.libc_ver(
|
||||
filename,
|
||||
lib='test',
|
||||
version='100.1.0',
|
||||
chunksize=chunksize,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def test_android_ver(self):
|
||||
res = platform.android_ver()
|
||||
@@ -690,5 +758,66 @@ class PlatformTest(unittest.TestCase):
|
||||
self.assertEqual(len(info["SPECIALS"]), 5)
|
||||
|
||||
|
||||
class CommandLineTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
platform.invalidate_caches()
|
||||
self.addCleanup(platform.invalidate_caches)
|
||||
|
||||
def invoke_platform(self, *flags):
|
||||
output = io.StringIO()
|
||||
with contextlib.redirect_stdout(output):
|
||||
platform._main(args=flags)
|
||||
return output.getvalue()
|
||||
|
||||
def test_unknown_flag(self):
|
||||
with self.assertRaises(SystemExit):
|
||||
output = io.StringIO()
|
||||
# suppress argparse error message
|
||||
with contextlib.redirect_stderr(output):
|
||||
_ = self.invoke_platform('--unknown')
|
||||
self.assertStartsWith(output, "usage: ")
|
||||
|
||||
def test_invocation(self):
|
||||
flags = (
|
||||
"--terse", "--nonaliased", "terse", "nonaliased"
|
||||
)
|
||||
|
||||
for r in range(len(flags) + 1):
|
||||
for combination in itertools.combinations(flags, r):
|
||||
self.invoke_platform(*combination)
|
||||
|
||||
def test_arg_parsing(self):
|
||||
# For backwards compatibility, the `aliased` and `terse` parameters are
|
||||
# computed based on a combination of positional arguments and flags.
|
||||
#
|
||||
# Test that the arguments are correctly passed to the underlying
|
||||
# `platform.platform()` call.
|
||||
options = (
|
||||
(["--nonaliased"], False, False),
|
||||
(["nonaliased"], False, False),
|
||||
(["--terse"], True, True),
|
||||
(["terse"], True, True),
|
||||
(["nonaliased", "terse"], False, True),
|
||||
(["--nonaliased", "terse"], False, True),
|
||||
(["--terse", "nonaliased"], False, True),
|
||||
)
|
||||
|
||||
for flags, aliased, terse in options:
|
||||
with self.subTest(flags=flags, aliased=aliased, terse=terse):
|
||||
with mock.patch.object(platform, 'platform') as obj:
|
||||
self.invoke_platform(*flags)
|
||||
obj.assert_called_once_with(aliased, terse)
|
||||
|
||||
@support.force_not_colorized
|
||||
def test_help(self):
|
||||
output = io.StringIO()
|
||||
|
||||
with self.assertRaises(SystemExit):
|
||||
with contextlib.redirect_stdout(output):
|
||||
platform._main(args=["--help"])
|
||||
|
||||
self.assertStartsWith(output.getvalue(), "usage:")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user