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