Skip to content

How plugins provide components

A plugin is an installed Python package that offers components to sessions, so a session file can name a component by the plugin it comes from, without any Python code:

presenters:
  motor_ctrl:
    plugin_name: my-plugin
    plugin_id: motor
    step: 2.0

plugin_name and plugin_id say which class to make, and every other key is an argument to its constructor. Step through an entry becoming a presenter:

From a session file entry to a component

A session file names a component by its plugin and its id.

# session.yaml presenters:   motor_ctrl:     plugin_name: mylab     plugin_id: motor     step: 2.0# session.yaml presenters:   motor_ctrl:     plugin_name: mylab     plugin_id: motor     step: 2.0 plugin_namethe manifest it namesplugin_id
# session.yaml presenters:   motor_ctrl:     plugin_name: mylab     plugin_id: motor     step: 2.0# session.yaml presenters:   motor_ctrl:     plugin_name: mylab     plugin_id: motor     step: 2.0 plugin_namethe manifest it namesplugin_id

plugin_name is the name of an entry point in the redsun.plugins group, which names the manifest inside the installed package.

# session.yaml presenters:   motor_ctrl:     plugin_name: mylab     plugin_id: motor     step: 2.0# session.yaml presenters:   motor_ctrl:     plugin_name: mylab     plugin_id: motor     step: 2.0# pyproject.toml of the mylab package [project.entry-points."redsun.plugins"] mylab = "redsun.yaml"# pyproject.toml of the mylab package [project.entry-points."redsun.plugins"] mylab = "redsun.yaml" plugin_namethe manifest it namesplugin_id
# session.yaml presenters:   motor_ctrl:     plugin_name: mylab     plugin_id: motor     step: 2.0# session.yaml presenters:   motor_ctrl:     plugin_name: mylab     plugin_id: motor     step: 2.0# pyproject.toml of the mylab package [project.entry-points."redsun.plugins"] mylab = "redsun.yaml"# pyproject.toml of the mylab package [project.entry-points."redsun.plugins"] mylab = "redsun.yaml" plugin_namethe manifest it namesplugin_id

The manifest maps each id to a class, written as module:ClassName.

# session.yaml presenters:   motor_ctrl:     plugin_name: mylab     plugin_id: motor     step: 2.0# session.yaml presenters:   motor_ctrl:     plugin_name: mylab     plugin_id: motor     step: 2.0# pyproject.toml of the mylab package [project.entry-points."redsun.plugins"] mylab = "redsun.yaml"# pyproject.toml of the mylab package [project.entry-points."redsun.plugins"] mylab = "redsun.yaml"# redsun.yaml inside the mylab package presenters:   motor: mylab.presenters:MotorPresenter# redsun.yaml inside the mylab package presenters:   motor: mylab.presenters:MotorPresenter plugin_namethe manifest it namesplugin_id
# session.yaml presenters:   motor_ctrl:     plugin_name: mylab     plugin_id: motor     step: 2.0# session.yaml presenters:   motor_ctrl:     plugin_name: mylab     plugin_id: motor     step: 2.0# pyproject.toml of the mylab package [project.entry-points."redsun.plugins"] mylab = "redsun.yaml"# pyproject.toml of the mylab package [project.entry-points."redsun.plugins"] mylab = "redsun.yaml"# redsun.yaml inside the mylab package presenters:   motor: mylab.presenters:MotorPresenter# redsun.yaml inside the mylab package presenters:   motor: mylab.presenters:MotorPresenter plugin_namethe manifest it namesplugin_id

The session imports the class only now, because a session names it, and passes every other key of the entry to the constructor.

