Chromium Code Reviews| Index: subprocess2.py |
| diff --git a/subprocess2.py b/subprocess2.py |
| index 1f9c6c99e365ad8a958acae8511d10d0b99aaaf0..c8f47e2bcfd6cf702a89aab1fe4bb74ae00a16a7 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 = 0 |
| 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 > 0: |
| + 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=0): |
|
M-A Ruel
2013/05/02 00:34:19
I prefer None to mean it's disabled.
|
| """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=0, **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): |