summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Grainger <tagrain@gmail.com>2022-07-24 20:18:05 (GMT)
committerGitHub <noreply@github.com>2022-07-24 20:18:05 (GMT)
commit0c6f898005099be189ee65bcfda659f5fc13b802 (patch)
tree92406f5b3df6f1e9784ba25d2dc918d677f3e145
parenteb9c8a8bea9128b31d4a604c4a1f27ec9b45f333 (diff)
downloadcpython-0c6f898005099be189ee65bcfda659f5fc13b802.zip
cpython-0c6f898005099be189ee65bcfda659f5fc13b802.tar.gz
cpython-0c6f898005099be189ee65bcfda659f5fc13b802.tar.bz2
gh-95051: ensure that timeouts scheduled with `asyncio.Timeout` that have already expired are deliverered promptly (#95109)
Co-authored-by: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com>
-rw-r--r--Doc/library/asyncio-task.rst3
-rw-r--r--Lib/asyncio/timeouts.py8
-rw-r--r--Lib/test/test_asyncio/test_timeouts.py24
-rw-r--r--Misc/NEWS.d/next/Library/2022-07-21-22-59-22.gh-issue-95109.usxA9r.rst1
4 files changed, 32 insertions, 4 deletions
diff --git a/Doc/library/asyncio-task.rst b/Doc/library/asyncio-task.rst
index a307d22..a6b638c 100644
--- a/Doc/library/asyncio-task.rst
+++ b/Doc/library/asyncio-task.rst
@@ -625,6 +625,9 @@ Timeouts
If *when* is a float, it is set as the new deadline.
+ if *when* is in the past, the timeout will trigger on the next
+ iteration of the event loop.
+
.. method:: expired() -> bool
Return whether the context manager has exceeded its deadline
diff --git a/Lib/asyncio/timeouts.py b/Lib/asyncio/timeouts.py
index a892053..94d2553 100644
--- a/Lib/asyncio/timeouts.py
+++ b/Lib/asyncio/timeouts.py
@@ -52,10 +52,10 @@ class Timeout:
self._timeout_handler = None
else:
loop = events.get_running_loop()
- self._timeout_handler = loop.call_at(
- when,
- self._on_timeout,
- )
+ if when <= loop.time():
+ self._timeout_handler = loop.call_soon(self._on_timeout)
+ else:
+ self._timeout_handler = loop.call_at(when, self._on_timeout)
def expired(self) -> bool:
"""Is timeout expired during execution?"""
diff --git a/Lib/test/test_asyncio/test_timeouts.py b/Lib/test/test_asyncio/test_timeouts.py
index 9801541..b9bac6f 100644
--- a/Lib/test/test_asyncio/test_timeouts.py
+++ b/Lib/test/test_asyncio/test_timeouts.py
@@ -105,6 +105,30 @@ class TimeoutTests(unittest.IsolatedAsyncioTestCase):
self.assertLess(t1-t0, 2)
self.assertTrue(t0 <= cm.when() <= t1)
+ async def test_timeout_zero_sleep_zero(self):
+ loop = asyncio.get_running_loop()
+ t0 = loop.time()
+ with self.assertRaises(TimeoutError):
+ async with asyncio.timeout(0) as cm:
+ await asyncio.sleep(0)
+ t1 = loop.time()
+ self.assertTrue(cm.expired())
+ # 2 sec for slow CI boxes
+ self.assertLess(t1-t0, 2)
+ self.assertTrue(t0 <= cm.when() <= t1)
+
+ async def test_timeout_in_the_past_sleep_zero(self):
+ loop = asyncio.get_running_loop()
+ t0 = loop.time()
+ with self.assertRaises(TimeoutError):
+ async with asyncio.timeout(-11) as cm:
+ await asyncio.sleep(0)
+ t1 = loop.time()
+ self.assertTrue(cm.expired())
+ # 2 sec for slow CI boxes
+ self.assertLess(t1-t0, 2)
+ self.assertTrue(t0 >= cm.when() <= t1)
+
async def test_foreign_exception_passed(self):
with self.assertRaises(KeyError):
async with asyncio.timeout(0.01) as cm:
diff --git a/Misc/NEWS.d/next/Library/2022-07-21-22-59-22.gh-issue-95109.usxA9r.rst b/Misc/NEWS.d/next/Library/2022-07-21-22-59-22.gh-issue-95109.usxA9r.rst
new file mode 100644
index 0000000..40196dd
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2022-07-21-22-59-22.gh-issue-95109.usxA9r.rst
@@ -0,0 +1 @@
+Ensure that timeouts scheduled with :class:`asyncio.Timeout` that have already expired are delivered promptly.