pyobservable is a minimalist event system for python. It provides you an easy-to-use interface to trigger arbitrary functions when specific events occur.
from observable import Observable
obs = Observable()
@obs.on("error")
def error_handler(message):
# do some fancy error handling
logging.error(f"An error occured: {message}")
...
def do_time_travel():
# do some time traveling
...
if year != 1291:
obs.trigger("error", "Time travel to 1291 didn't work")
An ObservableProperty
type is included as well, which makes observing
object properties a breeze.
Note: We are Python 3 only! Only Python Versions >= 3.5 are supported. Use v0.3.2 for older Python Versions.
Use a pip
to install it from PyPI:
pip install observable
After completion you can start using observable
:
from observable import Observable
obs = Observable()
There are two ways to register a function to an event.
The first way is to register the event with a decorator like this:
@obs.on("error")
def error_func(message):
print("Error: %s" % message)
The second way is to register it with a method call:
def error_func(message):
print("Error: %s" % message)
obs.on("error", error_func)
once
works like on
, but once the event handler is triggered it will be removed and cannot be triggered again.
You can trigger a registered event with the trigger
method:
obs.trigger("error", "This is my error message")
If no handler for the event error
could be found an Observable.NoHandlerFound
-Exception will be raised.
Remove a handler from a specified event:
obs.off("error", error_func)
obs.off("error", [error_func, second_error_func])
Remove all handlers from a specified event:
obs.off("error")
Clear all events:
obs.off()
Imagine you registered the following handlers:
@obs.on("success")
def success_func():
print("Success!")
@obs.on("error")
def error_func(message):
print("Error: %s" % message)
Then you can do the following to inspect the registered handlers:
>>> obs.get_all_handlers()
{'success': [<function success_func at 0x7f7f32d0a1e0>], 'error': [<function error_func at 0x7f7f32d0a268>]}
>>> obs.get_handlers("success")
[<function success_func at 0x7f7f32d0a1e0>]
>>> obs.get_handlers("other_event")
[]
A property that can be observed easily by listening for some special,
auto-generated events. Its API is identical to that of the build-in
property
.
The ObservableProperty
needs an Observable
object for
triggering events. If the property is an attribute of an object of
type Observable
, that one will be used automatically. To specify a
different Observable
object, pass it as the observable
keyword
argument when initializing the ObservableProperty
. You may also pass
a string for observable
, which looks for an attribute of that
name in the containing object. This is useful to specify the name of an
attribute which will only be created at object initialization and thus
isn't there when defining the property.
The following events, of which "after_set_<name>"
is probably the
one used most, are triggered:
"before_get_<name>"()
and"after_get_<name>"(value)
"before_set_<name>"(value)
and"after_set_<name>"(value)
"before_del_<name>"()
and"after_del_<name>"()
<name>
has to be replaced with the property's name. Note that names
are taken from the individual functions supplied as getter, setter and
deleter, so please name those functions like the property itself.
Alternatively, the name to use can be specified with the name
keyword argument when initializing the ObservableProperty.
The convenience helper ObservableProperty.create_with()
can be used
as a decorator for creating ObservableProperty
objects with custom
event
and/or observable
. It returns a functools.partial()
with the chosen attributes pre-set.
Here's an example for using the event
and observable
keyword
arguments:
>>> from observable import Observable
>>> from observable.property import ObservableProperty
>>> class MyObject:
... def __init__(self):
... self.events = Observable()
... self._value = 10
... @ObservableProperty.create_with(event="prop", observable="events")
... def some_obscure_name(self):
... return self._value
... @some_obscure_name.setter
... def some_obscure_name(self, value):
... self._value += value
...
>>> obj = MyObject()
>>> obj.obs.on("after_get_prop", lambda v: print("got", v))
>>> obj.obs.on("before_set_prop",
... lambda v: print("setting", obs.some_obscure_name, v))
>>> obj.obs.on("after_set_prop", lambda v: print("set", v))
>>> obj.some_obscure_name = 32
got 10
setting 10 32
set 32
>>> obj.some_obscure_name
got 42
42
This project is published under MIT.
A Timo Furrer project.
- 🎉 -