Axel - Python events handling
Documentation
Version 0.0.5
Last updated: March 9th, 2016
Description, Features & Download
Axel is a very small Python library, inspired by C#
events, that allows an easy and elegant event handling.
Features:
- handlers can receive the sender as their first argument
- handlers can be executed in the same or separate threads
- handlers may be executed synchronous or asynchronous
- handlers execution may be synchronized on a lock
- the time allocated for the execution of a handler is controllable
- the number of actively executed handlers at one time is controllable
- the execution result of a handler can be memoized
- the result of the execution is provided as a tuple ((flag, result, handler),)
- in case of error, the traceback can be provided for each handler in error
Download
The latest version of Axel can be downloaded from
https://pypi.python.org/pypi/axel
Examples
from axel import Event
event = Event()
def on_event(*args, **kwargs):
return (args, kwargs)
event += on_event # handler registration
print(event(10, 20, y=30))
>> ((True, ((10, 20), {'y': 30}), <function on_event at 0x00BAA270>),)
event -= on_event # handler is unregistered
print(event(10, 20, y=30))
>> None
class Mouse(object):
def __init__(self):
self.click = Event(self)
self.click += self.on_click
def on_click(self, sender, *args, **kwargs):
assert isinstance(sender, Mouse), 'Wrong sender'
return (args, kwargs)
mouse = Mouse()
print(mouse.click(10, 20))
>> ((True, ((10, 20), {})
>> <bound method Mouse.on_click of <__main__.Mouse object at 0x00B6F470>>),)
mouse.click -= mouse.on_click
print(mouse.click(10, 20))
>> None
Events can trigger further events, if necessary:
from axel import Event
class Mouse(object):
def __init__(self):
self.click = Event(self)
self.move = Event(self)
self.click += self.on_click
self.move += self.on_move
self.click += self.move # triggers event 'move' on call
def on_click(self, sender, x, y):
assert isinstance(sender, Mouse)
return ('on_click', x, y)
def on_move(self, sender, x, y):
assert isinstance(sender, Mouse)
return ('on_move', x, y)
m = Mouse()
res = m.click(10, 20)
print(res)
>>((True,
>> ('on_click', 10, 20),
>> <bound method Mouse.on_click of <__main__.Mouse object at 0x00BBCED0>>),
>> (True,
>> ((True,
>> ('on_move', 10, 20),
>> <bound method Mouse.on_move of <__main__.Mouse object at 0x00BBCED0>>),),
>> <axel.axel.Event object at 0x00BBCF50>))
The Event class
Constructor's arguments
Event is the core class of the implementation. The usage is very
straightforward and all the features are controlled by the constructor's
arguments. The arguments are:
- asynch - if True, handlers are executes asynchronous
- exc_info - if True, result will contain sys.exc_info()[:2] on error
- lock - threading.RLock used to synchronize execution
- sender - event's sender. The sender is passed as the first argument to the
handler, only if is not None. For this case the handler must have
a placeholder in the arguments to receive the sender
- threads - maximum number of threads that will be started. If threads = 0,
handlers are executed in the same thread as the event
- traceback - if True, the execution result will contain sys.exc_info()
on error. exc_info must be also True to get the traceback
Although most of the arguments are Event related, there is a way to pass two
arguments when a handler is registered: one will determine if the execution
result of the current handler will be cached; the second one will allocate
a predefined time interval for execution.
event += handler or
event += (handler, True, .3) or
event += {'handler':handler, 'memoize':True, 'timeout':.3}
Upon the call of the event, the execution results of registered handlers are returned
as a tuple. The content of the tuple is determined by the Event arguments and
the result itself. For each executed handler, the tuple will contain an entry with the
following structure:
(flag, result, handler)
- The flag will be True on success,
False on error and None for asynchronous executions
- The result will contain the actual result on success
or the error
- On error, the content is determined by exc_info and traceback arguments
(True, result, handler), # success
(False, exc_info, handler), # error
(None, None, handler), ... # asynchronous execution
Methods
The Event class has the follwing methods:
- handle (alias for __iadd__) - register a handler
- unhandle (alias for __isub__) - unregister a handler
- fire (alias for __call__) - call the event
- count (alias for __len__) - return the handler count
- clear - removes all the handlers and cached results
Structures
The following structures are used to store events, cache and return results:
hash = hash(handler)
handlers = {
hash : (handler, memoize, timeout),
hash : (handler, memoize, timeout), ...
}
memoize = {
hash : ((args, kwargs, result), (args, kwargs, result), ...),
hash : ((args, kwargs, result), ...), ...
}
exec_result = (
(True, result, handler), # success
(False, ecx_info, handler), # error
(None, None, handler), ... # asynchronous execution
)