bpo-35346, platform: replace os.popen() with subprocess (GH-10786)

Replace os.popen() with subprocess.check_output() in the platform module:

* platform.uname() (its _syscmd_ver() helper function) now redirects
  stderr to DEVNULL.
* Remove platform.DEV_NULL.
* _syscmd_uname() and _syscmd_file() no longer catch AttributeError.
  The "except AttributeError:" was only needed in Python 2, when
  os.popen() was not always available. In Python 3,
  subprocess.check_output() is always available.
This commit is contained in:
Victor Stinner 2018-12-07 11:10:33 +01:00 committed by GitHub
parent 9ebe8794f0
commit 3a521f0b61
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 30 additions and 48 deletions

View File

@ -119,19 +119,6 @@ import sys
### Globals & Constants ### Globals & Constants
# Determine the platform's /dev/null device
try:
DEV_NULL = os.devnull
except AttributeError:
# os.devnull was added in Python 2.4, so emulate it for earlier
# Python versions
if sys.platform in ('dos', 'win32', 'win16'):
# Use the old CP/M NUL as device name
DEV_NULL = 'NUL'
else:
# Standard Unix uses /dev/null
DEV_NULL = '/dev/null'
# Helper for comparing two version number strings. # Helper for comparing two version number strings.
# Based on the description of the PHP's version_compare(): # Based on the description of the PHP's version_compare():
# http://php.net/manual/en/function.version-compare.php # http://php.net/manual/en/function.version-compare.php
@ -288,16 +275,15 @@ def _syscmd_ver(system='', release='', version='',
return system, release, version return system, release, version
# Try some common cmd strings # Try some common cmd strings
import subprocess
for cmd in ('ver', 'command /c ver', 'cmd /c ver'): for cmd in ('ver', 'command /c ver', 'cmd /c ver'):
try: try:
pipe = os.popen(cmd) info = subprocess.check_output(cmd,
info = pipe.read() stderr=subprocess.DEVNULL,
if pipe.close(): text=True,
raise OSError('command failed') shell=True)
# XXX How can I suppress shell errors from being written except (OSError, subprocess.CalledProcessError) as why:
# to stderr ? #print('Command %s failed: %s' % (cmd, why))
except OSError as why:
#print 'Command %s failed: %s' % (cmd, why)
continue continue
else: else:
break break
@ -602,16 +588,15 @@ def _syscmd_uname(option, default=''):
if sys.platform in ('dos', 'win32', 'win16'): if sys.platform in ('dos', 'win32', 'win16'):
# XXX Others too ? # XXX Others too ?
return default return default
import subprocess
try: try:
f = os.popen('uname %s 2> %s' % (option, DEV_NULL)) output = subprocess.check_output(('uname', option),
except (AttributeError, OSError): stderr=subprocess.DEVNULL,
text=True)
except (OSError, subprocess.CalledProcessError):
return default return default
output = f.read().strip() return (output.strip() or default)
rc = f.close()
if not output or rc:
return default
else:
return output
def _syscmd_file(target, default=''): def _syscmd_file(target, default=''):
@ -629,17 +614,12 @@ def _syscmd_file(target, default=''):
import subprocess import subprocess
target = _follow_symlinks(target) target = _follow_symlinks(target)
try: try:
proc = subprocess.Popen(['file', target], output = subprocess.check_output(['file', target],
stdout=subprocess.PIPE, stderr=subprocess.DEVNULL,
stderr=subprocess.STDOUT) encoding='latin-1')
except (AttributeError, OSError): except (OSError, subprocess.CalledProcessError):
return default return default
output = proc.communicate()[0].decode('latin-1') return (output or default)
rc = proc.wait()
if not output or rc:
return default
else:
return output
### Information about the used architecture ### Information about the used architecture

View File

@ -222,16 +222,16 @@ class PlatformTest(unittest.TestCase):
res = platform.mac_ver() res = platform.mac_ver()
if platform.uname().system == 'Darwin': if platform.uname().system == 'Darwin':
# We're on a MacOSX system, check that # We are on a macOS system, check that the right version
# the right version information is returned # information is returned
fd = os.popen('sw_vers', 'r') output = subprocess.check_output(['sw_vers'], text=True)
real_ver = None for line in output.splitlines():
for ln in fd: if line.startswith('ProductVersion:'):
if ln.startswith('ProductVersion:'): real_ver = line.strip().split()[-1]
real_ver = ln.strip().split()[-1]
break break
fd.close() else:
self.assertFalse(real_ver is None) self.fail(f"failed to parse sw_vers output: {output!r}")
result_list = res[0].split('.') result_list = res[0].split('.')
expect_list = real_ver.split('.') expect_list = real_ver.split('.')
len_diff = len(result_list) - len(expect_list) len_diff = len(result_list) - len(expect_list)

View File

@ -0,0 +1,2 @@
:func:`platform.uname` now redirects ``stderr`` to :data:`os.devnull` when
running external programs like ``cmd /c ver``.