How to Use Wildcard Event Subscriptions in Bubus
Use bus.on('*', handler) to register a wildcard event subscription in Bubus that captures every event dispatched through the bus regardless of its specific type.
The browser-use/bubus repository provides a lightweight asynchronous event bus for Python applications. Wildcard event subscriptions enable powerful cross-cutting patterns like universal logging, metrics collection, and event forwarding across multiple bus instances. This guide explains the internal dispatch mechanism and practical implementation based on the actual source code in bubus/service.py.
How Wildcard Subscriptions Work Internally
When you register a handler using the string '*' as the event pattern, Bubus treats it as a universal subscription that applies to all event types. The dispatch logic explicitly merges these wildcard handlers with type-specific ones before execution.
Handler Registration via on()
The EventBus.on(event_pattern, handler) method stores handlers in the self.handlers dictionary using the pattern as the key. When you pass '*', the handler is appended to self.handlers['*'] according to the implementation in bubus/service.py.
The Discovery Logic in _get_applicable_handlers
During event dispatch, the private method _get_applicable_handlers (lines 1020-1024 in bubus/service.py) constructs the execution set through three specific steps:
- Retrieves handlers registered for the exact
event.event_type - Extends the list with wildcard handlers via
self.handlers.get('*', []) - Filters out any handlers that would create a circular loop using
_would_create_loop
Because the wildcard list is merged with type-specific handlers, universal handlers execute alongside any targeted subscribers for the same event. The handler receives the concrete event instance, allowing inspection of event.event_type and other fields at runtime.
Practical Implementation Examples
Registering a Universal Logger
Subscribe to all events for centralized logging or debugging:
from bubus import EventBus, BaseEvent
bus = EventBus()
async def universal_handler(event: BaseEvent) -> None:
# Runs for every event that passes through the bus
print(f'🔔 Universal handler got {event.event_type}')
# Subscribe to all events using the wildcard pattern
bus.on('*', universal_handler)
Combining Specific and Wildcard Handlers
You can register both targeted and universal handlers simultaneously. The test suite in tests/test_eventbus.py (lines 69-92) demonstrates this pattern:
from bubus import EventBus, BaseEvent
bus = EventBus()
async def user_handler(event):
print('UserEvent:', event.username)
async def all_handler(event: BaseEvent):
print('All events:', event.event_type)
bus.on('UserActionEvent', user_handler) # specific subscription
bus.on('*', all_handler) # wildcard subscription
bus.dispatch(UserActionEvent(username='alice'))
bus.dispatch(SystemEventModel(event_name='startup'))
# Output:
# UserEvent: alice
# All events: UserActionEvent
# All events: SystemEventModel
Forwarding Events Between Buses
Wildcard subscriptions are the canonical mechanism for event bus meshing. The pattern appears in tests/test_eventbus.py (lines 150-160):
from bubus import EventBus
main_bus = EventBus(name='Main')
auth_bus = EventBus(name='Auth')
data_bus = EventBus(name='Data')
# Forward everything from Main → Auth → Data
main_bus.on('*', auth_bus.dispatch) # every event on Main gets sent to Auth
auth_bus.on('*', data_bus.dispatch) # every event on Auth gets sent to Data
The dispatch method of each downstream bus acts as the wildcard handler for the upstream bus. The internal _would_create_loop check prevents infinite recursion if your topology contains cycles.
Filtering Inside Wildcard Handlers
Since wildcard handlers receive all events, implement internal guards to process only relevant subsets:
async def selective_handler(event: BaseEvent):
if event.event_type == 'LoginEvent':
print('Login detected!')
elif event.event_type == 'LogoutEvent':
print('Logout detected!')
bus.on('*', selective_handler)
Critical Implementation Details
Loop Prevention with _would_create_loop
Before adding any handler (including wildcards) to the final execution set, Bubus calls _would_create_loop to detect circular forwarding chains. This ensures that an event processed by a wildcard forwarder will not endlessly bounce between interconnected buses.
Execution Order and Parallelism
Wildcards receive no special priority. Handlers execute in the order they were registered within their respective lists (type-specific vs. wildcard), and the bus respects the parallel_handlers configuration setting. When enabled, all applicable handlers—including wildcards—run concurrently.
Summary
- Wildcard subscriptions use the string literal
'*'as the event pattern inEventBus.on() - The
_get_applicable_handlersmethod inbubus/service.pyexplicitly mergesself.handlers.get('*', [])with type-specific handlers - Wildcard handlers execute alongside specific handlers for the same event, not instead of them
- Internal loop detection via
_would_create_loopprevents infinite cycles when forwarding between buses - Filter events inside the handler by checking
event.event_typewhen you need selective processing
Frequently Asked Questions
Do wildcard handlers execute before or after specific handlers?
Bubus does not prioritize wildcards over specific handlers or vice versa. The _get_applicable_handlers method extends the type-specific list with the wildcard list, and the combined set executes according to the bus's parallel_handlers setting. When running serially, handlers execute in registration order within their respective groups.
Can I register multiple wildcard handlers on the same bus?
Yes. The self.handlers['*'] entry is a list, allowing you to register multiple universal handlers via repeated calls to bus.on('*', handler). All registered wildcard handlers will be retrieved and executed during every dispatch cycle.
How does Bubus prevent infinite loops when using wildcards for forwarding?
The _would_create_loop method checks the handler chain before execution. If a wildcard subscription would cause an event to cycle back to a previously visited bus (for example, main_bus → auth_bus → main_bus), the loop-creating handler is filtered out of the applicable set, ensuring each event is processed only once per bus instance.
Does the wildcard pattern support partial matches like 'user.*'?
No. Bubus implements '*' as a special universal key rather than a glob pattern. The source code in bubus/service.py specifically looks up the literal string '*' in the handlers dictionary. For selective filtering, register a wildcard handler and inspect event.event_type inside your function to match against your own naming conventions.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →