diff --git a/lib/matplotlib/compat/subprocess.py b/lib/matplotlib/compat/subprocess.py index 9b5b516a68c2..8cb0f845a650 100644 --- a/lib/matplotlib/compat/subprocess.py +++ b/lib/matplotlib/compat/subprocess.py @@ -1,10 +1,10 @@ """ A replacement wrapper around the subprocess module, with a number of work-arounds: -- Provides the check_output function (which subprocess only provides from Python - 2.7 onwards). - Provides a stub implementation of subprocess members on Google App Engine (which are missing in subprocess). +- Use subprocess32, backport from python 3.2 on Linux/Mac work-around for + https://github.com/matplotlib/matplotlib/issues/5314 Instead of importing subprocess, other modules should use this as follows: @@ -15,8 +15,16 @@ from __future__ import absolute_import # Required to import subprocess from __future__ import print_function - -import subprocess +import os +import sys +if os.name == 'posix' and sys.version_info[:2] < (3, 2): + # work around for https://github.com/matplotlib/matplotlib/issues/5314 + try: + import subprocess32 as subprocess + except ImportError: + import subprocess +else: + import subprocess __all__ = ['Popen', 'PIPE', 'STDOUT', 'check_output', 'CalledProcessError'] @@ -27,55 +35,17 @@ PIPE = subprocess.PIPE STDOUT = subprocess.STDOUT CalledProcessError = subprocess.CalledProcessError + check_output = subprocess.check_output else: # In restricted environments (such as Google App Engine), these are # non-existent. Replace them with dummy versions that always raise OSError. def Popen(*args, **kwargs): raise OSError("subprocess.Popen is not supported") + + def check_output(*args, **kwargs): + raise OSError("subprocess.check_output is not supported") PIPE = -1 STDOUT = -2 # There is no need to catch CalledProcessError. These stubs cannot raise # it. None in an except clause will simply not match any exceptions. CalledProcessError = None - - -def _check_output(*popenargs, **kwargs): - r"""Run command with arguments and return its output as a byte - string. - - If the exit code was non-zero it raises a CalledProcessError. The - CalledProcessError object will have the return code in the - returncode - attribute and output in the output attribute. - - The arguments are the same as for the Popen constructor. Example:: - - >>> check_output(["ls", "-l", "/dev/null"]) - 'crw-rw-rw- 1 root root 1, 3 Oct 18 2007 /dev/null\n' - - The stdout argument is not allowed as it is used internally. - To capture standard error in the result, use stderr=STDOUT.:: - - >>> check_output(["/bin/sh", "-c", - ... "ls -l non_existent_file ; exit 0"], - ... stderr=STDOUT) - 'ls: non_existent_file: No such file or directory\n' - """ - if 'stdout' in kwargs: - raise ValueError('stdout argument not allowed, it will be overridden.') - process = Popen(stdout=PIPE, *popenargs, **kwargs) - output, unused_err = process.communicate() - retcode = process.poll() - if retcode: - cmd = kwargs.get("args") - if cmd is None: - cmd = popenargs[0] - raise subprocess.CalledProcessError(retcode, cmd, output=output) - return output - - -# python2.7's subprocess provides a check_output method -if hasattr(subprocess, 'check_output'): - check_output = subprocess.check_output -else: - check_output = _check_output diff --git a/lib/matplotlib/tests/test_coding_standards.py b/lib/matplotlib/tests/test_coding_standards.py index 529591fddf18..81d30682097e 100644 --- a/lib/matplotlib/tests/test_coding_standards.py +++ b/lib/matplotlib/tests/test_coding_standards.py @@ -217,7 +217,6 @@ def test_pep8_conformance_installed_files(): 'tests/test_subplots.py', 'tests/test_tightlayout.py', 'tests/test_triangulation.py', - 'compat/subprocess.py', 'backends/__init__.py', 'backends/backend_agg.py', 'backends/backend_cairo.py', diff --git a/setup.py b/setup.py index 5fdc01d1a3f1..4b4038a7d306 100644 --- a/setup.py +++ b/setup.py @@ -70,6 +70,7 @@ setupext.Six(), setupext.Dateutil(), setupext.FuncTools32(), + setupext.Subprocess32(), setupext.Pytz(), setupext.Cycler(), setupext.Tornado(), diff --git a/setupext.py b/setupext.py index 514ab795eefa..af4b1989af16 100755 --- a/setupext.py +++ b/setupext.py @@ -1386,8 +1386,8 @@ def check(self): import functools32 except ImportError: return ( - "functools32 was not found. It is required for for" - "python versions prior to 3.2") + "functools32 was not found. It is required for" + "Python versions prior to 3.2") return "using functools32" else: @@ -1400,6 +1400,30 @@ def get_install_requires(self): return [] +class Subprocess32(SetupPackage): + name = "subprocess32" + + def check(self): + if sys.version_info[:2] < (3, 2): + try: + import subprocess32 + except ImportError: + return ( + "subprocess32 was not found. It used " + " for Python versions prior to 3.2 to improves" + " functionality on Linux and OSX") + + return "using subprocess32" + else: + return "Not required" + + def get_install_requires(self): + if sys.version_info[:2] < (3, 2) and os.name == 'posix': + return ['subprocess32'] + else: + return [] + + class Tornado(OptionalPackage): name = "tornado"