Skip to content

Commit 518835f

Browse files
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>
1 parent 6a5bf15 commit 518835f

File tree

3 files changed

+96
-86
lines changed

3 files changed

+96
-86
lines changed

Lib/platform.py

+93-79
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,9 @@
116116
import os
117117
import re
118118
import sys
119+
import subprocess
120+
import functools
121+
import itertools
119122

120123
### Globals & Constants
121124

@@ -600,22 +603,6 @@ def _follow_symlinks(filepath):
600603
os.path.join(os.path.dirname(filepath), os.readlink(filepath)))
601604
return filepath
602605

603-
def _syscmd_uname(option, default=''):
604-
605-
""" Interface to the system's uname command.
606-
"""
607-
if sys.platform in ('dos', 'win32', 'win16'):
608-
# XXX Others too ?
609-
return default
610-
611-
import subprocess
612-
try:
613-
output = subprocess.check_output(('uname', option),
614-
stderr=subprocess.DEVNULL,
615-
text=True)
616-
except (OSError, subprocess.CalledProcessError):
617-
return default
618-
return (output.strip() or default)
619606

620607
def _syscmd_file(target, default=''):
621608

@@ -736,13 +723,89 @@ def architecture(executable=sys.executable, bits='', linkage=''):
736723

737724
return bits, linkage
738725

726+
727+
def _get_machine_win32():
728+
# Try to use the PROCESSOR_* environment variables
729+
# available on Win XP and later; see
730+
# http://support.microsoft.com/kb/888731 and
731+
# http://www.geocities.com/rick_lively/MANUALS/ENV/MSWIN/PROCESSI.HTM
732+
733+
# WOW64 processes mask the native architecture
734+
return (
735+
os.environ.get('PROCESSOR_ARCHITEW6432', '') or
736+
os.environ.get('PROCESSOR_ARCHITECTURE', '')
737+
)
738+
739+
740+
class _Processor:
741+
@classmethod
742+
def get(cls):
743+
func = getattr(cls, f'get_{sys.platform}', cls.from_subprocess)
744+
return func() or ''
745+
746+
def get_win32():
747+
return os.environ.get('PROCESSOR_IDENTIFIER', _get_machine_win32())
748+
749+
def get_OpenVMS():
750+
try:
751+
import vms_lib
752+
except ImportError:
753+
pass
754+
else:
755+
csid, cpu_number = vms_lib.getsyi('SYI$_CPU', 0)
756+
return 'Alpha' if cpu_number >= 128 else 'VAX'
757+
758+
def from_subprocess():
759+
"""
760+
Fall back to `uname -p`
761+
"""
762+
try:
763+
return subprocess.check_output(
764+
['uname', '-p'],
765+
stderr=subprocess.DEVNULL,
766+
text=True,
767+
).strip()
768+
except (OSError, subprocess.CalledProcessError):
769+
pass
770+
771+
772+
def _unknown_as_blank(val):
773+
return '' if val == 'unknown' else val
774+
775+
739776
### Portable uname() interface
740777

741-
uname_result = collections.namedtuple("uname_result",
742-
"system node release version machine processor")
778+
class uname_result(
779+
collections.namedtuple(
780+
"uname_result_base",
781+
"system node release version machine")
782+
):
783+
"""
784+
A uname_result that's largely compatible with a
785+
simple namedtuple except that 'platform' is
786+
resolved late and cached to avoid calling "uname"
787+
except when needed.
788+
"""
789+
790+
@functools.cached_property
791+
def processor(self):
792+
return _unknown_as_blank(_Processor.get())
793+
794+
def __iter__(self):
795+
return itertools.chain(
796+
super().__iter__(),
797+
(self.processor,)
798+
)
799+
800+
def __getitem__(self, key):
801+
if key == 5:
802+
return self.processor
803+
return super().__getitem__(key)
804+
743805

744806
_uname_cache = None
745807

808+
746809
def uname():
747810

748811
""" Fairly portable uname interface. Returns a tuple
@@ -756,52 +819,30 @@ def uname():
756819
757820
"""
758821
global _uname_cache
759-
no_os_uname = 0
760822

761823
if _uname_cache is not None:
762824
return _uname_cache
763825

