hbutils.design.observer
Observer pattern implementation for event-driven subscriptions.
This module implements a lightweight observer pattern utility centered on the
Observable class, enabling objects (subscribers) to register callbacks
for specific events and receive notifications when those events are dispatched.
The module contains the following public components:
Observable- Event dispatcher supporting subscriptions and notifications.
Note
Events can be defined as an enum.Enum subclass or as an iterable
of event identifiers (e.g., strings, integers, or enum members).
Example:
>>> from enum import Enum, unique
>>> from hbutils.design.observer import Observable
>>>
>>> @unique
... class Event(Enum):
... READY = 'ready'
... DONE = 'done'
>>>
>>> observable = Observable(Event)
>>> received = []
>>> observable.subscribe(Event.READY, received, 'append')
>>> observable.dispatch(Event.READY)
>>> received
[<Event.READY: 'ready'>]
__all__
- hbutils.design.observer.__all__ = ['Observable']
Built-in mutable sequence.
If no argument is given, the constructor creates a new empty list. The argument must be an iterable if specified.
Observable
- class hbutils.design.observer.Observable(events: Type[Enum] | list | tuple)[source]
Observable object implementing the observer pattern.
The observable holds a set of events, and subscribers may register callbacks for each event. When an event is dispatched, all associated callbacks are invoked with the event and the observable instance as arguments.
subscribe()can be used for subscribing to a specific event.unsubscribe()can be used for unsubscribing from a specific event.dispatch()can be used for broadcasting a specific event, and all the subscribed callbacks will be triggered.
- Examples::
>>> from enum import IntEnum, unique >>> from hbutils.design import Observable >>> >>> @unique ... class MyIntEnum(IntEnum): ... A = 1 ... B = 2 >>> >>> o = Observable(MyIntEnum) >>> list_a, list_b = [], [] >>> o.subscribe(MyIntEnum.A, list_a, 'append') # use list_a.append >>> o.subscribe(MyIntEnum.B, list_a, lambda v: list_a.append(v)) # custom function. >>> o.subscribe(MyIntEnum.A, list_b, 'append') # use list_b.append >>> >>> list_a, list_b ([], []) >>> o.dispatch(MyIntEnum.A) >>> list_a, list_b ([<MyIntEnum.A: 1>], [<MyIntEnum.A: 1>]) >>> o.dispatch(MyIntEnum.B) >>> list_a, list_b ([<MyIntEnum.A: 1>, <MyIntEnum.B: 2>], [<MyIntEnum.A: 1>]) >>> >>> o.unsubscribe(MyIntEnum.A, list_a) >>> o.dispatch(MyIntEnum.A) >>> list_a, list_b ([<MyIntEnum.A: 1>, <MyIntEnum.B: 2>], [<MyIntEnum.A: 1>, <MyIntEnum.A: 1>]) >>> o.dispatch(MyIntEnum.B) >>> list_a, list_b ([<MyIntEnum.A: 1>, <MyIntEnum.B: 2>, <MyIntEnum.B: 2>], [<MyIntEnum.A: 1>, <MyIntEnum.A: 1>])
- __init__(events: Type[Enum] | list | tuple) None[source]
Construct an
Observable.- Parameters:
events (_EventSetType) – Set of events, can be a list, tuple, or an enum class.
Note
When an enum class is used, its members will be used as events. For example:
>>> from enum import IntEnum >>> from hbutils.design import Observable >>> >>> class MyIntEnum(IntEnum): ... A = 1 ... B = 2 >>> >>> # equals to `Observable([MyIntEnum.A, MyIntEnum.B])` ... o = Observable(MyIntEnum) >>> o._events # just for explanation, do not do this on actual use {<MyIntEnum.A: 1>: {}, <MyIntEnum.B: 2>: {}}
- dispatch(event: _EventType) None[source]
Dispatch an event to all subscribers.
This method triggers all callbacks subscribed to the given event, passing the event and the observable instance as arguments.
- Parameters:
event (_EventType) – Event to be dispatched.
- subscribe(event: _EventType, subscriber: _SubscriberType, callback: _CallbackType | str | None = None) None[source]
Subscribe to the given
event.- Parameters:
event (_EventType) – Event to be subscribed.
subscriber (_SubscriberType) – Subscriber of this subscription.
callback (Union[_CallbackType, str, None]) – Callback function. If
stris given, the method with this name onsubscriberwill be used. Default isNonewhich means theupdatemethod onsubscriberwill be used.
- Raises:
TypeError – If the callback is not callable.
Note
Callback functions should accept no more than 2 positional arguments. For example:
>>> o.subscribe(MyIntEnum.A, 'user1', lambda: 2) # ok >>> o.subscribe(MyIntEnum.A, 'user2', lambda event: 2) # ok >>> o.subscribe(MyIntEnum.A, 'user3', lambda event, obs: 2) # ok >>> o.subscribe(MyIntEnum.A, 'user4', lambda x, y, z: 2) # invalid
- subscribers(event: _EventType) List[_SubscriberType][source]
Get subscribers of the given
event.- Parameters:
event (_EventType) – Event for querying.
- Returns:
A list of subscribers.
- Return type:
List[_SubscriberType]
- subscriptions(event: _EventType) List[Tuple[_SubscriberType, _CallbackType]][source]
Get subscriptions of the given
event.- Parameters:
event (_EventType) – Event for querying.
- Returns:
A list of tuples with subscribers and their callbacks.
- Return type:
List[Tuple[_SubscriberType, _CallbackType]]
- unsubscribe(event: _EventType, subscriber: _SubscriberType) None[source]
Unsubscribe from the given
event.- Parameters:
event (_EventType) – Event to be unsubscribed.
subscriber (_SubscriberType) – Subscriber of this unsubscription.
- Raises:
KeyError – If the subscriber is not found for the given event.