23
23
_local = threading .local ()
24
24
25
25
26
+ #
27
+ # Event loop
28
+ #
29
+
30
+ def run (func : Callable [..., Coroutine [Any , Any , T_Retval ]], * args ,
31
+ backend : str = BACKENDS [0 ], backend_options : Optional [Dict [str , Any ]] = None ) -> T_Retval :
32
+ """
33
+ Run the given coroutine function in an asynchronous event loop.
34
+
35
+ The current thread must not be already running an event loop.
36
+
37
+ :param func: a coroutine function
38
+ :param args: positional arguments to ``func``
39
+ :param backend: name of the asynchronous event loop implementation – one of ``asyncio``,
40
+ ``curio`` and ``trio``
41
+ :param backend_options: keyword arguments to call the backend ``run()`` implementation with
42
+ :return: the return value of the coroutine function
43
+ :raises RuntimeError: if an asynchronous event loop is already running in this thread
44
+ :raises LookupError: if the named backend is not found
45
+
46
+ """
47
+ asynclib_name = detect_running_asynclib ()
48
+ if asynclib_name :
49
+ raise RuntimeError ('Already running {} in this thread' .format (asynclib_name ))
50
+
51
+ try :
52
+ asynclib = import_module ('{}._backends.{}' .format (__name__ , backend ))
53
+ except ImportError as exc :
54
+ raise LookupError ('No such backend: {}' .format (backend )) from exc
55
+
56
+ backend_options = backend_options or {}
57
+ with claim_current_thread (asynclib ):
58
+ return asynclib .run (func , * args , ** backend_options )
59
+
60
+
26
61
@contextmanager
27
62
def claim_current_thread (asynclib ) -> None :
28
63
assert ismodule (asynclib )
@@ -33,11 +68,24 @@ def claim_current_thread(asynclib) -> None:
33
68
reset_detected_asynclib ()
34
69
35
70
36
- def reset_detected_asynclib ():
71
+ def reset_detected_asynclib () -> None :
72
+ """
73
+ Reset the cached information about the currently running async library.
74
+
75
+ This is only needed in case you need to run AnyIO code on two or more different async libraries
76
+ using their native ``run()`` functions one after another in the same thread.
77
+
78
+ """
37
79
_local .__dict__ .clear ()
38
80
39
81
40
82
def detect_running_asynclib () -> Optional [str ]:
83
+ """
84
+ Return the name of the asynchronous framework running in the current thread.
85
+
86
+ :return: the name of the framework, or ``None`` if no supported framework is running
87
+
88
+ """
41
89
if 'trio' in sys .modules :
42
90
from trio .hazmat import current_trio_token
43
91
try :
@@ -76,37 +124,6 @@ def _get_asynclib():
76
124
return _local .asynclib
77
125
78
126
79
- def run (func : Callable [..., Coroutine [Any , Any , T_Retval ]], * args ,
80
- backend : str = BACKENDS [0 ], backend_options : Optional [Dict [str , Any ]] = None ) -> T_Retval :
81
- """
82
- Run the given coroutine function in an asynchronous event loop.
83
-
84
- The current thread must not be already running an event loop.
85
-
86
- :param func: a coroutine function
87
- :param args: positional arguments to ``func``
88
- :param backend: name of the asynchronous event loop implementation – one of ``asyncio``,
89
- ``curio`` and ``trio``
90
- :param backend_options: keyword arguments to call the backend ``run()`` implementation with
91
- :return: the return value of the coroutine function
92
- :raises RuntimeError: if an asynchronous event loop is already running in this thread
93
- :raises LookupError: if the named backend is not found
94
-
95
- """
96
- asynclib_name = detect_running_asynclib ()
97
- if asynclib_name :
98
- raise RuntimeError ('Already running {} in this thread' .format (asynclib_name ))
99
-
100
- try :
101
- asynclib = import_module ('{}._backends.{}' .format (__name__ , backend ))
102
- except ImportError as exc :
103
- raise LookupError ('No such backend: {}' .format (backend )) from exc
104
-
105
- backend_options = backend_options or {}
106
- with claim_current_thread (asynclib ):
107
- return asynclib .run (func , * args , ** backend_options )
108
-
109
-
110
127
def is_in_event_loop_thread () -> bool :
111
128
"""
112
129
Determine whether the current thread is running a recognized asynchronous event loop.
@@ -117,6 +134,11 @@ def is_in_event_loop_thread() -> bool:
117
134
return detect_running_asynclib () is not None
118
135
119
136
137
+ #
138
+ # Miscellaneous
139
+ #
140
+
141
+
120
142
def finalize (resource : T_Agen ) -> 'typing.AsyncContextManager[T_Agen]' :
121
143
"""
122
144
Return a context manager that automatically closes an asynchronous resource on exit.
@@ -130,10 +152,6 @@ def finalize(resource: T_Agen) -> 'typing.AsyncContextManager[T_Agen]':
130
152
return _get_asynclib ().finalize (resource )
131
153
132
154
133
- #
134
- # Timeouts and cancellation
135
- #
136
-
137
155
def sleep (delay : float ) -> Awaitable [None ]:
138
156
"""
139
157
Pause the current task for the specified duration.
@@ -144,6 +162,11 @@ def sleep(delay: float) -> Awaitable[None]:
144
162
return _get_asynclib ().sleep (delay )
145
163
146
164
165
+ #
166
+ # Timeouts and cancellation
167
+ #
168
+
169
+
147
170
def open_cancel_scope (* , shield : bool = False ) -> 'typing.AsyncContextManager[CancelScope]' :
148
171
"""
149
172
Open a cancel scope.
@@ -190,7 +213,15 @@ def move_on_after(delay: Optional[float], *,
190
213
return _get_asynclib ().move_on_after (delay , shield = shield )
191
214
192
215
193
- def current_effective_deadline () -> Awaitable [float ]:
216
+ def current_effective_deadline () -> Coroutine [Any , Any , float ]:
217
+ """
218
+ Return the nearest deadline among all the cancel scopes effective for the current task.
219
+
220
+ :return: a clock value from the event loop's internal clock (``float('inf')`` if there is no
221
+ deadline in effect)
222
+ :rtype: float
223
+
224
+ """
194
225
return _get_asynclib ().current_effective_deadline ()
195
226
196
227
@@ -252,6 +283,7 @@ def aopen(file: Union[str, Path, int], mode: str = 'r', buffering: int = -1,
252
283
The arguments are exactly the same as for the builtin :func:`open`.
253
284
254
285
:return: an asynchronous file object
286
+ :rtype: AsyncFile
255
287
256
288
"""
257
289
if isinstance (file , Path ):
@@ -261,7 +293,7 @@ def aopen(file: Union[str, Path, int], mode: str = 'r', buffering: int = -1,
261
293
262
294
263
295
#
264
- # Networking
296
+ # Sockets and networking
265
297
#
266
298
267
299
def wait_socket_readable (sock : Union [socket .SocketType , ssl .SSLSocket ]) -> Awaitable [None ]:
@@ -512,7 +544,7 @@ def create_queue(capacity: int) -> Queue:
512
544
513
545
514
546
#
515
- # Signal handling
547
+ # Operating system signals
516
548
#
517
549
518
550
def receive_signals (* signals : int ) -> 'typing.ContextManager[typing.AsyncIterator[int]]' :
0 commit comments