summaryrefslogtreecommitdiffstats
path: root/Lib/test/test_subprocess.py
diff options
context:
space:
mode:
authorGregory P. Smith <greg@krypto.org>2019-09-11 09:23:05 (GMT)
committerT. Wouters <thomas@python.org>2019-09-11 09:23:05 (GMT)
commit580d2782f70f8e0bed7ec20abb03d740cb83b5da (patch)
tree28962e4e84a383c7d5c79250d406a663bc3fa7c8 /Lib/test/test_subprocess.py
parent3fb1363fe87a24cdb2ee1dd9746f1c49046af958 (diff)
downloadcpython-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.py21
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):