Define class with instance methods marked with callback.callback_regisry decorator:
from callback import callback_registry
class SomeClass:
@callback_registry
def on_something(self, arg1: int, arg2: bool) -> None:
pass
def method(self, arg: int) -> None:
self.on_something(arg, arg == 42)Each time SomeClass.on_something is called all registered handlers will be invoked. Handlers can be registered using += operator and removed by -= operator:
obj = SomeClass()
def handler(owner: SomeClass, arg1: int, arg2: bool) -> None:
print('Handler called')
# register handler
obj.on_something += handler
print('Calling...')
obj.method(42)
# remove handler
obj.on_something -= handlerIt is possible to register many handlers for single callback, they will be called in order. Any exception raised by single handler will be propagated and will prevent remaining handlers from running.
In some cases it is useful to execute callbacks in specific way, e.g. execute them asynchronously in some kind of work queue. Each class can provide custom caller for callbacks which will be used automatically, class have to inherit from callback.ICallbackCaller and implement call_callback method.
from callback import ICallbackCaller, callback_registry
from typing import Callable
from work_queue import post_to_queue
class SomeClass(ICallbackCaller):
@callback_registry
def on_something(self, arg1: int, arg2: bool) -> None:
pass
def method(self, arg: int) -> None:
self.on_something(arg, arg == 42)
def call_callback(self, target: Callable[[], None]) -> None:
post_to_queue(lambda: target())It is not possible to implement strict typing for callback registry using pure Python type hints. For mypy users this package includes plugin that provides support for type checking callbacks.
Plugin can be enabled by adding following snippet to mypy config file:
[mypy]
plugins = callback._mypy.plugin