99from typing import Optional , Type
1010
1111
12- # From https://github.com/python/typing_extensions/blob/main/src/typing_extensions.py
13- # Licensed under the Python Software Foundation License (PSF-2.0)
14-
1512if sys .version_info >= (3 , 11 ):
1613 from typing import final
1714else :
15+ # From https://github.com/python/typing_extensions/blob/main/src/typing_extensions.py
16+ # Licensed under the Python Software Foundation License (PSF-2.0)
17+
1818 # @final exists in 3.8+, but we backport it for all versions
1919 # before 3.11 to keep support for the __final__ attribute.
2020 # See https://bugs.python.org/issue46342
@@ -49,10 +49,21 @@ class Other(Leaf): # Error reported by type checker
4949 pass
5050 return f
5151
52+ # End https://github.com/python/typing_extensions/blob/main/src/typing_extensions.py
53+
54+
55+ if sys .version_info >= (3 , 11 ):
56+
57+ def _uncancel_task (task : "asyncio.Task[object]" ) -> None :
58+ task .uncancel ()
59+
60+ else :
61+
62+ def _uncancel_task (task : "asyncio.Task[object]" ) -> None :
63+ pass
5264
53- # End https://github.com/aio-libs/async-timeout/blob/master/async_timeout/__init__.py
5465
55- __version__ = "4.0.2 "
66+ __version__ = "4.0.3 "
5667
5768
5869__all__ = ("timeout" , "timeout_at" , "Timeout" )
@@ -124,14 +135,15 @@ class Timeout:
124135 # The purpose is to time out as soon as possible
125136 # without waiting for the next await expression.
126137
127- __slots__ = ("_deadline" , "_loop" , "_state" , "_timeout_handler" )
138+ __slots__ = ("_deadline" , "_loop" , "_state" , "_timeout_handler" , "_task" )
128139
129140 def __init__ (
130141 self , deadline : Optional [float ], loop : asyncio .AbstractEventLoop
131142 ) -> None :
132143 self ._loop = loop
133144 self ._state = _State .INIT
134145
146+ self ._task : Optional ["asyncio.Task[object]" ] = None
135147 self ._timeout_handler = None # type: Optional[asyncio.Handle]
136148 if deadline is None :
137149 self ._deadline = None # type: Optional[float]
@@ -187,6 +199,7 @@ def reject(self) -> None:
187199 self ._reject ()
188200
189201 def _reject (self ) -> None :
202+ self ._task = None
190203 if self ._timeout_handler is not None :
191204 self ._timeout_handler .cancel ()
192205 self ._timeout_handler = None
@@ -234,11 +247,11 @@ def _reschedule(self) -> None:
234247 if self ._timeout_handler is not None :
235248 self ._timeout_handler .cancel ()
236249
237- task = asyncio .current_task ()
250+ self . _task = asyncio .current_task ()
238251 if deadline <= now :
239- self ._timeout_handler = self ._loop .call_soon (self ._on_timeout , task )
252+ self ._timeout_handler = self ._loop .call_soon (self ._on_timeout )
240253 else :
241- self ._timeout_handler = self ._loop .call_at (deadline , self ._on_timeout , task )
254+ self ._timeout_handler = self ._loop .call_at (deadline , self ._on_timeout )
242255
243256 def _do_enter (self ) -> None :
244257 if self ._state != _State .INIT :
@@ -248,15 +261,19 @@ def _do_enter(self) -> None:
248261
249262 def _do_exit (self , exc_type : Optional [Type [BaseException ]]) -> None :
250263 if exc_type is asyncio .CancelledError and self ._state == _State .TIMEOUT :
264+ assert self ._task is not None
265+ _uncancel_task (self ._task )
251266 self ._timeout_handler = None
267+ self ._task = None
252268 raise asyncio .TimeoutError
253269 # timeout has not expired
254270 self ._state = _State .EXIT
255271 self ._reject ()
256272 return None
257273
258- def _on_timeout (self , task : "asyncio.Task[None]" ) -> None :
259- task .cancel ()
274+ def _on_timeout (self ) -> None :
275+ assert self ._task is not None
276+ self ._task .cancel ()
260277 self ._state = _State .TIMEOUT
261278 # drop the reference early
262279 self ._timeout_handler = None
0 commit comments