Skip to content

Commit 3a521f0

Browse files
authored
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.
1 parent 9ebe879 commit 3a521f0

3 files changed

Lines changed: 30 additions & 48 deletions

File tree

Lib/platform.py

Lines changed: 19 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -119,19 +119,6 @@
119119

120120
### Globals & Constants
121121

122-
# Determine the platform's /dev/null device
123-
try:
124-
DEV_NULL = os.devnull
125-
except AttributeError:
126-
# os.devnull was added in Python 2.4, so emulate it for earlier
127-
# Python versions
128-
if sys.platform in ('dos', 'win32', 'win16'):
129-
# Use the old CP/M NUL as device name
130-
DEV_NULL = 'NUL'
131-
else:
132-
# Standard Unix uses /dev/null
133-
DEV_NULL = '/dev/null'
134-
135122
# Helper for comparing two version number strings.
136123
# Based on the description of the PHP's version_compare():
137124
# http://php.net/manual/en/function.version-compare.php
@@ -288,16 +275,15 @@ def _syscmd_ver(system='', release='', version='',
288275
return system, release, version
289276

290277
# Try some common cmd strings
278+
import subprocess
291279
for cmd in ('ver', 'command /c ver', 'cmd /c ver'):
292280
try:
293-
pipe = os.popen(cmd)
294-
info = pipe.read()
295-
if pipe.close():
296-
raise OSError('command failed')
297-
# XXX How can I suppress shell errors from being written
298-
# to stderr ?
299-
except OSError as why:
300-
#print 'Command %s failed: %s' % (cmd, why)
281+
info = subprocess.check_output(cmd,
282+
stderr=subprocess.DEVNULL,
283+
text=True,
284+
shell=True)
285+
except (OSError, subprocess.CalledProcessError) as why:
286+
#print('Command %s failed: %s' % (cmd, why))
301287
continue
302288
else:
303289
break
@@ -602,16 +588,15 @@ def _syscmd_uname(option, default=''):
602588
if sys.platform in ('dos', 'win32', 'win16'):
603589
# XXX Others too ?
604590
return default
591+
592+
import subprocess
605593
try:
606-
f = os.popen('uname %s 2> %s' % (option, DEV_NULL))
607-
except (AttributeError, OSError):
608-
return default
609-
output = f.read().strip()
610-
rc = f.close()
611-
if not output or rc:
594+
output = subprocess.check_output(('uname', option),
595+
stderr=subprocess.DEVNULL,
596+
text=True)
597+
except (OSError, subprocess.CalledProcessError):
612598
return default
613-
else:
614-
return output
599+
return (output.strip() or default)
615600

616601
def _syscmd_file(target, default=''):
617602

@@ -629,17 +614,12 @@ def _syscmd_file(target, default=''):
629614
import subprocess
630615
target = _follow_symlinks(target)
631616
try:
632-
proc = subprocess.Popen(['file', target],
633-
stdout=subprocess.PIPE,
634-
stderr=subprocess.STDOUT)
635-
except (AttributeError, OSError):
617+
output = subprocess.check_output(['file', target],
618+
stderr=subprocess.DEVNULL,
619+
encoding='latin-1')
620+
except (OSError, subprocess.CalledProcessError):
636621
return default
637-
output = proc.communicate()[0].decode('latin-1')
638-
rc = proc.wait()
639-
if not output or rc:
640-
return default
641-
else:
642-
return output
622+
return (output or default)
643623

644624
### Information about the used architecture
645625

Lib/test/test_platform.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -222,16 +222,16 @@ def test_mac_ver(self):
222222
res = platform.mac_ver()
223223

224224
if platform.uname().system == 'Darwin':
225-
# We're on a MacOSX system, check that
226-
# the right version information is returned
227-
fd = os.popen('sw_vers', 'r')
228-
real_ver = None
229-
for ln in fd:
230-
if ln.startswith('ProductVersion:'):
231-
real_ver = ln.strip().split()[-1]
225+
# We are on a macOS system, check that the right version
226+
# information is returned
227+
output = subprocess.check_output(['sw_vers'], text=True)
228+
for line in output.splitlines():
229+
if line.startswith('ProductVersion:'):
230+
real_ver = line.strip().split()[-1]
232231
break
233-
fd.close()
234-
self.assertFalse(real_ver is None)
232+
else:
233+
self.fail(f"failed to parse sw_vers output: {output!r}")
234+
235235
result_list = res[0].split('.')
236236
expect_list = real_ver.split('.')
237237
len_diff = len(result_list) - len(expect_list)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
:func:`platform.uname` now redirects ``stderr`` to :data:`os.devnull` when
2+
running external programs like ``cmd /c ver``.

0 commit comments

Comments
 (0)