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:

  1. Retrieves handlers registered for the exact event.event_type
  2. Extends the list with wildcard handlers via self.handlers.get('*', [])
  3. 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 in EventBus.on()
  • The _get_applicable_handlers method in bubus/service.py explicitly merges self.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_loop prevents infinite cycles when forwarding between buses
  • Filter events inside the handler by checking event.event_type when 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →