Index: subprocess2.py |
diff --git a/subprocess2.py b/subprocess2.py |
index 1f9c6c99e365ad8a958acae8511d10d0b99aaaf0..e81798613cf6946789841df1dd4fce44417d9897 100644 |
--- a/subprocess2.py |
+++ b/subprocess2.py |
@@ -175,6 +175,7 @@ class Popen(subprocess.Popen): |
self.stdin_is_void = False |
self.stdout_is_void = False |
self.stderr_is_void = False |
+ self.cmd_str = tmp_str |
if kwargs.get('stdin') is VOID: |
kwargs['stdin'] = open(os.devnull, 'r') |
@@ -190,6 +191,7 @@ class Popen(subprocess.Popen): |
self.start = time.time() |
self.timeout = None |
+ self.nag_timer = None |
self.shell = kwargs.get('shell', None) |
# Silence pylint on MacOSX |
self.returncode = None |
@@ -228,6 +230,8 @@ class Popen(subprocess.Popen): |
# because of memory exhaustion. |
queue = Queue.Queue() |
done = threading.Event() |
+ timer = [] |
+ last_output = [time.time()] * 2 |
def write_stdin(): |
try: |
@@ -249,10 +253,28 @@ class Popen(subprocess.Popen): |
data = pipe.read(1) |
if not data: |
break |
+ last_output[0] = time.time() |
queue.put((name, data)) |
finally: |
queue.put(name) |
+ def nag_fn(): |
+ now = time.time() |
+ if done.is_set(): |
+ return |
+ if last_output[0] == last_output[1]: |
+ logging.warn(' No output for %.0f seconds from command:' % ( |
+ now - last_output[1])) |
+ logging.warn(' %s' % self.cmd_str) |
+ # Use 0.1 fudge factor in case: |
+ # now ~= last_output[0] + self.nag_timer |
+ sleep_time = self.nag_timer + last_output[0] - now - 0.1 |
+ while sleep_time < 0: |
+ sleep_time += self.nag_timer |
+ last_output[1] = last_output[0] |
+ timer[0] = threading.Timer(sleep_time, nag_fn) |
+ timer[0].start() |
+ |
def timeout_fn(): |
try: |
done.wait(self.timeout) |
@@ -290,6 +312,10 @@ class Popen(subprocess.Popen): |
for t in threads.itervalues(): |
t.start() |
+ if self.nag_timer: |
+ timer.append(threading.Timer(self.nag_timer, nag_fn)) |
+ timer[0].start() |
+ |
timed_out = False |
try: |
# This thread needs to be optimized for speed. |
@@ -313,6 +339,8 @@ class Popen(subprocess.Popen): |
finally: |
# Stop the threads. |
done.set() |
+ if timer: |
+ timer[0].cancel() |
if 'wait' in threads: |
# Accelerate things, otherwise it would hang until the child process is |
# done. |
@@ -324,16 +352,21 @@ class Popen(subprocess.Popen): |
if timed_out: |
self.returncode = TIMED_OUT |
- def communicate(self, input=None, timeout=None): # pylint: disable=W0221,W0622 |
+ # pylint: disable=W0221,W0622 |
+ def communicate(self, input=None, timeout=None, nag_timer=None): |
"""Adds timeout and callbacks support. |
Returns (stdout, stderr) like subprocess.Popen().communicate(). |
- The process will be killed after |timeout| seconds and returncode set to |
TIMED_OUT. |
+ - If the subprocess runs for |nag_timer| seconds without producing terminal |
+ output, print a warning to stderr. |
""" |
self.timeout = timeout |
- if not self.timeout and not self.stdout_cb and not self.stderr_cb: |
+ self.nag_timer = nag_timer |
+ if (not self.timeout and not self.nag_timer and |
+ not self.stdout_cb and not self.stderr_cb): |
return super(Popen, self).communicate(input) |
if self.timeout and self.shell: |
@@ -360,13 +393,15 @@ class Popen(subprocess.Popen): |
return (stdout, stderr) |
-def communicate(args, timeout=None, **kwargs): |
+def communicate(args, timeout=None, nag_timer=None, **kwargs): |
"""Wraps subprocess.Popen().communicate() and add timeout support. |
Returns ((stdout, stderr), returncode). |
- The process will be killed after |timeout| seconds and returncode set to |
TIMED_OUT. |
+ - If the subprocess runs for |nag_timer| seconds without producing terminal |
+ output, print a warning to stderr. |
- Automatically passes stdin content as input so do not specify stdin=PIPE. |
""" |
stdin = kwargs.pop('stdin', None) |
@@ -381,9 +416,9 @@ def communicate(args, timeout=None, **kwargs): |
proc = Popen(args, **kwargs) |
if stdin: |
- return proc.communicate(stdin, timeout), proc.returncode |
+ return proc.communicate(stdin, timeout, nag_timer), proc.returncode |
else: |
- return proc.communicate(None, timeout), proc.returncode |
+ return proc.communicate(None, timeout, nag_timer), proc.returncode |
def call(args, **kwargs): |