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