Skip to content

Commit 3cfabe2

Browse files
authored
Uncancel tasks after timeout in python 3.11 (#363)
1 parent adf9e28 commit 3cfabe2

File tree

3 files changed

+31
-6
lines changed

3 files changed

+31
-6
lines changed

CHANGES/362.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix compatibility with asyncio.timeout() on python 3.11+.

async_timeout/__init__.py

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,17 @@
1212
from typing_extensions import final
1313

1414

15+
if sys.version_info >= (3, 11):
16+
17+
def _uncancel_task(task: "asyncio.Task[object]") -> None:
18+
task.uncancel()
19+
20+
else:
21+
22+
def _uncancel_task(task: "asyncio.Task[object]") -> None:
23+
pass
24+
25+
1526
__version__ = "4.0.2"
1627

1728

@@ -84,14 +95,15 @@ class Timeout:
8495
# The purpose is to time out as soon as possible
8596
# without waiting for the next await expression.
8697

87-
__slots__ = ("_deadline", "_loop", "_state", "_timeout_handler")
98+
__slots__ = ("_deadline", "_loop", "_state", "_timeout_handler", "_task")
8899

89100
def __init__(
90101
self, deadline: Optional[float], loop: asyncio.AbstractEventLoop
91102
) -> None:
92103
self._loop = loop
93104
self._state = _State.INIT
94105

106+
self._task: Optional["asyncio.Task[object]"] = None
95107
self._timeout_handler = None # type: Optional[asyncio.Handle]
96108
if deadline is None:
97109
self._deadline = None # type: Optional[float]
@@ -147,6 +159,7 @@ def reject(self) -> None:
147159
self._reject()
148160

149161
def _reject(self) -> None:
162+
self._task = None
150163
if self._timeout_handler is not None:
151164
self._timeout_handler.cancel()
152165
self._timeout_handler = None
@@ -194,11 +207,11 @@ def _reschedule(self) -> None:
194207
if self._timeout_handler is not None:
195208
self._timeout_handler.cancel()
196209

197-
task = asyncio.current_task()
210+
self._task = asyncio.current_task()
198211
if deadline <= now:
199-
self._timeout_handler = self._loop.call_soon(self._on_timeout, task)
212+
self._timeout_handler = self._loop.call_soon(self._on_timeout)
200213
else:
201-
self._timeout_handler = self._loop.call_at(deadline, self._on_timeout, task)
214+
self._timeout_handler = self._loop.call_at(deadline, self._on_timeout)
202215

203216
def _do_enter(self) -> None:
204217
if self._state != _State.INIT:
@@ -208,15 +221,19 @@ def _do_enter(self) -> None:
208221

209222
def _do_exit(self, exc_type: Optional[Type[BaseException]]) -> None:
210223
if exc_type is asyncio.CancelledError and self._state == _State.TIMEOUT:
224+
assert self._task is not None
225+
_uncancel_task(self._task)
211226
self._timeout_handler = None
227+
self._task = None
212228
raise asyncio.TimeoutError
213229
# timeout has not expired
214230
self._state = _State.EXIT
215231
self._reject()
216232
return None
217233

218-
def _on_timeout(self, task: "asyncio.Task[None]") -> None:
219-
task.cancel()
234+
def _on_timeout(self) -> None:
235+
assert self._task is not None
236+
self._task.cancel()
220237
self._state = _State.TIMEOUT
221238
# drop the reference early
222239
self._timeout_handler = None

tests/test_timeout.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import asyncio
2+
import sys
23
import time
34
from functools import wraps
45
from typing import Any, Callable, List, TypeVar
@@ -39,6 +40,10 @@ async def long_running_task() -> None:
3940
await long_running_task()
4041
assert t._loop is asyncio.get_event_loop()
4142
assert canceled_raised, "CancelledError was not raised"
43+
if sys.version_info >= (3, 11):
44+
task = asyncio.current_task()
45+
assert task is not None
46+
assert not task.cancelling()
4247

4348

4449
@pytest.mark.asyncio
@@ -158,6 +163,8 @@ async def outer() -> None:
158163
await task
159164
assert has_timeout
160165
assert not task.cancelled()
166+
if sys.version_info >= (3, 11):
167+
assert not task.cancelling()
161168
assert task.done()
162169

163170

0 commit comments

Comments
 (0)