summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Shepelev <temotor@gmail.com>2013-06-04 17:17:44 +0400
committerSergey Shepelev <temotor@gmail.com>2013-06-07 12:35:34 +0400
commit3c8440209d26bf22d3a4884a19f3e1967797d4cb (patch)
treeb44140b142ad5039237ce8a8e4d7339258a036ce
parent447849bb4410aeaca69d959ae5e4ab03b288856a (diff)
downloadeventlet-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.py29
-rw-r--r--tests/subprocess_test.py25
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'