# How to Use Wildcard Event Subscriptions in Bubus

> Master Bubus wildcard event subscriptions with bus.on('*', handler) to capture all events. Learn to manage every event dispatched through the bus effectively.

- Repository: [Browser Use/bubus](https://github.com/browser-use/bubus)
- Tags: how-to-guide
- Published: 2026-02-26

---

**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`](https://github.com/browser-use/bubus/blob/main/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`](https://github.com/browser-use/bubus/blob/main/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`](https://github.com/browser-use/bubus/blob/main/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:

```python
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`](https://github.com/browser-use/bubus/blob/main/tests/test_eventbus.py) (lines 69-92) demonstrates this pattern:

```python
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`](https://github.com/browser-use/bubus/blob/main/tests/test_eventbus.py) (lines 150-160):

```python
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:

```python
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`](https://github.com/browser-use/bubus/blob/main/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`](https://github.com/browser-use/bubus/blob/main/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.