Skip to content

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:

  1. Reads the configuration - parses the YAML file to determine which devices, presenters, and views are needed.
  2. Queries entry points - looks up installed packages registered under the redsun.plugins entry point group.
  3. Loads manifests - each plugin provides a YAML manifest file that maps plugin IDs to their Python class locations.
  4. Validates protocols - each loaded class is checked against the expected protocol (Device, Presenter, or View).
  5. 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:

[project.entry-points."redsun.plugins"]
my-plugin = "redsun.yaml"

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_version is the version value of the component system, kept for future compatibility;
  • session is the name which will be assigned to the application for display;
  • frontend is the UI toolkit used to load the correct subclass of AppContainer;
  • metadata are 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 Device or structurally implement the PDevice protocol.
  • Presenters must be subclasses of Presenter or structurally implement the PPresenter protocol.
  • Views must be subclasses of View or structurally implement the PView protocol.

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.