[Python-checkins] bpo-35967 resolve platform.processor late (GH-12239)

Jason R. Coombs webhook-mailer at python.org
Thu Apr 16 08:28:17 EDT 2020


https://github.com/python/cpython/commit/518835f3354d6672e61c9f52348c1e4a2533ea00
commit: 518835f3354d6672e61c9f52348c1e4a2533ea00
branch: master
author: Jason R. Coombs <jaraco at jaraco.com>
committer: GitHub <noreply at github.com>
date: 2020-04-16T08:28:09-04:00
summary:

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>

files:
A Misc/NEWS.d/next/Library/2019-04-14-14-11-07.bpo-35967.KUMT9E.rst
M Lib/platform.py
M Lib/test/test_platform.py

diff --git a/Lib/platform.py b/Lib/platform.py
index ed41edc98fe08..3f442ef0fbb65 100755
--- a/Lib/platform.py
+++ b/Lib/platform.py
@@ -116,6 +116,9 @@
 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 63215a06358b6..855304a68c204 100644
--- a/Lib/test/test_platform.py
+++ b/Lib/test/test_platform.py
@@ -4,7 +4,6 @@
 import sys
 import unittest
 import collections
-import contextlib
 from unittest import mock
 
 from test import support
@@ -168,12 +167,8 @@ def test_uname_processor(self):
         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 0000000000000..38bec77313ac0
--- /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



More information about the Python-checkins mailing list