Skip to content
Merged
24 changes: 24 additions & 0 deletions Doc/library/subprocess.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1525,6 +1525,30 @@
Notes
-----

.. _subprocess-timeout-behavior:

Timeout Behavior
---------------

Check warning on line 1531 in Doc/library/subprocess.rst

View workflow job for this annotation

GitHub Actions / Docs / Docs

Title underline too short.

Check warning on line 1531 in Doc/library/subprocess.rst

View workflow job for this annotation

GitHub Actions / Docs / Docs

Title underline too short.

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:

Process Creation Delay
~~~~~~~~~~~~~~~~~~~~~

Check warning on line 1538 in Doc/library/subprocess.rst

View workflow job for this annotation

GitHub Actions / Docs / Docs

Title underline too short.

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.

Extremely Small Timeout Values
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Check warning on line 1545 in Doc/library/subprocess.rst

View workflow job for this annotation

GitHub Actions / Docs / Docs

Title underline too short.

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
Expand Down
4 changes: 2 additions & 2 deletions Lib/subprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -2031,7 +2031,7 @@ def _wait(self, timeout):
if self.returncode is not None:
return self.returncode

if timeout is not None:
if timeout is not None and timeout > 0:
endtime = _time() + timeout
# Enter a busy loop if we have a timeout. This busy loop was
# cribbed from Lib/threading.py in Thread.wait() at r71065.
Expand All @@ -2049,7 +2049,7 @@ def _wait(self, timeout):
finally:
self._waitpid_lock.release()
remaining = self._remaining_time(endtime)
if remaining <= 0:
if remaining < 0:
raise TimeoutExpired(self.args, timeout)
delay = min(delay * 2, remaining, .05)
time.sleep(delay)
Expand Down
2 changes: 2 additions & 0 deletions Lib/test/test_subprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -1660,6 +1660,8 @@ def test_timeout(self):
# child.
with self.assertRaises(subprocess.TimeoutExpired):
self.run_python("while True: pass", timeout=0.0001)
def test_timeout_zero(self):
self.run_python("import time; time.sleep(0.1)", timeout=0)

def test_capture_stdout(self):
# capture stdout with zero return code
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Make :func:`subprocess.run`'s behavior is same with 'timeout=None' when
the timeout is zero
Loading