_tasks.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. from __future__ import annotations
  2. import sys
  3. from abc import ABCMeta, abstractmethod
  4. from collections.abc import Awaitable, Callable
  5. from types import TracebackType
  6. from typing import TYPE_CHECKING, Any, Protocol, overload
  7. if sys.version_info >= (3, 13):
  8. from typing import TypeVar
  9. else:
  10. from typing_extensions import TypeVar
  11. if sys.version_info >= (3, 11):
  12. from typing import TypeVarTuple, Unpack
  13. else:
  14. from typing_extensions import TypeVarTuple, Unpack
  15. if TYPE_CHECKING:
  16. from .._core._tasks import CancelScope
  17. T_Retval = TypeVar("T_Retval")
  18. T_contra = TypeVar("T_contra", contravariant=True, default=None)
  19. PosArgsT = TypeVarTuple("PosArgsT")
  20. class TaskStatus(Protocol[T_contra]):
  21. @overload
  22. def started(self: TaskStatus[None]) -> None: ...
  23. @overload
  24. def started(self, value: T_contra) -> None: ...
  25. def started(self, value: T_contra | None = None) -> None:
  26. """
  27. Signal that the task has started.
  28. :param value: object passed back to the starter of the task
  29. """
  30. class TaskGroup(metaclass=ABCMeta):
  31. """
  32. Groups several asynchronous tasks together.
  33. :ivar cancel_scope: the cancel scope inherited by all child tasks
  34. :vartype cancel_scope: CancelScope
  35. .. note:: On asyncio, support for eager task factories is considered to be
  36. **experimental**. In particular, they don't follow the usual semantics of new
  37. tasks being scheduled on the next iteration of the event loop, and may thus
  38. cause unexpected behavior in code that wasn't written with such semantics in
  39. mind.
  40. """
  41. cancel_scope: CancelScope
  42. @abstractmethod
  43. def start_soon(
  44. self,
  45. func: Callable[[Unpack[PosArgsT]], Awaitable[Any]],
  46. *args: Unpack[PosArgsT],
  47. name: object = None,
  48. ) -> None:
  49. """
  50. Start a new task in this task group.
  51. :param func: a coroutine function
  52. :param args: positional arguments to call the function with
  53. :param name: name of the task, for the purposes of introspection and debugging
  54. .. versionadded:: 3.0
  55. """
  56. @abstractmethod
  57. async def start(
  58. self,
  59. func: Callable[..., Awaitable[Any]],
  60. *args: object,
  61. name: object = None,
  62. ) -> Any:
  63. """
  64. Start a new task and wait until it signals for readiness.
  65. The target callable must accept a keyword argument ``task_status`` (of type
  66. :class:`TaskStatus`). Awaiting on this method will return whatever was passed to
  67. ``task_status.started()`` (``None`` by default).
  68. .. note:: The :class:`TaskStatus` class is generic, and the type argument should
  69. indicate the type of the value that will be passed to
  70. ``task_status.started()``.
  71. :param func: a coroutine function that accepts the ``task_status`` keyword
  72. argument
  73. :param args: positional arguments to call the function with
  74. :param name: an optional name for the task, for introspection and debugging
  75. :return: the value passed to ``task_status.started()``
  76. :raises RuntimeError: if the task finishes without calling
  77. ``task_status.started()``
  78. .. seealso:: :ref:`start_initialize`
  79. .. versionadded:: 3.0
  80. """
  81. @abstractmethod
  82. async def __aenter__(self) -> TaskGroup:
  83. """Enter the task group context and allow starting new tasks."""
  84. @abstractmethod
  85. async def __aexit__(
  86. self,
  87. exc_type: type[BaseException] | None,
  88. exc_val: BaseException | None,
  89. exc_tb: TracebackType | None,
  90. ) -> bool:
  91. """Exit the task group context waiting for all tasks to finish."""