diff options
author | Gregory P. Smith <greg@krypto.org> | 2019-09-11 09:23:05 (GMT) |
---|---|---|
committer | T. Wouters <thomas@python.org> | 2019-09-11 09:23:05 (GMT) |
commit | 580d2782f70f8e0bed7ec20abb03d740cb83b5da (patch) | |
tree | 28962e4e84a383c7d5c79250d406a663bc3fa7c8 /Lib/test/test_subprocess.py | |
parent | 3fb1363fe87a24cdb2ee1dd9746f1c49046af958 (diff) | |
download | cpython-580d2782f70f8e0bed7ec20abb03d740cb83b5da.zip cpython-580d2782f70f8e0bed7ec20abb03d740cb83b5da.tar.gz cpython-580d2782f70f8e0bed7ec20abb03d740cb83b5da.tar.bz2 |
bpo-37424: Avoid a hang in subprocess.run timeout output capture (GH-14490)
Fixes a possible hang when using a timeout on subprocess.run() while
capturing output. If the child process spawned its own children or otherwise
connected its stdout or stderr handles with another process, we could hang
after the timeout was reached and our child was killed when attempting to read
final output from the pipes.
Diffstat (limited to 'Lib/test/test_subprocess.py')
-rw-r--r-- | Lib/test/test_subprocess.py | 21 |
1 files changed, 21 insertions, 0 deletions
diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index 4fe74bf..91f525d 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -10,6 +10,7 @@ import os import errno import tempfile import time +import traceback import selectors import sysconfig import select @@ -1557,6 +1558,26 @@ class RunFuncTestCase(BaseTestCase): self.assertIn('stderr', c.exception.args[0]) self.assertIn('capture_output', c.exception.args[0]) + # This test _might_ wind up a bit fragile on loaded build+test machines + # as it depends on the timing with wide enough margins for normal situations + # but does assert that it happened "soon enough" to believe the right thing + # happened. + @unittest.skipIf(mswindows, "requires posix like 'sleep' shell command") + def test_run_with_shell_timeout_and_capture_output(self): + """Output capturing after a timeout mustn't hang forever on open filehandles.""" + before_secs = time.monotonic() + try: + subprocess.run('sleep 3', shell=True, timeout=0.1, + capture_output=True) # New session unspecified. + except subprocess.TimeoutExpired as exc: + after_secs = time.monotonic() + stacks = traceback.format_exc() # assertRaises doesn't give this. + else: + self.fail("TimeoutExpired not raised.") + self.assertLess(after_secs - before_secs, 1.5, + msg="TimeoutExpired was delayed! Bad traceback:\n```\n" + f"{stacks}```") + @unittest.skipIf(mswindows, "POSIX specific tests") class POSIXProcessTestCase(BaseTestCase): |