Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 3d1b8e2

Browse files
miss-islingtonZheaoligpshead
authored
[3.13] gh-133089: Use original timeout value for TimeoutExpired when the func subprocess.run is called with a timeout (GH-133103) (#133418)
gh-133089: Use original timeout value for `TimeoutExpired` when the func `subprocess.run` is called with a timeout (GH-133103) (cherry picked from commit 2bbcaed) Signed-off-by: Manjusaka <[email protected]> Co-authored-by: Nadeshiko Manju <[email protected]> Co-authored-by: Gregory P. Smith <[email protected]>
1 parent 5daeebb commit 3d1b8e2

File tree

4 files changed

+46
-4
lines changed

4 files changed

+46
-4
lines changed

Doc/library/subprocess.rst

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1591,6 +1591,24 @@ handling consistency are valid for these functions.
15911591
Notes
15921592
-----
15931593

1594+
.. _subprocess-timeout-behavior:
1595+
1596+
Timeout Behavior
1597+
^^^^^^^^^^^^^^^^
1598+
1599+
When using the ``timeout`` parameter in functions like :func:`run`,
1600+
:meth:`Popen.wait`, or :meth:`Popen.communicate`,
1601+
users should be aware of the following behaviors:
1602+
1603+
1. **Process Creation Delay**: The initial process creation itself cannot be interrupted
1604+
on many platform APIs. This means that even when specifying a timeout, you are not
1605+
guaranteed to see a timeout exception until at least after however long process
1606+
creation takes.
1607+
1608+
2. **Extremely Small Timeout Values**: Setting very small timeout values (such as a few
1609+
milliseconds) may result in almost immediate :exc:`TimeoutExpired` exceptions because
1610+
process creation and system scheduling inherently require time.
1611+
15941612
.. _converting-argument-sequence:
15951613

15961614
Converting an argument sequence to a string on Windows

Lib/subprocess.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1237,8 +1237,11 @@ def communicate(self, input=None, timeout=None):
12371237

12381238
finally:
12391239
self._communication_started = True
1240-
1241-
sts = self.wait(timeout=self._remaining_time(endtime))
1240+
try:
1241+
sts = self.wait(timeout=self._remaining_time(endtime))
1242+
except TimeoutExpired as exc:
1243+
exc.timeout = timeout
1244+
raise
12421245

12431246
return (stdout, stderr)
12441247

@@ -2147,8 +2150,11 @@ def _communicate(self, input, endtime, orig_timeout):
21472150
selector.unregister(key.fileobj)
21482151
key.fileobj.close()
21492152
self._fileobj2output[key.fileobj].append(data)
2150-
2151-
self.wait(timeout=self._remaining_time(endtime))
2153+
try:
2154+
self.wait(timeout=self._remaining_time(endtime))
2155+
except TimeoutExpired as exc:
2156+
exc.timeout = orig_timeout
2157+
raise
21522158

21532159
# All data exchanged. Translate lists into strings.
21542160
if stdout is not None:

Lib/test/test_subprocess.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,20 @@ def test_call_timeout(self):
161161
[sys.executable, "-c", "while True: pass"],
162162
timeout=0.1)
163163

164+
def test_timeout_exception(self):
165+
try:
166+
subprocess.run(['echo', 'hi'], timeout = -1)
167+
except subprocess.TimeoutExpired as e:
168+
self.assertIn("-1 seconds", str(e))
169+
else:
170+
self.fail("Expected TimeoutExpired exception not raised")
171+
try:
172+
subprocess.run(['echo', 'hi'], timeout = 0)
173+
except subprocess.TimeoutExpired as e:
174+
self.assertIn("0 seconds", str(e))
175+
else:
176+
self.fail("Expected TimeoutExpired exception not raised")
177+
164178
def test_check_call_zero(self):
165179
# check_call() function with zero return code
166180
rc = subprocess.check_call(ZERO_RETURN_CMD)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Use original timeout value for :exc:`subprocess.TimeoutExpired`
2+
when the func :meth:`subprocess.run` is called with a timeout
3+
instead of sometimes a confusing partial remaining time out value
4+
used internally on the final ``wait()``.

0 commit comments

Comments
 (0)