# session.yaml presenters:   motor_ctrl:     plugin_name: mylab     plugin_id: motor     step: 2.0# session.yaml presenters:   motor_ctrl:     plugin_name: mylab     plugin_id: motor     step: 2.0# pyproject.toml of the mylab package [project.entry-points."redsun.plugins"] mylab = "redsun.yaml"# pyproject.toml of the mylab package [project.entry-points."redsun.plugins"] mylab = "redsun.yaml"# redsun.yaml inside the mylab package presenters:   motor: mylab.presenters:MotorPresenter# redsun.yaml inside the mylab package presenters:   motor: mylab.presenters:MotorPresenterfrom mylab.presenters import MotorPresenter motor_ctrl = MotorPresenter(name="motor_ctrl", step=2.0)from mylab.presenters import MotorPresenter motor_ctrl = MotorPresenter(name="motor_ctrl", step=2.0) plugin_namethe manifest it namesplugin_id
# session.yaml presenters:   motor_ctrl:     plugin_name: mylab     plugin_id: motor     step: 2.0# session.yaml presenters:   motor_ctrl:     plugin_name: mylab     plugin_id: motor     step: 2.0# pyproject.toml of the mylab package [project.entry-points."redsun.plugins"] mylab = "redsun.yaml"# pyproject.toml of the mylab package [project.entry-points."redsun.plugins"] mylab = "redsun.yaml"# redsun.yaml inside the mylab package presenters:   motor: mylab.presenters:MotorPresenter# redsun.yaml inside the mylab package presenters:   motor: mylab.presenters:MotorPresenterfrom mylab.presenters import MotorPresenter motor_ctrl = MotorPresenter(name="motor_ctrl", step=2.0)from mylab.presenters import MotorPresenter motor_ctrl = MotorPresenter(name="motor_ctrl", step=2.0) plugin_namethe manifest it namesplugin_id

The manifest

A plugin lists what it offers in a manifest, a YAML file in the package registered in the entry point group redsun.plugins under the plugin_name a session file uses. Point at one of its five groups to read what it holds:

The groups of a manifest
devicesid -> module:ClassNamepresentersid -> module:ClassNameviewsid -> module:ClassNameprovidersid -> module:ClassNameClasses that share values with every component without being components themselves. Each method a provider marks with provides shares a value. A provider's constructor is filled by type only, never from the keys of a session file.servicesid -> how to launch itEach entry gives the module to run as python -m, its arguments, the line it prints once it serves, and how long each stop step waits. A session file may override any of it.Classes that share values with every component without being components themselves. Each method a provider marks with provides shares a value. A provider's constructor is filled by type only, never from the keys of a session file. Each entry gives the module to run as python -m, its arguments, the line it prints once it serves, and how long each stop step waits. A session file may override any of it.
devicesid -> module:ClassNamepresentersid -> module:ClassNameviewsid -> module:ClassNameprovidersid -> module:ClassNameClasses that share values with every component without being components themselves. Each method a provider marks with provides shares a value. A provider's constructor is filled by type only, never from the keys of a session file.servicesid -> how to launch itEach entry gives the module to run as python -m, its arguments, the line it prints once it serves, and how long each stop step waits. A session file may override any of it.Classes that share values with every component without being components themselves. Each method a provider marks with provides shares a value. A provider's constructor is filled by type only, never from the keys of a session file. Each entry gives the module to run as python -m, its arguments, the line it prints once it serves, and how long each stop step waits. A session file may override any of it.

A class is imported only when a session names it, so a plugin's Qt views cost nothing to a session that doesn't use them. A manifest that fails its schema is left out whole; a session-file entry that can't be resolved is left out on its own (ADR 14). How to package components as a plugin writes one, and Plugin manifest lists every key.

Components from a file and from a class

A session can take components from both. The class declares the components it wants as typed attributes, and the file adds the rest:

class MyApp(QtSession):
    config = "session.yaml"

    motor_ctrl: AsPresenter[MotorPresenter]  # also configured in the file
A session built from a class and a file

The class and the file both contribute to one session.

class MyApp(QtSession):     config = "session.yaml"     motor_ctrl: AsPresenter[MotorPresenter]class MyApp(QtSession):     config = "session.yaml"     motor_ctrl: AsPresenter[MotorPresenter]# session.yaml presenters:   motor_ctrl:     step: 2.0 views:   logs:     plugin_name: redsun     plugin_id: logs# session.yaml presenters:   motor_ctrl:     step: 2.0 views:   logs:     plugin_name: redsun     plugin_id: logsthe session declaresconfiguresadds
class MyApp(QtSession):     config = "session.yaml"     motor_ctrl: AsPresenter[MotorPresenter]class MyApp(QtSession):     config = "session.yaml"     motor_ctrl: AsPresenter[MotorPresenter]# session.yaml presenters:   motor_ctrl:     step: 2.0 views:   logs:     plugin_name: redsun     plugin_id: logs# session.yaml presenters:   motor_ctrl:     step: 2.0 views:   logs:     plugin_name: redsun     plugin_id: logsthe session declaresconfiguresadds

