summaryrefslogtreecommitdiffstats
path: root/Lib/subprocess.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/subprocess.py')
-rw-r--r--Lib/subprocess.py36
1 files changed, 27 insertions, 9 deletions
diff --git a/Lib/subprocess.py b/Lib/subprocess.py
index aed7292..85b9ea0 100644
--- a/Lib/subprocess.py
+++ b/Lib/subprocess.py
@@ -489,11 +489,20 @@ def run(*popenargs,
with Popen(*popenargs, **kwargs) as process:
try:
stdout, stderr = process.communicate(input, timeout=timeout)
- except TimeoutExpired:
+ except TimeoutExpired as exc:
process.kill()
- stdout, stderr = process.communicate()
- raise TimeoutExpired(process.args, timeout, output=stdout,
- stderr=stderr)
+ if _mswindows:
+ # Windows accumulates the output in a single blocking
+ # read() call run on child threads, with the timeout
+ # being done in a join() on those threads. communicate()
+ # _after_ kill() is required to collect that and add it
+ # to the exception.
+ exc.stdout, exc.stderr = process.communicate()
+ else:
+ # POSIX _communicate already populated the output so
+ # far into the TimeoutExpired exception.
+ process.wait()
+ raise
except: # Including KeyboardInterrupt, communicate handled that.
process.kill()
# We don't call process.wait() as .__exit__ does that for us.
@@ -1050,12 +1059,16 @@ class Popen(object):
return endtime - _time()
- def _check_timeout(self, endtime, orig_timeout):
+ def _check_timeout(self, endtime, orig_timeout, stdout_seq, stderr_seq,
+ skip_check_and_raise=False):
"""Convenience for checking if a timeout has expired."""
if endtime is None:
return
- if _time() > endtime:
- raise TimeoutExpired(self.args, orig_timeout)
+ if skip_check_and_raise or _time() > endtime:
+ raise TimeoutExpired(
+ self.args, orig_timeout,
+ output=b''.join(stdout_seq) if stdout_seq else None,
+ stderr=b''.join(stderr_seq) if stderr_seq else None)
def wait(self, timeout=None):
@@ -1843,10 +1856,15 @@ class Popen(object):
while selector.get_map():
timeout = self._remaining_time(endtime)
if timeout is not None and timeout < 0:
- raise TimeoutExpired(self.args, orig_timeout)
+ self._check_timeout(endtime, orig_timeout,
+ stdout, stderr,
+ skip_check_and_raise=True)
+ raise RuntimeError( # Impossible :)
+ '_check_timeout(..., skip_check_and_raise=True) '
+ 'failed to raise TimeoutExpired.')
ready = selector.select(timeout)
- self._check_timeout(endtime, orig_timeout)
+ self._check_timeout(endtime, orig_timeout, stdout, stderr)
# XXX Rewrite these to use non-blocking I/O on the file
# objects; they are no longer using C stdio!