class EventDispatcher: __event_stack = {} def register_event_type(self, event_type): """Register an event type with the dispatcher. Registering event types allows the dispatcher to validate event handler names as they are attached and to search attached objects for suitable handlers. Each event type declaration must: 1. start with the prefix `on_`. 2. have a default handler in the class. """ if event_type[:3] != 'on_': raise Exception('A new event must start with "on_"') # Ensure that the user has at least declared the default handler if not hasattr(self, event_type): raise Exception(f'Missing default handler {event_type} in {self.__class__.__name__}') # Add the event type to the stack if event_type not in self.__event_stack: self.__event_stack[event_type] = [] def unregister_event_type(self, event_type): pass def bind(self, **kwargs): for key, value in kwargs.items(): assert callable(value), f'{value!r} is not callable' if key[:3] == 'on_': observers: list = self.__event_stack.get(key) if observers is None: continue observers.append(value) def unbind(self, **kwargs): """Unbind event from callback functions with similar usage as""" for key, value in kwargs.items(): if key[:3] == 'on_': observers: list = self.__event_stack.get(key) if observers is None: continue observers.remove(value) def is_event_type(self, event_type): """Return True if the event_type is already registered.""" return event_type in self.__event_stack def dispatch(self, event_type, *args, **kwargs): """Dispatch an event across all the handlers added in bind/fbind(). As soon as a handler returns True, the dispatching stops. The function collects all the positional and keyword arguments and passes them on to the handlers. .. note:: The handlers are called in reverse order than they were registered with :meth:`bind`. :Parameters: `event_type`: str the event name to dispatch. .. versionchanged:: 1.9.0 Keyword arguments collection and forwarding was added. Before, only positional arguments would be collected and forwarded. """ observers: list = self.__event_stack.get(event_type) if observers: observers.reverse() for callbacks in observers: callbacks(*args, **kwargs) handler = getattr(self, event_type) return handler(*args, **kwargs)