summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJason R. Coombs <jaraco@jaraco.com>2020-04-16 12:28:09 (GMT)
committerGitHub <noreply@github.com>2020-04-16 12:28:09 (GMT)
commit518835f3354d6672e61c9f52348c1e4a2533ea00 (patch)
tree870820aa95948c58a6616795f266c290f0432a65
parent6a5bf15c71a1c101c28774ae714b58e8a65b130c (diff)
downloadcpython-518835f3354d6672e61c9f52348c1e4a2533ea00.zip
cpython-518835f3354d6672e61c9f52348c1e4a2533ea00.tar.gz
cpython-518835f3354d6672e61c9f52348c1e4a2533ea00.tar.bz2
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>
-rwxr-xr-xLib/platform.py172
-rw-r--r--Lib/test/test_platform.py9
-rw-r--r--Misc/NEWS.d/next/Library/2019-04-14-14-11-07.bpo-35967.KUMT9E.rst1
3 files changed, 96 insertions, 86 deletions
diff --git a/Lib/platform.py b/Lib/platform.py
index ed41edc..3f442ef 100755
--- a/Lib/platform.py
+++ b/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 = ''
+ system = sys.platform
+ node = _node()
+ 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
diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py
index 63215a0..855304a 100644
--- a/Lib/test/test_platform.py
+++ b/Lib/test/test_platform.py
@@ -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")
diff --git a/Misc/NEWS.d/next/Library/2019-04-14-14-11-07.bpo-35967.KUMT9E.rst b/Misc/NEWS.d/next/Library/2019-04-14-14-11-07.bpo-35967.KUMT9E.rst
new file mode 100644
index 0000000..38bec77
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2019-04-14-14-11-07.bpo-35967.KUMT9E.rst
@@ -0,0 +1 @@
+In platform, delay the invocation of 'uname -p' until the processor attribute is requested. \ No newline at end of file