_eventloop.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. from __future__ import annotations
  2. import math
  3. import sys
  4. from abc import ABCMeta, abstractmethod
  5. from collections.abc import AsyncIterator, Awaitable, Callable, Coroutine, Sequence
  6. from contextlib import AbstractContextManager
  7. from os import PathLike
  8. from signal import Signals
  9. from socket import AddressFamily, SocketKind, socket
  10. from typing import (
  11. IO,
  12. TYPE_CHECKING,
  13. Any,
  14. TypeAlias,
  15. TypeVar,
  16. overload,
  17. )
  18. if sys.version_info >= (3, 11):
  19. from typing import TypeVarTuple, Unpack
  20. else:
  21. from typing_extensions import TypeVarTuple, Unpack
  22. if TYPE_CHECKING:
  23. from _typeshed import FileDescriptorLike
  24. from .._core._synchronization import CapacityLimiter, Event, Lock, Semaphore
  25. from .._core._tasks import CancelScope
  26. from .._core._testing import TaskInfo
  27. from ._sockets import (
  28. ConnectedUDPSocket,
  29. ConnectedUNIXDatagramSocket,
  30. IPSockAddrType,
  31. SocketListener,
  32. SocketStream,
  33. UDPSocket,
  34. UNIXDatagramSocket,
  35. UNIXSocketStream,
  36. )
  37. from ._subprocesses import Process
  38. from ._tasks import TaskGroup
  39. from ._testing import TestRunner
  40. T_Retval = TypeVar("T_Retval")
  41. T_co = TypeVar("T_co", covariant=True)
  42. PosArgsT = TypeVarTuple("PosArgsT")
  43. StrOrBytesPath: TypeAlias = str | bytes | PathLike[str] | PathLike[bytes]
  44. class AsyncBackend(metaclass=ABCMeta):
  45. @classmethod
  46. @abstractmethod
  47. def run(
  48. cls,
  49. func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]],
  50. args: tuple[Unpack[PosArgsT]],
  51. kwargs: dict[str, Any],
  52. options: dict[str, Any],
  53. ) -> T_Retval:
  54. """
  55. Run the given coroutine function in an asynchronous event loop.
  56. The current thread must not be already running an event loop.
  57. :param func: a coroutine function
  58. :param args: positional arguments to ``func``
  59. :param kwargs: positional arguments to ``func``
  60. :param options: keyword arguments to call the backend ``run()`` implementation
  61. with
  62. :return: the return value of the coroutine function
  63. """
  64. @classmethod
  65. @abstractmethod
  66. def current_token(cls) -> object:
  67. """
  68. Return an object that allows other threads to run code inside the event loop.
  69. :return: a token object, specific to the event loop running in the current
  70. thread
  71. """
  72. @classmethod
  73. @abstractmethod
  74. def current_time(cls) -> float:
  75. """
  76. Return the current value of the event loop's internal clock.
  77. :return: the clock value (seconds)
  78. """
  79. @classmethod
  80. @abstractmethod
  81. def cancelled_exception_class(cls) -> type[BaseException]:
  82. """Return the exception class that is raised in a task if it's cancelled."""
  83. @classmethod
  84. @abstractmethod
  85. async def checkpoint(cls) -> None:
  86. """
  87. Check if the task has been cancelled, and allow rescheduling of other tasks.
  88. This is effectively the same as running :meth:`checkpoint_if_cancelled` and then
  89. :meth:`cancel_shielded_checkpoint`.
  90. """
  91. @classmethod
  92. async def checkpoint_if_cancelled(cls) -> None:
  93. """
  94. Check if the current task group has been cancelled.
  95. This will check if the task has been cancelled, but will not allow other tasks
  96. to be scheduled if not.
  97. """
  98. if cls.current_effective_deadline() == -math.inf:
  99. await cls.checkpoint()
  100. @classmethod
  101. async def cancel_shielded_checkpoint(cls) -> None:
  102. """
  103. Allow the rescheduling of other tasks.
  104. This will give other tasks the opportunity to run, but without checking if the
  105. current task group has been cancelled, unlike with :meth:`checkpoint`.
  106. """
  107. with cls.create_cancel_scope(shield=True):
  108. await cls.sleep(0)
  109. @classmethod
  110. @abstractmethod
  111. async def sleep(cls, delay: float) -> None:
  112. """
  113. Pause the current task for the specified duration.
  114. :param delay: the duration, in seconds
  115. """
  116. @classmethod
  117. @abstractmethod
  118. def create_cancel_scope(
  119. cls, *, deadline: float = math.inf, shield: bool = False
  120. ) -> CancelScope:
  121. pass
  122. @classmethod
  123. @abstractmethod
  124. def current_effective_deadline(cls) -> float:
  125. """
  126. Return the nearest deadline among all the cancel scopes effective for the
  127. current task.
  128. :return:
  129. - a clock value from the event loop's internal clock
  130. - ``inf`` if there is no deadline in effect
  131. - ``-inf`` if the current scope has been cancelled
  132. :rtype: float
  133. """
  134. @classmethod
  135. @abstractmethod
  136. def create_task_group(cls) -> TaskGroup:
  137. pass
  138. @classmethod
  139. @abstractmethod
  140. def create_event(cls) -> Event:
  141. pass
  142. @classmethod
  143. @abstractmethod
  144. def create_lock(cls, *, fast_acquire: bool) -> Lock:
  145. pass
  146. @classmethod
  147. @abstractmethod
  148. def create_semaphore(
  149. cls,
  150. initial_value: int,
  151. *,
  152. max_value: int | None = None,
  153. fast_acquire: bool = False,
  154. ) -> Semaphore:
  155. pass
  156. @classmethod
  157. @abstractmethod
  158. def create_capacity_limiter(cls, total_tokens: float) -> CapacityLimiter:
  159. pass
  160. @classmethod
  161. @abstractmethod
  162. async def run_sync_in_worker_thread(
  163. cls,
  164. func: Callable[[Unpack[PosArgsT]], T_Retval],
  165. args: tuple[Unpack[PosArgsT]],
  166. abandon_on_cancel: bool = False,
  167. limiter: CapacityLimiter | None = None,
  168. ) -> T_Retval:
  169. pass
  170. @classmethod
  171. @abstractmethod
  172. def check_cancelled(cls) -> None:
  173. pass
  174. @classmethod
  175. @abstractmethod
  176. def run_async_from_thread(
  177. cls,
  178. func: Callable[[Unpack[PosArgsT]], Coroutine[Any, Any, T_co]],
  179. args: tuple[Unpack[PosArgsT]],
  180. token: object,
  181. ) -> T_co:
  182. pass
  183. @classmethod
  184. @abstractmethod
  185. def run_sync_from_thread(
  186. cls,
  187. func: Callable[[Unpack[PosArgsT]], T_Retval],
  188. args: tuple[Unpack[PosArgsT]],
  189. token: object,
  190. ) -> T_Retval:
  191. pass
  192. @classmethod
  193. @abstractmethod
  194. async def open_process(
  195. cls,
  196. command: StrOrBytesPath | Sequence[StrOrBytesPath],
  197. *,
  198. stdin: int | IO[Any] | None,
  199. stdout: int | IO[Any] | None,
  200. stderr: int | IO[Any] | None,
  201. **kwargs: Any,
  202. ) -> Process:
  203. pass
  204. @classmethod
  205. @abstractmethod
  206. def setup_process_pool_exit_at_shutdown(cls, workers: set[Process]) -> None:
  207. pass
  208. @classmethod
  209. @abstractmethod
  210. async def connect_tcp(
  211. cls, host: str, port: int, local_address: IPSockAddrType | None = None
  212. ) -> SocketStream:
  213. pass
  214. @classmethod
  215. @abstractmethod
  216. async def connect_unix(cls, path: str | bytes) -> UNIXSocketStream:
  217. pass
  218. @classmethod
  219. @abstractmethod
  220. def create_tcp_listener(cls, sock: socket) -> SocketListener:
  221. pass
  222. @classmethod
  223. @abstractmethod
  224. def create_unix_listener(cls, sock: socket) -> SocketListener:
  225. pass
  226. @classmethod
  227. @abstractmethod
  228. async def create_udp_socket(
  229. cls,
  230. family: AddressFamily,
  231. local_address: IPSockAddrType | None,
  232. remote_address: IPSockAddrType | None,
  233. reuse_port: bool,
  234. ) -> UDPSocket | ConnectedUDPSocket:
  235. pass
  236. @classmethod
  237. @overload
  238. async def create_unix_datagram_socket(
  239. cls, raw_socket: socket, remote_path: None
  240. ) -> UNIXDatagramSocket: ...
  241. @classmethod
  242. @overload
  243. async def create_unix_datagram_socket(
  244. cls, raw_socket: socket, remote_path: str | bytes
  245. ) -> ConnectedUNIXDatagramSocket: ...
  246. @classmethod
  247. @abstractmethod
  248. async def create_unix_datagram_socket(
  249. cls, raw_socket: socket, remote_path: str | bytes | None
  250. ) -> UNIXDatagramSocket | ConnectedUNIXDatagramSocket:
  251. pass
  252. @classmethod
  253. @abstractmethod
  254. async def getaddrinfo(
  255. cls,
  256. host: bytes | str | None,
  257. port: str | int | None,
  258. *,
  259. family: int | AddressFamily = 0,
  260. type: int | SocketKind = 0,
  261. proto: int = 0,
  262. flags: int = 0,
  263. ) -> Sequence[
  264. tuple[
  265. AddressFamily,
  266. SocketKind,
  267. int,
  268. str,
  269. tuple[str, int] | tuple[str, int, int, int] | tuple[int, bytes],
  270. ]
  271. ]:
  272. pass
  273. @classmethod
  274. @abstractmethod
  275. async def getnameinfo(
  276. cls, sockaddr: IPSockAddrType, flags: int = 0
  277. ) -> tuple[str, str]:
  278. pass
  279. @classmethod
  280. @abstractmethod
  281. async def wait_readable(cls, obj: FileDescriptorLike) -> None:
  282. pass
  283. @classmethod
  284. @abstractmethod
  285. async def wait_writable(cls, obj: FileDescriptorLike) -> None:
  286. pass
  287. @classmethod
  288. @abstractmethod
  289. def notify_closing(cls, obj: FileDescriptorLike) -> None:
  290. pass
  291. @classmethod
  292. @abstractmethod
  293. async def wrap_listener_socket(cls, sock: socket) -> SocketListener:
  294. pass
  295. @classmethod
  296. @abstractmethod
  297. async def wrap_stream_socket(cls, sock: socket) -> SocketStream:
  298. pass
  299. @classmethod
  300. @abstractmethod
  301. async def wrap_unix_stream_socket(cls, sock: socket) -> UNIXSocketStream:
  302. pass
  303. @classmethod
  304. @abstractmethod
  305. async def wrap_udp_socket(cls, sock: socket) -> UDPSocket:
  306. pass
  307. @classmethod
  308. @abstractmethod
  309. async def wrap_connected_udp_socket(cls, sock: socket) -> ConnectedUDPSocket:
  310. pass
  311. @classmethod
  312. @abstractmethod
  313. async def wrap_unix_datagram_socket(cls, sock: socket) -> UNIXDatagramSocket:
  314. pass
  315. @classmethod
  316. @abstractmethod
  317. async def wrap_connected_unix_datagram_socket(
  318. cls, sock: socket
  319. ) -> ConnectedUNIXDatagramSocket:
  320. pass
  321. @classmethod
  322. @abstractmethod
  323. def current_default_thread_limiter(cls) -> CapacityLimiter:
  324. pass
  325. @classmethod
  326. @abstractmethod
  327. def open_signal_receiver(
  328. cls, *signals: Signals
  329. ) -> AbstractContextManager[AsyncIterator[Signals]]:
  330. pass
  331. @classmethod
  332. @abstractmethod
  333. def get_current_task(cls) -> TaskInfo:
  334. pass
  335. @classmethod
  336. @abstractmethod
  337. def get_running_tasks(cls) -> Sequence[TaskInfo]:
  338. pass
  339. @classmethod
  340. @abstractmethod
  341. async def wait_all_tasks_blocked(cls) -> None:
  342. pass
  343. @classmethod
  344. @abstractmethod
  345. def create_test_runner(cls, options: dict[str, Any]) -> TestRunner:
  346. pass