The class declares motor_ctrl and the class it is made from.

class MyApp(QtSession):     config = "session.yaml"     motor_ctrl: AsPresenter[MotorPresenter]class MyApp(QtSession):     config = "session.yaml"     motor_ctrl: AsPresenter[MotorPresenter]# session.yaml presenters:   motor_ctrl:     step: 2.0 views:   logs:     plugin_name: redsun     plugin_id: logs# session.yaml presenters:   motor_ctrl:     step: 2.0 views:   logs:     plugin_name: redsun     plugin_id: logsthe sessionmotor_ctrl declaresconfiguresadds
class MyApp(QtSession):     config = "session.yaml"     motor_ctrl: AsPresenter[MotorPresenter]class MyApp(QtSession):     config = "session.yaml"     motor_ctrl: AsPresenter[MotorPresenter]# session.yaml presenters:   motor_ctrl:     step: 2.0 views:   logs:     plugin_name: redsun     plugin_id: logs# session.yaml presenters:   motor_ctrl:     step: 2.0 views:   logs:     plugin_name: redsun     plugin_id: logsthe sessionmotor_ctrl declaresconfiguresadds

The file's entry of the same name configures it, with no plugin_name needed.

class MyApp(QtSession):     config = "session.yaml"     motor_ctrl: AsPresenter[MotorPresenter]class MyApp(QtSession):     config = "session.yaml"     motor_ctrl: AsPresenter[MotorPresenter]# session.yaml presenters:   motor_ctrl:     step: 2.0 views:   logs:     plugin_name: redsun     plugin_id: logs# session.yaml presenters:   motor_ctrl:     step: 2.0 views:   logs:     plugin_name: redsun     plugin_id: logsthe sessionmotor_ctrl declaresconfiguresadds
class MyApp(QtSession):     config = "session.yaml"     motor_ctrl: AsPresenter[MotorPresenter]class MyApp(QtSession):     config = "session.yaml"     motor_ctrl: AsPresenter[MotorPresenter]# session.yaml presenters:   motor_ctrl:     step: 2.0 views:   logs:     plugin_name: redsun     plugin_id: logs# session.yaml presenters:   motor_ctrl:     step: 2.0 views:   logs:     plugin_name: redsun     plugin_id: logsthe sessionmotor_ctrl declaresconfiguresadds

An entry the class doesn't declare adds a component, naming its plugin with both plugin_name and plugin_id.

class MyApp(QtSession):     config = "session.yaml"     motor_ctrl: AsPresenter[MotorPresenter]class MyApp(QtSession):     config = "session.yaml"     motor_ctrl: AsPresenter[MotorPresenter]# session.yaml presenters:   motor_ctrl:     step: 2.0 views:   logs:     plugin_name: redsun     plugin_id: logs# session.yaml presenters:   motor_ctrl:     step: 2.0 views:   logs:     plugin_name: redsun     plugin_id: logsthe sessionmotor_ctrllogs declaresconfiguresadds
class MyApp(QtSession):     config = "session.yaml"     motor_ctrl: AsPresenter[MotorPresenter]class MyApp(QtSession):     config = "session.yaml"     motor_ctrl: AsPresenter[MotorPresenter]# session.yaml presenters:   motor_ctrl:     step: 2.0 views:   logs:     plugin_name: redsun     plugin_id: logs# session.yaml presenters:   motor_ctrl:     step: 2.0 views:   logs:     plugin_name: redsun     plugin_id: logsthe sessionmotor_ctrllogs declaresconfiguresadds

When a file entry has the same name as a declaration, it configures that declaration and needs no plugin_name. An entry the class doesn't declare names its plugin with both plugin_name and plugin_id, or it is left out.

Built-in components

redsun is a plugin of itself, named redsun. It offers three stacks, each a presenter and a view written to work together, and a log window:

id presenter view for
positioner PositionerPresenter PositionerView moving devices by hand
lights LightPresenter LightView switching and dimming lights
acquisition AcquisitionPresenter AcquisitionView running plans from the window
logs none LogView showing the log

A session file names each one by plugin_name: redsun and its id, under presenters or views:

views:
  logs:
    plugin_name: redsun
    plugin_id: logs

In a Qt session, the log view docks at the bottom.