This section covers the two methods developers usually need before emitting any event:
qualify_event_name(...)get_event_slots(...)
It also explains how these methods relate to @event_slot(...) and @event_listener(...), because the facade only makes sense in the context of those decorators.
Relative Names vs Fully Qualified Names¶
In this SDK, event names are usually treated as module-scoped keys.
If your module is auth and you talk about login.succeeded, the fully qualified event name is usually:
auth.login.succeededThis is why the SDK exposes qualify_event_name(...): module authors often want to work with the short relative name inside the producing module, while listeners in other modules usually subscribe to the fully qualified key.
qualify_event_name(name: str) -> str¶
This method returns the fully qualified event name for the current SDK module.
Example:
qualified = module_sdk.events.qualify_event_name("login.succeeded")
# -> "auth.login.succeeded"What It Is For¶
Use this method when you need the canonical event key rather than the short module-local name.
Typical cases:
- logging the exact emitted event name
- storing an event key in metadata or configuration
- building cross-module documentation
- checking that a listener is targeting the same key your module will emit
How It Works¶
The method applies a simple rule:
- if the current module is
core, it leaves the name unchanged - if the name is already prefixed with the current module, it leaves it unchanged
- otherwise it prefixes the name with
<module_name>.
That means:
module_sdk.events.qualify_event_name("order.completed")
# "shop.order.completed"
module_sdk.events.qualify_event_name("shop.order.completed")
# "shop.order.completed"Why You Should Care¶
Without qualification rules, producers and listeners drift very quickly.
One module emits login.succeeded, another listens for auth.login.succeeded, a third listens for login_success, and now the contract is broken even though everyone thinks they are talking about the same business event.
qualify_event_name(...) helps keep the producer side deterministic.
@event_slot(...) And Producer Contracts¶
Although event_slot is documented under the decorators domain, it is part of the practical event model and belongs in your mental model when using sdk.events.
An event slot declares:
- the event name
- the expected payload keys
- whether listeners are optional
- a human-readable description
Example from the real codebase:
@event_slot(
"login.succeeded",
params=["user_id", "role", "organization_id"],
optional=True,
description="Triggered after a user successfully authenticates.",
)
def register_login_succeeded_event():
return NoneBecause the slot is declared inside the auth module, the registered key becomes:
auth.login.succeededWhy Slots Matter¶
You can emit events without a slot declaration, but the runtime will warn when an event is emitted without a declared public contract.
That warning exists for a reason: undeclared events are harder to discover, harder to document, and easier to break accidentally.
If an event is part of your module’s extension surface, declare a slot for it.
get_event_slots(module_name: str | None = None)¶
This method returns the declared event slot definitions from the runtime registry.
Example:
all_slots = module_sdk.events.get_event_slots()
auth_slots = module_sdk.events.get_event_slots("auth")What It Is For¶
Use this method when your module wants to inspect the event contracts currently registered in the application.
This is useful for:
- documentation pages
- developer tooling
- admin/debug interfaces
- validation that a target event contract exists
What It Returns¶
The method returns the registry definitions for declared event slots.
In practice, each definition contains metadata such as:
namemodule_nameparamsoptionaldescription
This gives you the public contract for each declared event key.
Filtering By Module¶
If you pass module_name, only definitions belonging to that module are returned.
If you omit it, you get all declared slots known to the runtime.
Example:
for slot in module_sdk.events.get_event_slots("auth"):
print(slot["name"], slot["params"])Why This Method Is Valuable¶
Event systems become opaque very quickly if modules emit arbitrary strings that nobody can discover.
get_event_slots(...) makes the public event surface inspectable at runtime. That is a major part of what turns the event system from a hidden implementation detail into a usable extension contract.
@event_listener(...) And Name Matching¶
Listeners work differently from slots.
The listener decorator expects a fully qualified event name unless you are relying on scoped decorator binding behavior in very specific module authoring contexts. In the base helper, registration is skipped if the name is not fully qualified.
That means this is the safe pattern to document and use:
@event_listener("auth.login.succeeded", priority=10)
async def on_login(...):
...Not this:
@event_listener("login.succeeded")unless you are absolutely sure you are going through the scoped binding path that qualifies names for you.
For documentation aimed at SDK users, the important rule is:
- producers often work with relative names and
qualify_event_name(...) - listeners should subscribe to the fully qualified event key
Practical Naming Guidance¶
Event names work best when they describe a completed domain fact, not a UI action.
Good examples:
auth.login.succeededshop.order.completedcrm.lead.converted
Weak examples:
clicked_buttonsavedo_auth
The better the event name reflects a domain fact, the easier it is for another module author to understand whether they should subscribe to it.