764-
processor = ''
765-
766826
# Get some infos from the builtin os.uname API...
767827
try:
768-
system, node, release, version, machine = os.uname()
828+
system, node, release, version, machine = infos = os.uname()
769829
except AttributeError:
770-
no_os_uname = 1
771-
772-
if no_os_uname or not list(filter(None, (system, node, release, version, machine))):
773-
# Hmm, no there is either no uname or uname has returned
774-
#'unknowns'... we'll have to poke around the system then.
775-
if no_os_uname:
776-
system = sys.platform
777-
release = ''
778-
version = ''
779-
node = _node()
780-
machine = ''
830+
system = sys.platform
831+
node = _node()
832+
release = version = machine = ''
833+
infos = ()
781834

782-
use_syscmd_ver = 1
835+
if not any(infos):
836+
# uname is not available
783837

784838
# Try win32_ver() on win32 platforms
785839
if system == 'win32':
786840
release, version, csd, ptype = win32_ver()
787-
if release and version:
788-
use_syscmd_ver = 0
789-
# Try to use the PROCESSOR_* environment variables
790-
# available on Win XP and later; see
791-
# http://support.microsoft.com/kb/888731 and
792-
# http://www.geocities.com/rick_lively/MANUALS/ENV/MSWIN/PROCESSI.HTM
793-
if not machine:
794-
# WOW64 processes mask the native architecture
795-
if "PROCESSOR_ARCHITEW6432" in os.environ:
796-
machine = os.environ.get("PROCESSOR_ARCHITEW6432", '')
797-
else:
798-
machine = os.environ.get('PROCESSOR_ARCHITECTURE', '')
799-
if not processor:
800-
processor = os.environ.get('PROCESSOR_IDENTIFIER', machine)
841+
machine = machine or _get_machine_win32()
801842

802843
# Try the 'ver' system command available on some
803844
# platforms
804-
if use_syscmd_ver:
845+
if not (release and version):
805846
system, release, version = _syscmd_ver(system)
806847
# Normalize system to what win32_ver() normally returns
807848
# (_syscmd_ver() tends to return the vendor name as well)
@@ -841,42 +882,15 @@ def uname():
841882
if not release or release == '0':
842883
release = version
843884
version = ''
844-
# Get processor information
845-
try:
846-
import vms_lib
847-
except ImportError:
848-
pass
849-
else:
850-
csid, cpu_number = vms_lib.getsyi('SYI$_CPU', 0)
851-
if (cpu_number >= 128):
852-
processor = 'Alpha'
853-
else:
854-
processor = 'VAX'
855-
if not processor:
856-
# Get processor information from the uname system command
857-
processor = _syscmd_uname('-p', '')
858-
859-
#If any unknowns still exist, replace them with ''s, which are more portable
860-
if system == 'unknown':
861-
system = ''
862-
if node == 'unknown':
863-
node = ''
864-
if release == 'unknown':
865-
release = ''
866-
if version == 'unknown':
867-
version = ''
868-
if machine == 'unknown':
869-
machine = ''
870-
if processor == 'unknown':
871-
processor = ''
872885

873886
# normalize name
874887
if system == 'Microsoft' and release == 'Windows':
875888
system = 'Windows'
876889
release = 'Vista'
877890

878-
_uname_cache = uname_result(system, node, release, version,
879-
machine, processor)
891+
vals = system, node, release, version, machine
892+
# Replace 'unknown' values with the more portable ''
893+
_uname_cache = uname_result(*map(_unknown_as_blank, vals))
880894
return _uname_cache
881895

882896
### Direct interfaces to some of the uname() return values

Lib/test/test_platform.py

+2-7
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import sys
55
import unittest
66
import collections
7-
import contextlib
87
from unittest import mock
98

109
from test import support
@@ -168,12 +167,8 @@ def test_uname_processor(self):
168167
On some systems, the processor must match the output
169168
of 'uname -p'. See Issue 35967 for rationale.
170169
"""
171-
with contextlib.suppress(subprocess.CalledProcessError):
172-
expect = subprocess.check_output(['uname', '-p'], text=True).strip()
173-
174-
if expect == 'unknown':
175-
expect = ''
176-
170+
proc_res = subprocess.check_output(['uname', '-p'], text=True).strip()
171+
expect = platform._unknown_as_blank(proc_res)
177172
self.assertEqual(platform.uname().processor, expect)
178173

179174
@unittest.skipUnless(sys.platform.startswith('win'), "windows only test")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
In platform, delay the invocation of 'uname -p' until the processor attribute is requested.

0 commit comments

Comments
 (0)