| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394 |
- """Minimal backported implementation of asyncio._PyTask from 3.11 compatible down to Python 3.8."""
- import asyncio.tasks
- import sys
- from asyncio import AbstractEventLoop
- from typing import Coroutine, TypeVar, Any, Optional
- _T = TypeVar("_T")
- # See https://github.com/python/cpython/blob/3.11/Lib/asyncio/tasks.py
- class Task(asyncio.tasks._PyTask): # type: ignore[name-defined, misc]
- """A coroutine wrapped in a Future."""
- def __init__(
- self,
- coro: Coroutine[Any, Any, _T],
- *,
- loop: Optional[AbstractEventLoop] = None,
- name: Optional[str] = None,
- ) -> None:
- self._num_cancels_requested = 0
- # https://github.com/python/cpython/blob/3.11/Modules/_asynciomodule.c#L2026
- # Backport Note: self._context is temporarily patched in Runner.run() instead.
- super().__init__(coro, loop=loop, name=name)
- def cancel(self, msg: Optional[str] = None) -> bool:
- """Request that this task cancel itself.
- This arranges for a CancelledError to be thrown into the
- wrapped coroutine on the next cycle through the event loop.
- The coroutine then has a chance to clean up or even deny
- the request using try/except/finally.
- Unlike Future.cancel, this does not guarantee that the
- task will be cancelled: the exception might be caught and
- acted upon, delaying cancellation of the task or preventing
- cancellation completely. The task may also return a value or
- raise a different exception.
- Immediately after this method is called, Task.cancelled() will
- not return True (unless the task was already cancelled). A
- task will be marked as cancelled when the wrapped coroutine
- terminates with a CancelledError exception (even if cancel()
- was not called).
- This also increases the task's count of cancellation requests.
- """
- self._log_traceback = False
- if self.done():
- return False
- self._num_cancels_requested += 1
- # These two lines are controversial. See discussion starting at
- # https://github.com/python/cpython/pull/31394#issuecomment-1053545331
- # Also remember that this is duplicated in _asynciomodule.c.
- # if self._num_cancels_requested > 1:
- # return False
- if self._fut_waiter is not None:
- if sys.version_info >= (3, 9):
- if self._fut_waiter.cancel(msg=msg):
- # Leave self._fut_waiter; it may be a Task that
- # catches and ignores the cancellation so we may have
- # to cancel it again later.
- return True
- else:
- if self._fut_waiter.cancel():
- return True
- # It must be the case that self.__step is already scheduled.
- self._must_cancel = True
- if sys.version_info >= (3, 9):
- self._cancel_message = msg
- return True
- def cancelling(self) -> int:
- """Return the count of the task's cancellation requests.
- This count is incremented when .cancel() is called
- and may be decremented using .uncancel().
- """
- return self._num_cancels_requested
- def uncancel(self) -> int:
- """Decrement the task's count of cancellation requests.
- This should be called by the party that called `cancel()` on the task
- beforehand.
- Returns the remaining number of cancellation requests.
- """
- if self._num_cancels_requested > 0:
- self._num_cancels_requested -= 1
- return self._num_cancels_requested
|