diff options
| author | Sergey Shepelev <temotor@gmail.com> | 2013-06-04 17:17:44 +0400 |
|---|---|---|
| committer | Sergey Shepelev <temotor@gmail.com> | 2013-06-07 12:35:34 +0400 |
| commit | 3c8440209d26bf22d3a4884a19f3e1967797d4cb (patch) | |
| tree | b44140b142ad5039237ce8a8e4d7339258a036ce | |
| parent | 447849bb4410aeaca69d959ae5e4ab03b288856a (diff) | |
| download | eventlet-3c8440209d26bf22d3a4884a19f3e1967797d4cb.tar.gz | |
green: subprocess: Popen.wait accepts new `timeout` kwarg
As in Python 3.3 http://docs.python.org/3.3/library/subprocess.html#subprocess.Popen.wait
https://bitbucket.org/eventlet/eventlet/issue/89/add-a-timeout-argument-to-subprocesspopen
https://bitbucket.org/eventlet/eventlet/pull-request/30
| -rw-r--r-- | eventlet/green/subprocess.py | 29 | ||||
| -rw-r--r-- | tests/subprocess_test.py | 25 |
2 files changed, 51 insertions, 3 deletions
diff --git a/eventlet/green/subprocess.py b/eventlet/green/subprocess.py index c1cc9b3..4e93836 100644 --- a/eventlet/green/subprocess.py +++ b/eventlet/green/subprocess.py @@ -1,5 +1,6 @@ import errno import new +import time import eventlet from eventlet import greenio @@ -10,6 +11,23 @@ from eventlet.green import select patcher.inject('subprocess', globals(), ('select', select)) subprocess_orig = __import__("subprocess") + +if getattr(subprocess_orig, 'TimeoutExpired', None) is None: + # Backported from Python 3.3. + # https://bitbucket.org/eventlet/eventlet/issue/89 + class TimeoutExpired(Exception): + """This exception is raised when the timeout expires while waiting for + a child process. + """ + def __init__(self, cmd, output=None): + self.cmd = cmd + self.output = output + + def __str__(self): + return ("Command '%s' timed out after %s seconds" % + (self.cmd, self.timeout)) + + # This is the meat of this module, the green version of Popen. class Popen(subprocess_orig.Popen): """eventlet-friendly version of subprocess.Popen""" @@ -21,9 +39,10 @@ class Popen(subprocess_orig.Popen): # non-blocking I/O, don't even bother overriding it on Windows. if not subprocess_orig.mswindows: def __init__(self, args, bufsize=0, *argss, **kwds): + self.args = args # Forward the call to base-class constructor subprocess_orig.Popen.__init__(self, args, 0, *argss, **kwds) - # Now wrap the pipes, if any. This logic is loosely borrowed from + # Now wrap the pipes, if any. This logic is loosely borrowed from # eventlet.processes.Process.run() method. for attr in "stdin", "stdout", "stderr": pipe = getattr(self, attr) @@ -32,14 +51,18 @@ class Popen(subprocess_orig.Popen): setattr(self, attr, wrapped_pipe) __init__.__doc__ = subprocess_orig.Popen.__init__.__doc__ - def wait(self, check_interval=0.01): + def wait(self, timeout=None, check_interval=0.01): # Instead of a blocking OS call, this version of wait() uses logic # borrowed from the eventlet 0.2 processes.Process.wait() method. + if timeout is not None: + endtime = time.time() + timeout try: while True: status = self.poll() if status is not None: return status + if timeout is not None and time.time() > endtime: + raise TimeoutExpired(self.args) eventlet.sleep(check_interval) except OSError, e: if e.errno == errno.ECHILD: @@ -52,7 +75,7 @@ class Popen(subprocess_orig.Popen): if not subprocess_orig.mswindows: # don't want to rewrite the original _communicate() method, we - # just want a version that uses eventlet.green.select.select() + # just want a version that uses eventlet.green.select.select() # instead of select.select(). try: _communicate = new.function(subprocess_orig.Popen._communicate.im_func.func_code, diff --git a/tests/subprocess_test.py b/tests/subprocess_test.py new file mode 100644 index 0000000..b323ffa --- /dev/null +++ b/tests/subprocess_test.py @@ -0,0 +1,25 @@ +import eventlet +from eventlet.green import subprocess +import eventlet.patcher +import os +import sys +import time +original_subprocess = eventlet.patcher.original('subprocess') + + +def test_subprocess_wait(): + # https://bitbucket.org/eventlet/eventlet/issue/89 + # In Python 3.3 subprocess.Popen.wait() method acquired `timeout` + # argument. + # RHEL backported it to their Python 2.6 package. + p = subprocess.Popen([sys.executable, + "-c", "import time; time.sleep(0.5)"]) + ok = False + t1 = time.time() + try: + p.wait(timeout=0.1) + except subprocess.TimeoutExpired: + ok = True + tdiff = time.time() - t1 + assert ok == True, 'did not raise subprocess.TimeoutExpired' + assert 0.1 <= tdiff <= 0.2, 'did not stop within allowed time' |
