API Docs
pyee
pyee supplies a EventEmitter class that is similar to the
EventEmitter class from Node.js. In addition, it supplies the subclasses
AsyncIOEventEmitter, TwistedEventEmitter and ExecutorEventEmitter
for supporting async and threaded execution with asyncio, twisted, and
concurrent.futures Executors respectively, as supported by the environment.
Example
In [1]: from pyee.base import EventEmitter
In [2]: ee = EventEmitter()
In [3]: @ee.on('event')
...: def event_handler():
...: print('BANG BANG')
...:
In [4]: ee.emit('event')
BANG BANG
In [5]:
EventEmitter
The base event emitter class. All other event emitters inherit from this class.
Most events are registered with an emitter via the on and once
methods, and fired with the emit method. However, pyee event emitters
have two special events:
-
new_listener: Fires whenever a new listener is created. Listeners for this event do not fire upon their own creation. -
error: When emitted raises an Exception by default, behavior can be overridden by attaching callback to the event.
For example:
@ee.on('error')
def on_error(message):
logging.err(message)
ee.emit('error', Exception('something blew up'))
All callbacks are handled in a synchronous, blocking manner. As in node.js, raised exceptions are not automatically handled for you---you must catch your own exceptions, and treat them accordingly.
Source code in pyee/base.py
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 | |
add_listener(event, f)
Register the function f to the event name event:
def data_handler(data):
print(data)
h = ee.add_listener("event", data_handler)
By not supporting the decorator use case, this method has improved
type safety over EventEmitter#on.
Source code in pyee/base.py
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | |
emit(event, *args, **kwargs)
Emit event, passing *args and **kwargs to each attached
function. Returns True if any functions are attached to event;
otherwise returns False.
Example:
ee.emit('data', '00101001')
Assuming data is an attached function, this will call
data('00101001')'.
Source code in pyee/base.py
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 | |
event_names()
Get a set of events that this emitter is listening to.
Source code in pyee/base.py
174 175 176 | |
listeners(event)
Returns a list of all listeners registered to the event.
Source code in pyee/base.py
282 283 284 | |
listens_to(event)
Returns a decorator which will register the decorated function to
the event name event:
@ee.listens_to("event")
def data_handler(data):
print(data)
By only supporting the decorator use case, this method has improved
type safety over EventEmitter#on.
Source code in pyee/base.py
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | |
on(event, f=None)
on(event: str) -> Callable[[Handler], Handler]
on(event: str, f: Handler) -> Handler
Registers the function f to the event name event, if provided.
If f isn't provided, this method calls EventEmitter#listens_to, and
otherwise calls EventEmitter#add_listener. In other words, you may either
use it as a decorator:
@ee.on('data')
def data_handler(data):
print(data)
Or directly:
ee.on('data', data_handler)
In both the decorated and undecorated forms, the event handler is returned. The upshot of this is that you can call decorated handlers directly, as well as use them in remove_listener calls.
Note that this method's return type is a union type. If you are using
mypy or pyright, you will probably want to use either
EventEmitter#listens_to or EventEmitter#add_listener.
Source code in pyee/base.py
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | |
once(event, f=None)
The same as ee.on, except that the listener is automatically
removed after being called.
Source code in pyee/base.py
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 | |
remove_all_listeners(event=None)
Remove all listeners attached to event.
If event is None, remove all listeners on all events.
Source code in pyee/base.py
272 273 274 275 276 277 278 279 280 | |
remove_listener(event, f)
Removes the function f from event.
Source code in pyee/base.py
267 268 269 270 | |
PyeeException
Bases: Exception
An exception internal to pyee. Deprecated in favor of PyeeError.
Source code in pyee/base.py
22 23 | |
pyee.asyncio
AsyncIOEventEmitter
Bases: EventEmitter
An event emitter class which can run asyncio coroutines in addition to synchronous blocking functions. For example:
@ee.on('event')
async def async_handler(*args, **kwargs):
await returns_a_future()
On emit, the event emitter will automatically schedule the coroutine using
asyncio.ensure_future and the configured event loop (defaults to
asyncio.get_event_loop()).
Unlike the case with the EventEmitter, all exceptions raised by
event handlers are automatically emitted on the error event. This is
important for asyncio coroutines specifically but is also handled for
synchronous functions for consistency.
When loop is specified, the supplied event loop will be used when
scheduling work with ensure_future. Otherwise, the default asyncio
event loop is used.
For asyncio coroutine event handlers, calling emit is non-blocking. In other words, you do not have to await any results from emit, and the coroutine is scheduled in a fire-and-forget fashion.
Source code in pyee/asyncio.py
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | |
complete
property
When true, there are no pending tasks, and execution is complete. For example:
@ee.on('event')
async def async_handler(*args, **kwargs):
await returns_a_future()
# Triggers execution of async_handler
ee.emit('data', '00101001')
# async_handler is still running, so this prints False
print(ee.complete)
await ee.wait_for_complete()
# async_handler has completed execution, so this prints True
print(ee.complete)
cancel()
Cancel all pending tasks. For example:
@ee.on('event')
async def async_handler(*args, **kwargs):
await returns_a_future()
# Triggers execution of async_handler
ee.emit('data', '00101001')
ee.cancel()
# async_handler execution has been canceled
This is useful if you're attempting to shut down your application and
attempts at a graceful shutdown via wait_for_complete have failed.
Source code in pyee/asyncio.py
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | |
emit(event, *args, **kwargs)
Emit event, passing *args and **kwargs to each attached
function or coroutine. Returns True if any functions are attached to
event; otherwise returns False.
Example:
ee.emit('data', '00101001')
Assuming data is an attached function, this will call
data('00101001')'.
When executing coroutine handlers, their respective futures will be
stored in a "waiting" state. These futures may be waited on or
canceled with wait_for_complete and cancel, respectively; and
their status may be checked via the complete property.
Source code in pyee/asyncio.py
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | |
wait_for_complete()
async
Waits for all pending tasks to complete. For example:
@ee.on('event')
async def async_handler(*args, **kwargs):
await returns_a_future()
# Triggers execution of async_handler
ee.emit('data', '00101001')
await ee.wait_for_complete()
# async_handler has completed execution
This is useful if you're attempting a graceful shutdown of your application and want to ensure all coroutines have completed execution beforehand.
Source code in pyee/asyncio.py
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | |
pyee.twisted
TwistedEventEmitter
Bases: EventEmitter
An event emitter class which can run twisted coroutines and handle returned Deferreds, in addition to synchronous blocking functions. For example:
@ee.on('event')
@inlineCallbacks
def async_handler(*args, **kwargs):
yield returns_a_deferred()
or:
@ee.on('event')
async def async_handler(*args, **kwargs):
await returns_a_deferred()
When async handlers fail, Failures are first emitted on the failure
event. If there are no failure handlers, the Failure's associated
exception is then emitted on the error event. If there are no error
handlers, the exception is raised. For consistency, when handlers raise
errors synchronously, they're captured, wrapped in a Failure and treated
as an async failure. This is unlike the behavior of EventEmitter,
which have no special error handling.
For twisted coroutine event handlers, calling emit is non-blocking. In other words, you do not have to await any results from emit, and the coroutine is scheduled in a fire-and-forget fashion.
Similar behavior occurs for "sync" functions which return Deferreds.
Source code in pyee/twisted.py
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | |
pyee.executor
ExecutorEventEmitter
Bases: EventEmitter
An event emitter class which runs handlers in a concurrent.futures
executor.
By default, this class creates a default ThreadPoolExecutor, but
a custom executor may also be passed in explicitly to, for instance,
use a ProcessPoolExecutor instead.
This class runs all emitted events on the configured executor. Errors
captured by the resulting Future are automatically emitted on the
error event. This is unlike the EventEmitter, which have no error
handling.
The underlying executor may be shut down by calling the shutdown
method. Alternately you can treat the event emitter as a context manager:
with ExecutorEventEmitter() as ee:
# Underlying executor open
@ee.on('data')
def handler(data):
print(data)
ee.emit('event')
# Underlying executor closed
Since the function call is scheduled on an executor, emit is always non-blocking.
No effort is made to ensure thread safety, beyond using an executor.
Source code in pyee/executor.py
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | |
shutdown(wait=True)
Call shutdown on the internal executor.
Source code in pyee/executor.py
72 73 74 75 | |
pyee.trio
TrioEventEmitter
Bases: EventEmitter
An event emitter class which can run trio tasks in a trio nursery.
By default, this class will lazily create both a nursery manager (the
object returned from trio.open_nursery() and a nursery (the object
yielded by using the nursery manager as an async context manager). It is
also possible to supply an existing nursery manager via the manager
argument, or an existing nursery via the nursery argument.
Instances of TrioEventEmitter are themselves async context managers, so that they may manage the lifecycle of the underlying trio nursery. For example, typical usage of this library may look something like this::
async with TrioEventEmitter() as ee:
# Underlying nursery is instantiated and ready to go
@ee.on('data')
async def handler(data):
print(data)
ee.emit('event')
# Underlying nursery and manager have been cleaned up
Unlike the case with the EventEmitter, all exceptions raised by event
handlers are automatically emitted on the error event. This is
important for trio coroutines specifically but is also handled for
synchronous functions for consistency.
For trio coroutine event handlers, calling emit is non-blocking. In other words, you should not attempt to await emit; the coroutine is scheduled in a fire-and-forget fashion.
Source code in pyee/trio.py
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | |
context()
async
Returns an async contextmanager which manages the underlying
nursery to the EventEmitter. The TrioEventEmitter's
async context management methods are implemented using this
function, but it may also be used directly for clarity.
Source code in pyee/trio.py
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | |
pyee.uplift
unwrap(event_emitter)
Unwrap an uplifted EventEmitter, returning it to its prior state.
Source code in pyee/uplift.py
17 18 19 20 | |
uplift(cls, underlying, error_handling='new', proxy_new_listener='forward', *args, **kwargs)
A helper to create instances of an event emitter cls that inherits
event behavior from an underlying event emitter instance.
This is mostly helpful if you have a simple underlying event emitter
that you don't have direct control over, but you want to use that
event emitter in a new context - for example, you may want to uplift a
EventEmitter supplied by a third party library into an
AsyncIOEventEmitter so that you may register async event handlers
in your asyncio app but still be able to receive events from the
underlying event emitter and call the underlying event emitter's existing
handlers.
When called, uplift instantiates a new instance of cls, passing
along any unrecognized arguments, and overwrites the emit method on
the underlying event emitter to also emit events on the new event
emitter and vice versa. In both cases, they return whether the emit
method was handled by either emitter. Execution order prefers the event
emitter on which emit was called.
The unwrap function may be called on either instance; this will
unwrap both emit methods.
The error_handling flag can be configured to control what happens to
unhandled errors:
- 'new': Error handling for the new event emitter is always used and the underlying library's non-event-based error handling is inert.
- 'underlying': Error handling on the underlying event emitter is always used and the new event emitter can not implement non-event-based error handling.
- 'neither': Error handling for the new event emitter is used if the handler was registered on the new event emitter, and vice versa.
Tuning this option can be useful depending on how the underlying event emitter does error handling. The default is 'new'.
The proxy_new_listener option can be configured to control how
new_listener events are treated:
- 'forward':
new_listenerevents are propagated from the underlying event emitter to the new event emitter but not vice versa. - 'both':
new_listenerevents are propagated as with other events. - 'neither':
new_listenerevents are only fired on their respective event emitters. - 'backward':
new_listenerevents are propagated from the new event emitter to the underlying event emitter, but not vice versa.
Tuning this option can be useful depending on how the new_listener
event is used by the underlying event emitter, if at all. The default is
'forward', since underlying may not know how to handle certain
handlers, such as asyncio coroutines.
Each event emitter tracks its own internal table of handlers.
remove_listener, remove_all_listeners and listeners all
work independently. This means you will have to remember which event
emitter an event handler was added to!
Note that both the new event emitter returned by cls and the
underlying event emitter should inherit from EventEmitter, or at
least implement the interface for the undocumented _call_handlers and
_emit_handle_potential_error methods.
Source code in pyee/uplift.py
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 | |
pyee.cls
evented(cls)
Configure an evented class.
Evented classes are classes which use an EventEmitter to call instance
methods during runtime. To achieve this without this helper, you would
instantiate an EventEmitter in the __init__ method and then call
event_emitter.on for every method on self.
This decorator and the on function help make things look a little nicer
by defining the event handler on the method in the class and then adding
the __init__ hook in a wrapper:
from pyee.cls import evented, on
@evented
class Evented:
@on("event")
def event_handler(self, *args, **kwargs):
print(self, args, kwargs)
evented_obj = Evented()
evented_obj.event_emitter.emit(
"event", "hello world", numbers=[1, 2, 3]
)
The __init__ wrapper will create a self.event_emitter: EventEmitter
automatically but you can also define your own event_emitter inside your
class's unwrapped __init__ method. For example, to use this
decorator with a TwistedEventEmitter::
@evented
class Evented:
def __init__(self):
self.event_emitter = TwistedEventEmitter()
@on("event")
async def event_handler(self, *args, **kwargs):
await self.some_async_action(*args, **kwargs)
Source code in pyee/cls.py
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | |
on(event)
Register an event handler on an evented class. See the evented class
decorator for a full example.
Source code in pyee/cls.py
31 32 33 34 35 36 37 38 39 40 41 | |