gh-133089: Use original timeout value for TimeoutExpired
when the func subprocess.run
is called with a timeout (GH-133103)
Signed-off-by: Manjusaka <me@manjusaka.me> Co-authored-by: Gregory P. Smith <greg@krypto.org>
This commit is contained in:
parent
51d2459e4d
commit
2bbcaedb75
@ -1525,6 +1525,24 @@ handling consistency are valid for these functions.
|
||||
Notes
|
||||
-----
|
||||
|
||||
.. _subprocess-timeout-behavior:
|
||||
|
||||
Timeout Behavior
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
When using the ``timeout`` parameter in functions like :func:`run`,
|
||||
:meth:`Popen.wait`, or :meth:`Popen.communicate`,
|
||||
users should be aware of the following behaviors:
|
||||
|
||||
1. **Process Creation Delay**: The initial process creation itself cannot be interrupted
|
||||
on many platform APIs. This means that even when specifying a timeout, you are not
|
||||
guaranteed to see a timeout exception until at least after however long process
|
||||
creation takes.
|
||||
|
||||
2. **Extremely Small Timeout Values**: Setting very small timeout values (such as a few
|
||||
milliseconds) may result in almost immediate :exc:`TimeoutExpired` exceptions because
|
||||
process creation and system scheduling inherently require time.
|
||||
|
||||
.. _converting-argument-sequence:
|
||||
|
||||
Converting an argument sequence to a string on Windows
|
||||
|
@ -1235,8 +1235,11 @@ class Popen:
|
||||
|
||||
finally:
|
||||
self._communication_started = True
|
||||
|
||||
sts = self.wait(timeout=self._remaining_time(endtime))
|
||||
try:
|
||||
sts = self.wait(timeout=self._remaining_time(endtime))
|
||||
except TimeoutExpired as exc:
|
||||
exc.timeout = timeout
|
||||
raise
|
||||
|
||||
return (stdout, stderr)
|
||||
|
||||
@ -2145,8 +2148,11 @@ class Popen:
|
||||
selector.unregister(key.fileobj)
|
||||
key.fileobj.close()
|
||||
self._fileobj2output[key.fileobj].append(data)
|
||||
|
||||
self.wait(timeout=self._remaining_time(endtime))
|
||||
try:
|
||||
self.wait(timeout=self._remaining_time(endtime))
|
||||
except TimeoutExpired as exc:
|
||||
exc.timeout = orig_timeout
|
||||
raise
|
||||
|
||||
# All data exchanged. Translate lists into strings.
|
||||
if stdout is not None:
|
||||
|
@ -162,6 +162,20 @@ class ProcessTestCase(BaseTestCase):
|
||||
[sys.executable, "-c", "while True: pass"],
|
||||
timeout=0.1)
|
||||
|
||||
def test_timeout_exception(self):
|
||||
try:
|
||||
subprocess.run(['echo', 'hi'], timeout = -1)
|
||||
except subprocess.TimeoutExpired as e:
|
||||
self.assertIn("-1 seconds", str(e))
|
||||
else:
|
||||
self.fail("Expected TimeoutExpired exception not raised")
|
||||
try:
|
||||
subprocess.run(['echo', 'hi'], timeout = 0)
|
||||
except subprocess.TimeoutExpired as e:
|
||||
self.assertIn("0 seconds", str(e))
|
||||
else:
|
||||
self.fail("Expected TimeoutExpired exception not raised")
|
||||
|
||||
def test_check_call_zero(self):
|
||||
# check_call() function with zero return code
|
||||
rc = subprocess.check_call(ZERO_RETURN_CMD)
|
||||
|
@ -0,0 +1,4 @@
|
||||
Use original timeout value for :exc:`subprocess.TimeoutExpired`
|
||||
when the func :meth:`subprocess.run` is called with a timeout
|
||||
instead of sometimes a confusing partial remaining time out value
|
||||
used internally on the final ``wait()``.
|
Loading…
x
Reference in New Issue
Block a user