Skip to main content

Extensions and events in Python

The same two constructs, the same vocabulary.

Grouping plugins

from reactor import ExtensionManifest, PluginManifest, PluginPlatform, on_command

platform = PluginPlatform()

platform.register_extension(
ExtensionManifest(name="notebooks", display_name="Notebooks", emoji="📓"),
[
(PluginManifest(name="editor", version="1.0.0"), EditorPlugin()),
(PluginManifest(name="toolbar", version="1.0.0"), ToolbarPlugin()),
],
)

platform.list_extensions()
# [{"name": "notebooks", "display_name": "Notebooks", "plugins": ["editor", "toolbar"], …}]

GET /extensions serves the grouping.

Waiting, and factories

A plugin that waits registers a factory rather than an implementation, so the object is not built until its event fires:

platform.register_plugin(
PluginManifest(name="reports", version="1.0.0", activation_events=[on_command("report")]),
factory=lambda: ReportsPlugin(), # not called yet
)

platform.get_contributions(REPORT_POINT) # []
platform.fire_event(on_command("report")) # {"activated": ["reports"], …}
platform.get_contributions(REPORT_POINT) # the report contributions

Reading a contribution point fires onContributionPoint:<id> here too. Unlike the TypeScript tier there is no module on the wire, so activation is synchronous and the plugins a read wakes are in the list that read returns.

Standing down

The mirror of waking, and distinct from disabling:

platform.register_plugin(
PluginManifest(
name="document-mode",
version="1.0.0",
deactivation_events=["onView:notebook"],
),
DocumentModePlugin(),
)

platform.fire_event("onView:notebook")
# {"deactivated": ["document-mode"], "activated": ["notebook-mode"]}

platform.deactivate_plugin("catalog") # dependants first, transitively

A deactivated plugin keeps its manifest, its place in the list and its implementation, and comes back when one of its activation events fires. A disabled one does not — that is a person's decision, and no event overrides it. The three states are the same on both tiers.

POST /events/{event} fires an event and answers with what stood down and what woke; POST /plugins/{name}/deactivate stands one down directly.

Cross-tier activation

A backend plugin going down stands its frontend dependants down, and brings them back when it returns: setBackendPlugins() on the browser side applies the server's state, and GET /events/stream (consumed by useBackendPluginStream()) is what keeps the two in step without polling. See Cross-tier Dependencies.