Component system¶
redsun component system allows third-party packages to provide devices, presenters, and views that are dynamically discovered and loaded at runtime.
Overview¶
From Python point of view, components are standard Python packages that register themselves via entry points. When redsun builds an application from a YAML configuration file, it uses these entry points to discover available plugins and load the requested components.
graph TB
Config[YAML config] -->|references| Plugins
Plugins -->|discovered via| EntryPoints[entry points]
EntryPoints -->|load| Manifest[plugin manifest]
Manifest -->|resolves to| Classes[component classes]
Classes -->|registered in| Container[AppContainer]
Plugin discovery¶
When AppContainer.from_config()
is called with a configuration file, Redsun:
- Reads the configuration - parses the YAML file to determine which devices, presenters, and views are needed.
- Queries entry points - looks up installed packages registered under the
redsun.pluginsentry point group. - Loads manifests - each plugin provides a YAML manifest file that maps plugin IDs to their Python class locations.
- Validates protocols - each loaded class is checked against the expected protocol (
Device,Presenter, orView). - Creates the container - a dynamic container class is assembled with the discovered components.
Component manifest¶
Each component package must include a YAML manifest file named redsun.yaml that declares the available components.
Each entry maps a plugin ID directly to its "module:ClassName" class path:
# redsun.yaml
devices:
my_motor: "my_plugin.devices:MyMotor"
presenters:
my_controller: "my_plugin.presenters:MyController"
views:
my_ui: "my_plugin.views:MyView"
The manifest must be registered as a Python entry point in the package pyproject.toml:
Tip
This is inspired by the napari manifest.
Make sure that depending on the packaging system you use, the redsun.yaml is included in the built package otherwise your components will not be discoverable.
Configuration file format¶
The application configuration file references plugins by name and ID. A full example follows:
schema_version: 1.0
session: "My application"
frontend: "pyqt"
metadata:
user: Jacopo Abramo
location: Jena
setup: iSCAT
devices:
motor:
plugin_name: my-plugin
plugin_id: my_motor
axis:
- X
- Y
presenters:
controller:
plugin_name: my-plugin
plugin_id: my_controller
views:
ui:
plugin_name: my-plugin
plugin_id: my_ui
The top-level keys represent application level information:
schema_versionis the version value of the component system, kept for future compatibility;sessionis the name which will be assigned to the application for display;frontendis the UI toolkit used to load the correct subclass ofAppContainer;metadataare application-level metadata to add contextual informations.
The plugin_name and plugin_id keys are used for plugin resolution and are not passed to the component constructors. All other keys become keyword arguments for the component.
Protocol validation¶
Before a plugin class is used, redsun verifies it implements the expected protocol:
- Devices must be subclasses of
Deviceor structurally implement thePDeviceprotocol. - Presenters must be subclasses of
Presenteror structurally implement thePPresenterprotocol. - Views must be subclasses of
Viewor structurally implement thePViewprotocol.
Classes that satisfy the protocol structurally (without inheriting from the base class) are registered as virtual subclasses via ABCMeta.register().
Inline vs. config-based registration¶
The plugin system is used when building from configuration files via
AppContainer.from_config().
When using the declarative class-based approach (defining a container subclass with
device(), presenter() or view() field functions), component classes are
passed directly as the first argument to the respective function and do not go through plugin discovery.
Both approaches produce the same result: an
AppContainer with registered device,
presenter, and view components ready to be built.