3 from types import FunctionType
6 from eventlet import greenio
7 from eventlet import patcher
8 from eventlet.green import select, threading, time
9 from eventlet.support import six
12 to_patch = [('select', select), ('threading', threading), ('time', time)]
14 if sys.version_info > (3, 4):
15 from eventlet.green import selectors
16 to_patch.append(('selectors', selectors))
18 patcher.inject('subprocess', globals(), *to_patch)
19 subprocess_orig = __import__("subprocess")
22 if getattr(subprocess_orig, 'TimeoutExpired', None) is None:
23 # Backported from Python 3.3.
24 # https://bitbucket.org/eventlet/eventlet/issue/89
25 class TimeoutExpired(Exception):
26 """This exception is raised when the timeout expires while waiting for
30 def __init__(self, cmd, timeout, output=None):
32 self.timeout = timeout
36 return ("Command '%s' timed out after %s seconds" %
37 (self.cmd, self.timeout))
40 # This is the meat of this module, the green version of Popen.
41 class Popen(subprocess_orig.Popen):
42 """eventlet-friendly version of subprocess.Popen"""
43 # We do not believe that Windows pipes support non-blocking I/O. At least,
44 # the Python file objects stored on our base-class object have no
45 # setblocking() method, and the Python fcntl module doesn't exist on
46 # Windows. (see eventlet.greenio.set_nonblocking()) As the sole purpose of
47 # this __init__() override is to wrap the pipes for eventlet-friendly
48 # non-blocking I/O, don't even bother overriding it on Windows.
49 if not subprocess_orig.mswindows:
50 def __init__(self, args, bufsize=0, *argss, **kwds):
52 # Forward the call to base-class constructor
53 subprocess_orig.Popen.__init__(self, args, 0, *argss, **kwds)
54 # Now wrap the pipes, if any. This logic is loosely borrowed from
55 # eventlet.processes.Process.run() method.
56 for attr in "stdin", "stdout", "stderr":
57 pipe = getattr(self, attr)
58 if pipe is not None and not type(pipe) == greenio.GreenPipe:
59 wrapped_pipe = greenio.GreenPipe(pipe, pipe.mode, bufsize)
60 setattr(self, attr, wrapped_pipe)
61 __init__.__doc__ = subprocess_orig.Popen.__init__.__doc__
63 def wait(self, timeout=None, check_interval=0.01):
64 # Instead of a blocking OS call, this version of wait() uses logic
65 # borrowed from the eventlet 0.2 processes.Process.wait() method.
66 if timeout is not None:
67 endtime = time.time() + timeout
71 if status is not None:
73 if timeout is not None and time.time() > endtime:
74 raise TimeoutExpired(self.args, timeout)
75 eventlet.sleep(check_interval)
77 if e.errno == errno.ECHILD:
78 # no child process, this happens if the child process
79 # already died and has been cleaned up
83 wait.__doc__ = subprocess_orig.Popen.wait.__doc__
85 if not subprocess_orig.mswindows:
86 # don't want to rewrite the original _communicate() method, we
87 # just want a version that uses eventlet.green.select.select()
88 # instead of select.select().
89 _communicate = FunctionType(
90 six.get_function_code(six.get_unbound_function(
91 subprocess_orig.Popen._communicate)),
94 _communicate_with_select = FunctionType(
95 six.get_function_code(six.get_unbound_function(
96 subprocess_orig.Popen._communicate_with_select)),
98 _communicate_with_poll = FunctionType(
99 six.get_function_code(six.get_unbound_function(
100 subprocess_orig.Popen._communicate_with_poll)),
102 except AttributeError:
105 # Borrow subprocess.call() and check_call(), but patch them so they reference
106 # OUR Popen class rather than subprocess.Popen.
107 call = FunctionType(six.get_function_code(subprocess_orig.call), globals())
108 check_call = FunctionType(six.get_function_code(subprocess_orig.check_call), globals())