bpo-35967 resolve platform.processor late (GH-12239)
* Replace flag-flip indirection with direct inspection * Use any for simpler code * Avoid flag flip and set results directly. * Resolve processor in a single function. * Extract processor handling into a namespace (class) * Remove _syscmd_uname, unused * Restore platform.processor behavior to match prior expectation (reliant on uname -p in a subprocess). * Extract '_unknown_as_blank' function. * Override uname_result to resolve the processor late. * Add a test intended to capture the expected values from 'uname -p' * Instead of trying to keep track of all of the possible outputs on different systems (probably a fool's errand), simply assert that except for the known platform variance, uname().processor matches the output of 'uname -p' * Use a skipIf directive * Use contextlib.suppress to suppress the error. Inline strip call. * 📜🤖 Added by blurb_it. * Remove use of contextlib.suppress (it would fail with NameError if it had any effect). Rely on _unknown_as_blank to replace unknown with blank. Co-authored-by: blurb-it[bot] <blurb-it[bot]@users.noreply.github.com>
This commit is contained in:
parent
6a5bf15c71
commit
518835f335
168
Lib/platform.py
168
Lib/platform.py
|
@ -116,6 +116,9 @@ import collections
|
|||
import os
|
||||
import re
|
||||
import sys
|
||||
import subprocess
|
||||
import functools
|
||||
import itertools
|
||||
|
||||
### Globals & Constants
|
||||
|
||||
|
@ -600,22 +603,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.
|
||||
"""
|
||||
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=''):
|
||||
|
||||
|
@ -736,13 +723,89 @@ 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 'platform' 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,)
|
||||
)
|
||||
|
||||
def __getitem__(self, key):
|
||||
if key == 5:
|
||||
return self.processor
|
||||
return super().__getitem__(key)
|
||||
|
||||
|
||||
_uname_cache = None
|
||||
|
||||
|
||||
def uname():
|
||||
|
||||
""" Fairly portable uname interface. Returns a tuple
|
||||
|
@ -756,52 +819,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
|
||||
|
||||
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 = ''
|
||||
release = version = machine = ''
|
||||
infos = ()
|
||||
|
||||
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)
|
||||
|
@ -841,42 +882,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
|
||||
|
|
|
@ -4,7 +4,6 @@ import subprocess
|
|||
import sys
|
||||
import unittest
|
||||
import collections
|
||||
import contextlib
|
||||
from unittest import mock
|
||||
|
||||
from test import support
|
||||
|
@ -168,12 +167,8 @@ class PlatformTest(unittest.TestCase):
|
|||
On some systems, the processor must match the output
|
||||
of 'uname -p'. See Issue 35967 for rationale.
|
||||
"""
|
||||
with contextlib.suppress(subprocess.CalledProcessError):
|
||||
expect = subprocess.check_output(['uname', '-p'], text=True).strip()
|
||||
|
||||
if expect == 'unknown':
|
||||
expect = ''
|
||||
|
||||
proc_res = subprocess.check_output(['uname', '-p'], text=True).strip()
|
||||
expect = platform._unknown_as_blank(proc_res)
|
||||
self.assertEqual(platform.uname().processor, expect)
|
||||
|
||||
@unittest.skipUnless(sys.platform.startswith('win'), "windows only test")
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
In platform, delay the invocation of 'uname -p' until the processor attribute is requested.
|
Loading…
Reference in New Issue