Skip to content

feat: add EventBus.remove() to unsubscribe handlers - #35

Open
HankGrimm wants to merge 1 commit into
browser-use:mainfrom
HankGrimm:feat/eventbus-remove-handler
Open

HankGrimm wants to merge 1 commit into
browser-use:mainfrom
HankGrimm:feat/eventbus-remove-handler

Conversation

@HankGrimm

@HankGrimm HankGrimm commented Sep 11, 2026

Copy link
Copy Markdown

What

Add a public EventBus.remove(event_pattern, handler) method so callers can unsubscribe a handler registered with EventBus.on(). Returns True if the handler was found and removed, False otherwise.

Also extract the event-key resolution from on() into _get_event_key() and reuse it in on() and remove(), and refactor expect() cleanup to use remove().

Why

There is no way to unsubscribe after EventBus.on() — the only cleanup path is expect()'s internal self.handlers[...].remove(...). Closes #5.

How tested

  • Added tests/test_remove_handler.py (4 cases: by class, by string name, scoped removal, wildcard *).
  • Core suite: 63 passed.

🤖 Generated with Claude Code


Summary by cubic

Adds EventBus.remove() to unsubscribe a handler registered with EventBus.on(). Returns True if the handler was found and removed, False otherwise. Closes #5.

Changes

  • Extracts event-key resolution into _get_event_key() and reuses it in on(), remove(), and expect() cleanup.
  • Adds tests covering removal by class, string name, scoped handlers, and * wildcard.

Written for commit 72b92d6. Summary will update on new commits.

Review in cubic

Add a public EventBus.remove(event_pattern, handler) method so callers can
unsubscribe a handler registered with EventBus.on(). Returns True if the
handler was found and removed, False otherwise.

Extract event-key resolution from on() into _get_event_key() and reuse it in
on() and remove(), and refactor expect() cleanup to use remove().

Co-Authored-By: Claude Code <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 2 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="bubus/service.py">

<violation number="1" location="bubus/service.py:456">
P3: After removing the last handler for an event key, the key remains in `self.handlers` as an empty list because the method never cleans up the entry. Since `handlers` is a `defaultdict(list)`, repeated remove()/expect() calls leave many empty keys, which accumulates over the bus lifetime and inflates `len(self.handlers)` shown in `__str__`. Delete the key when its list becomes empty.</violation>

<violation number="2" location="bubus/service.py:483">
P2: When a handler is typed for a concrete event, strict type checking rejects `remove(PingEvent, handler)` even though `on(PingEvent, handler)` accepts it. Add `remove()` overloads matching `on()` or otherwise preserve the handler's concrete event type.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment thread bubus/service.py
return event_pattern.__name__ # pyright: ignore[reportUnknownMemberType, reportUnknownVariableType]
return str(event_pattern)

def remove(self, event_pattern: EventPatternType, handler: ContravariantEventHandler['BaseEvent[Any]']) -> bool:

@cubic-dev-ai cubic-dev-ai Bot Sep 11, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: When a handler is typed for a concrete event, strict type checking rejects remove(PingEvent, handler) even though on(PingEvent, handler) accepts it. Add remove() overloads matching on() or otherwise preserve the handler's concrete event type.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At bubus/service.py, line 483:

<comment>When a handler is typed for a concrete event, strict type checking rejects `remove(PingEvent, handler)` even though `on(PingEvent, handler)` accepts it. Add `remove()` overloads matching `on()` or otherwise preserve the handler's concrete event type.</comment>

<file context>
@@ -482,6 +472,38 @@ def on(
+            return event_pattern.__name__  # pyright: ignore[reportUnknownMemberType, reportUnknownVariableType]
+        return str(event_pattern)
+
+    def remove(self, event_pattern: EventPatternType, handler: ContravariantEventHandler['BaseEvent[Any]']) -> bool:
+        """
+        Unsubscribe a previously registered handler from events matching a pattern.
</file context>
Fix with cubic

Comment thread bubus/service.py

# Ensure event_key is definitely a string at this point
assert isinstance(event_key, str)
event_key = self._get_event_key(event_pattern)

@cubic-dev-ai cubic-dev-ai Bot Sep 11, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: After removing the last handler for an event key, the key remains in self.handlers as an empty list because the method never cleans up the entry. Since handlers is a defaultdict(list), repeated remove()/expect() calls leave many empty keys, which accumulates over the bus lifetime and inflates len(self.handlers) shown in __str__. Delete the key when its list becomes empty.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At bubus/service.py, line 456:

<comment>After removing the last handler for an event key, the key remains in `self.handlers` as an empty list because the method never cleans up the entry. Since `handlers` is a `defaultdict(list)`, repeated remove()/expect() calls leave many empty keys, which accumulates over the bus lifetime and inflates `len(self.handlers)` shown in `__str__`. Delete the key when its list becomes empty.</comment>

<file context>
@@ -453,17 +453,7 @@ def on(
-
-        # Ensure event_key is definitely a string at this point
-        assert isinstance(event_key, str)
+        event_key = self._get_event_key(event_pattern)
 
         # Check for duplicate handler names
</file context>
Fix with cubic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for unsubscribing after using EventBus.on

1 participant