Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pyee/asyncio.py: 38%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# -*- coding: utf-8 -*-
3from asyncio import AbstractEventLoop, ensure_future, Future, iscoroutine, wait
4from typing import Any, Callable, cast, Dict, Optional, Set, Tuple
6from pyee.base import EventEmitter
8Self = Any
10__all__ = ["AsyncIOEventEmitter"]
13class AsyncIOEventEmitter(EventEmitter):
14 """An event emitter class which can run asyncio coroutines in addition to
15 synchronous blocking functions. For example:
17 ```py
18 @ee.on('event')
19 async def async_handler(*args, **kwargs):
20 await returns_a_future()
21 ```
23 On emit, the event emitter will automatically schedule the coroutine using
24 `asyncio.ensure_future` and the configured event loop (defaults to
25 `asyncio.get_event_loop()`).
27 Unlike the case with the EventEmitter, all exceptions raised by
28 event handlers are automatically emitted on the `error` event. This is
29 important for asyncio coroutines specifically but is also handled for
30 synchronous functions for consistency.
32 When `loop` is specified, the supplied event loop will be used when
33 scheduling work with `ensure_future`. Otherwise, the default asyncio
34 event loop is used.
36 For asyncio coroutine event handlers, calling emit is non-blocking.
37 In other words, you do not have to await any results from emit, and the
38 coroutine is scheduled in a fire-and-forget fashion.
39 """
41 def __init__(self: Self, loop: Optional[AbstractEventLoop] = None) -> None:
42 super(AsyncIOEventEmitter, self).__init__()
43 self._loop: Optional[AbstractEventLoop] = loop
44 self._waiting: Set[Future] = set()
46 def emit(
47 self: Self,
48 event: str,
49 *args: Any,
50 **kwargs: Any,
51 ) -> bool:
52 """Emit `event`, passing `*args` and `**kwargs` to each attached
53 function or coroutine. Returns `True` if any functions are attached to
54 `event`; otherwise returns `False`.
56 Example:
58 ```py
59 ee.emit('data', '00101001')
60 ```
62 Assuming `data` is an attached function, this will call
63 `data('00101001')'`.
65 When executing coroutine handlers, their respective futures will be
66 stored in a "waiting" state. These futures may be waited on or
67 canceled with `wait_for_complete` and `cancel`, respectively; and
68 their status may be checked via the `complete` property.
69 """
70 return super().emit(event, *args, **kwargs)
72 def _emit_run(
73 self: Self,
74 f: Callable,
75 args: Tuple[Any, ...],
76 kwargs: Dict[str, Any],
77 ) -> None:
78 try:
79 coro: Any = f(*args, **kwargs)
80 except Exception as exc:
81 self.emit("error", exc)
82 else:
83 if iscoroutine(coro):
84 if self._loop:
85 # ensure_future is *extremely* cranky about the types here,
86 # but this is relatively well-tested and I think the types
87 # are more strict than they should be
88 fut: Any = ensure_future(cast(Any, coro), loop=self._loop)
89 else:
90 fut = ensure_future(cast(Any, coro))
92 elif isinstance(coro, Future):
93 fut = cast(Any, coro)
94 else:
95 return
97 def callback(f: Future) -> None:
98 self._waiting.discard(f)
100 if f.cancelled():
101 return
103 exc: Optional[BaseException] = f.exception()
104 if exc:
105 self.emit("error", exc)
107 fut.add_done_callback(callback)
108 self._waiting.add(fut)
110 async def wait_for_complete(self: Self) -> None:
111 """Waits for all pending tasks to complete. For example:
113 ```py
114 @ee.on('event')
115 async def async_handler(*args, **kwargs):
116 await returns_a_future()
118 # Triggers execution of async_handler
119 ee.emit('data', '00101001')
121 await ee.wait_for_complete()
123 # async_handler has completed execution
124 ```
126 This is useful if you're attempting a graceful shutdown of your
127 application and want to ensure all coroutines have completed execution
128 beforehand.
129 """
130 if self._waiting:
131 await wait(self._waiting)
133 def cancel(self: Self) -> None:
134 """Cancel all pending tasks. For example:
136 ```py
137 @ee.on('event')
138 async def async_handler(*args, **kwargs):
139 await returns_a_future()
141 # Triggers execution of async_handler
142 ee.emit('data', '00101001')
144 ee.cancel()
146 # async_handler execution has been canceled
147 ```
149 This is useful if you're attempting to shut down your application and
150 attempts at a graceful shutdown via `wait_for_complete` have failed.
151 """
152 for fut in self._waiting:
153 if not fut.done() and not fut.cancelled():
154 fut.cancel()
155 self._waiting.clear()
157 @property
158 def complete(self: Self) -> bool:
159 """When true, there are no pending tasks, and execution is complete.
160 For example:
162 ```py
163 @ee.on('event')
164 async def async_handler(*args, **kwargs):
165 await returns_a_future()
167 # Triggers execution of async_handler
168 ee.emit('data', '00101001')
170 # async_handler is still running, so this prints False
171 print(ee.complete)
173 await ee.wait_for_complete()
175 # async_handler has completed execution, so this prints True
176 print(ee.complete)
177 ```
178 """
179 return not self._waiting