-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Work around missing subprocess members on Google App Engine #1825
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
0755aa6
52c075f
b29c93c
a117e19
ea342b6
f41000d
888fa20
03cddc8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
""" | ||
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). | ||
|
||
Instead of importing subprocess, other modules should use this as follows: | ||
|
||
from matplotlib.compat import subprocess | ||
|
||
This module is safe to import from anywhere within matplotlib. | ||
""" | ||
|
||
from __future__ import absolute_import # Required to import subprocess | ||
from __future__ import print_function | ||
|
||
import subprocess | ||
|
||
__all__ = ['Popen', 'PIPE', 'STDOUT', 'check_output'] | ||
|
||
|
||
if hasattr(subprocess, 'Popen'): | ||
Popen = subprocess.Popen | ||
# Assume that it also has the other constants. | ||
PIPE = subprocess.PIPE | ||
STDOUT = subprocess.STDOUT | ||
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") | ||
PIPE = -1 | ||
STDOUT = -2 | ||
|
||
|
||
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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
import subprocess | ||
import sys | ||
from matplotlib.compat import subprocess | ||
|
||
|
||
class MiniExpect: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,7 +41,6 @@ | |
import os | ||
import shutil | ||
import sys | ||
from subprocess import Popen, PIPE, STDOUT | ||
|
||
from hashlib import md5 | ||
|
||
|
@@ -51,6 +50,7 @@ | |
from matplotlib import rcParams | ||
from matplotlib._png import read_png | ||
from matplotlib.cbook import mkdirs | ||
from matplotlib.compat.subprocess import Popen, PIPE, STDOUT | ||
import matplotlib.dviread as dviread | ||
import re | ||
|
||
|
@@ -63,8 +63,12 @@ | |
|
||
|
||
def dvipng_hack_alpha(): | ||
p = Popen('dvipng -version', shell=True, stdin=PIPE, stdout=PIPE, | ||
stderr=STDOUT, close_fds=(sys.platform != 'win32')) | ||
try: | ||
p = Popen('dvipng -version', stdin=PIPE, stdout=PIPE, stderr=STDOUT, | ||
close_fds=(sys.platform != 'win32')) | ||
except OSError: | ||
mpl.verbose.report('No dvipng was found', 'helpful') | ||
return False | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this a good idea? Previously an exception would have been raised, now False is returned. What impact does that have? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm happy to back off on this since it doesn't block texmanager from being imported, and it won't work anyway if dvipng is missing. I don't think There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did you want me to revert this, or document it, or just leave as-is? |
||
stdin, stdout = p.stdin, p.stdout | ||
for line in stdout: | ||
if line.startswith(b'dvipng '): | ||
|
@@ -74,7 +78,7 @@ def dvipng_hack_alpha(): | |
version = version.decode('ascii') | ||
version = distutils.version.LooseVersion(version) | ||
return version < distutils.version.LooseVersion('1.6') | ||
mpl.verbose.report('No dvipng was found', 'helpful') | ||
mpl.verbose.report('Unexpected response from dvipng -version', 'helpful') | ||
return False | ||
|
||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm tempted to suggest a RuntimeError here. None-the-less, this is an API change which should be documented in
docs/api/api_changes.rst
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. Note that I am having trouble building the docs locally, so I'm hoping my ReST syntax is correct!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, I agree
RuntimeError
is more appropriate (sinceNotImplementedError
implies that it is planning to be fixed one day), but I changed this to be compatible with the Windows implementation. Happy to change it if you want. For what it's worth,NotImplementedError
is a subclass ofRuntimeError
.