feat: support custom Python events with optional cancellation#368
Merged
wu-vincent merged 4 commits intomainfrom Apr 7, 2026
Merged
feat: support custom Python events with optional cancellation#368wu-vincent merged 4 commits intomainfrom
wu-vincent merged 4 commits intomainfrom
Conversation
Previously, Python plugins could not define custom events — only C++ events exposed via pybind11 were available. This adds a PyEvent trampoline class that allows Python subclasses of Event to work correctly, and moves Cancellable to a pure Python mixin so custom events can opt into cancellation via multiple inheritance. Also removes the deprecated `cancelled` property (replaced by `is_cancelled` in a prior release).
register_events and PyEvent::getEventName() now agree on naming: built-in events (endstone.*) use the short class name, custom plugin events use the fully qualified module.qualname to avoid collisions. Also adds runtime integration tests for custom event fire/handle cycle including cancellation semantics via plugin manager.
EventHandler::callEvent() was reading the C++ cancelled_ field directly, but custom Python events store cancellation state in a Python attribute. Use virtual dispatch so PyEvent::isCancelled() is called correctly.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
PyEventtrampoline class (event.h) enabling Python subclasses ofEventto work through pybind11 — previously custom events were not possible in the Python APICancellableto a pure Python mixin so custom events can opt into cancellation viaclass MyEvent(Event, Cancellable)multiple inheritancecancelledproperty (replaced byis_cancelledin a prior release)module.qualnameto avoid collisionsEventHandlerdispatch — use virtualisCancelled()onEventso cancellation state is read correctly for both C++ and Python eventstests/test_event.py) and runtime integration tests (tests/endstone_test/.../test_custom_event.py)Example
Design
Cancellable<T>template +ICancellablepybind11 bindingPyEventas the trampoline, which implementsICancellableby reading/writing the_cancelledattribute on the Python object — consistent with the PythonCancellablemixinPyEvent::isCancellable()checks at runtime whether the Python class inherits from the PythonCancellablemixinEventnow has a private virtualisCancelled()soEventHandlerdispatches correctly for both C++ (readscancelled_field) and Python events (reads Python attribute) via vtableTest plan
pytest tests/test_event.py— unit tests for custom event name, async flag, cancellation, un-cancellation, and async+cancellabletest_custom_event.py) — fire/handle cycle through plugin manager, cancellation priority semanticsctest) unaffected🤖 Generated with Claude Code