spaCR plugin SDK

The plugin SDK lets a separately installed Python package add assays, importers, analysis utilities, settings panels, model-zoo entries and report sections without editing spaCR. Plugins use Python package entry points, so spaCR discovers them at startup and keeps them available to both the Qt application and spacr-run.

The current SDK API is 1.0. Only the major version controls compatibility: a plugin declaring API 1.x works with spaCR’s 1.x SDK. A malformed or failing plugin is isolated and reported by spacr-plugins doctor; it cannot replace a built-in module or prevent spaCR from starting.

Minimal plugin

Declare the entry point in the plugin package’s pyproject.toml:

[project.entry-points."spacr.plugins"]
my_assays = "my_spacr_plugin:plugin"

Then expose a manifest:

from spacr.plugins import AppContribution, SpacrPlugin

plugin = SpacrPlugin(
    name="My laboratory assays",
    version="0.1.0",
    api_version="1.0",
    apps=(
        AppContribution(
            key="organelle_contact",
            name="Organelle Contact",
            description="Measure contact sites from a processed plate.",
            kind="assay",
            section="results",
            stage="alpha",
            entrypoint="my_spacr_plugin.pipeline:run",
            defaults="my_spacr_plugin.pipeline:default_settings",
            categories={
                "Input": ("src", "table"),
                "Detection": ("distance_px", "min_area"),
                "Output": ("save_figures",),
            },
            tooltips={
                "src": "Processed spaCR plate folder.",
                "distance_px": "Maximum membrane-to-membrane distance.",
            },
            labels={"distance_px": "Contact distance (px)"},
            docs_url="https://example.org/my-plugin/api/",
            aliases=("contacts",),
            validator="my_spacr_plugin.pipeline:validate",
            drop_handler="my_spacr_plugin.qt:PlateDropHandler",
            requires=("src — a processed spaCR plate",),
            writes=("<src>/measurements/contact_sites.csv",),
        ),
    ),
)

The pipeline callable receives one settings dictionary. The defaults callable must accept an optional settings dictionary and return a dictionary. A validator returns spacr.validate.Problem objects (or equivalent mappings). All processing still runs through spaCR’s worker, journal, cancellation and reproducibility paths.

Settings and custom screens

The generic settings screen is preferred: declare categories, tooltips and optional labels on spacr.plugins.AppContribution. The UI then provides the normal Run/Stop controls, console, progress reporting, API links, drag-and-drop fallback and remote-submit action.

For a layout the generic screen cannot express, set screen_factory to a module:callable. The callable is invoked as factory(app_key=...) and must return a PySide6.QtWidgets.QWidget. A custom drop_handler must be a subclass of spacr.qt.dnd_handlers.DropHandler. icon may name a spaCR semantic icon or an absolute image path; otherwise the normal puzzle piece fallback is used.

Model providers

Add a spacr.plugins.ModelProviderContribution. Its zero-argument callable returns an iterable of spacr.model_zoo.ModelEntry objects or the mappings accepted by spaCR’s JSON model catalogue.

from spacr.plugins import ModelProviderContribution

ModelProviderContribution(
    key="lab_models",
    provider="my_spacr_plugin.models:catalogue",
)

Providers must be read-only during catalogue discovery. Downloads remain an explicit Model Zoo action. A provider exception is shown by spacr-plugins doctor and built-in models remain available.

Report sections

A spacr.plugins.ReportSectionContribution builder receives a read-only spacr.plugins.ReportContext and returns a spacr.report.Section:

from spacr.plugins import ReportSectionContribution
from spacr.report import Section

def build_contacts(context):
    return Section(
        key="organelle_contacts",
        title="Organelle contacts",
        body_html="<p>Contact-site results.</p>",
        text_lines=["Contact-site results."],
    )

contact_report = ReportSectionContribution(
    key="organelle_contacts",
    title="Organelle contacts",
    builder="my_spacr_plugin.report:build_contacts",
    after="statistics",
)

If the builder raises, the generated report contains a visible problem chapter with the exception instead of silently omitting the section.

Translations

SpacrPlugin.translations maps spaCR language codes to English-source / translated-text mappings. Supported codes are sv, de, es, zh_CN, pt, hi, ko, is and fr. Missing strings fall back to English.

Development and diagnostics

During local development only, point SPACR_PLUGIN_MODULES at a comma-separated list of module:attribute references. Set SPACR_DISABLE_PLUGINS=1 to start spaCR without third-party plugins.

SPACR_PLUGIN_MODULES=my_spacr_plugin:plugin spacr-plugins list
spacr-plugins doctor
spacr-plugins doctor --json

The public SDK lives in spacr.plugins; the diagnostics command lives in spacr.cli_plugins.

API reference

Versioned extension SDK for third-party spaCR plugins.

Plugins are ordinary Python distributions exposing one entry point in the spacr.plugins group. The entry point may resolve to a SpacrPlugin, a mapping accepted by plugin_from_mapping(), or a zero-argument factory returning either. Discovery is lazy, deterministic and failure-isolated: one malformed plugin is recorded in diagnostics() without preventing spaCR or the remaining plugins from loading.

For editable/local development, SPACR_PLUGIN_MODULES may contain a comma-separated list of module or module:attribute references. Installed plugins should always use package entry points instead.

class spacr.plugins.AppContribution(key: str, name: str, description: str, entrypoint: str, defaults: str, section: str = 'results', stage: str = 'alpha', kind: str = 'analysis', categories: ~typing.Mapping[str, ~typing.Sequence[str]] = <factory>, tooltips: ~typing.Mapping[str, str] = <factory>, labels: ~typing.Mapping[str, str] = <factory>, docs_url: str = '', aliases: ~typing.Tuple[str, ...] = (), validator: str = '', screen_factory: str = '', drop_handler: str = '', icon: str = '', requires: ~typing.Tuple[str, ...] = (), writes: ~typing.Tuple[str, ...] = (), call_style: str = 'settings')[source]

Bases: object

One runnable GUI/headless application contributed by a plugin.

aliases: Tuple[str, ...] = ()[source]
call_style: str = 'settings'[source]
categories: Mapping[str, Sequence[str]][source]
defaults: str[source]
description: str[source]
docs_url: str = ''[source]
drop_handler: str = ''[source]
entrypoint: str[source]
icon: str = ''[source]
key: str[source]
kind: str = 'analysis'[source]
labels: Mapping[str, str][source]
name: str[source]
requires: Tuple[str, ...] = ()[source]
screen_factory: str = ''[source]
section: str = 'results'[source]
stage: str = 'alpha'[source]
tooltips: Mapping[str, str][source]
validator: str = ''[source]
writes: Tuple[str, ...] = ()[source]
class spacr.plugins.ModelProviderContribution(key: str, provider: str)[source]

Bases: object

A callable returning model-zoo entries or entry mappings.

key: str[source]
provider: str[source]
class spacr.plugins.PluginDiagnostic(plugin: str, severity: str, message: str, exception: str = '')[source]

Bases: object

One discovery or contribution error visible to users and logs.

exception: str = ''[source]
message: str[source]
plugin: str[source]
severity: str[source]
class spacr.plugins.ReportContext(src: Any, artifacts: Mapping[str, Any], runs: Tuple[Mapping[str, Any], ...], options: Mapping[str, Any])[source]

Bases: object

Read-only inputs passed to plugin report-section builders.

artifacts: Mapping[str, Any][source]
options: Mapping[str, Any][source]
runs: Tuple[Mapping[str, Any], ...][source]
src: Any[source]
class spacr.plugins.ReportSectionContribution(key: str, title: str, builder: str, after: str = 'statistics')[source]

Bases: object

A callable adding one section to spacr.report.collect_report().

after: str = 'statistics'[source]
builder: str[source]
key: str[source]
title: str[source]
class spacr.plugins.SpacrPlugin(name: str, version: str, api_version: str = '1.0', apps: ~typing.Tuple[~spacr.plugins.AppContribution, ...] = (), model_providers: ~typing.Tuple[~spacr.plugins.ModelProviderContribution, ...] = (), report_sections: ~typing.Tuple[~spacr.plugins.ReportSectionContribution, ...] = (), translations: ~typing.Mapping[str, ~typing.Mapping[str, str]] = <factory>)[source]

Bases: object

Validated plugin manifest returned by a spacr.plugins entry point.

api_version: str = '1.0'[source]
apps: Tuple[AppContribution, ...] = ()[source]
model_providers: Tuple[ModelProviderContribution, ...] = ()[source]
name: str[source]
report_sections: Tuple[ReportSectionContribution, ...] = ()[source]
translations: Mapping[str, Mapping[str, str]][source]
version: str[source]
spacr.plugins.diagnostics() Tuple[PluginDiagnostic, ...][source]

Return discovery and runtime contribution failures.

spacr.plugins.discover_plugins() Tuple[SpacrPlugin, ...][source]

Return every valid discovered plugin in deterministic order.

spacr.plugins.get_app(key: str) AppContribution | None[source]

Return a contributed app by key, or None.

spacr.plugins.load_object(reference: str) Any[source]

Import and return module:attribute (nested attributes supported).

spacr.plugins.model_providers() Tuple[Tuple[str, ModelProviderContribution], ...][source]

Return (plugin_name, provider) model-zoo contributions.

spacr.plugins.plugin_apps() Tuple[AppContribution, ...][source]

Return all contributed applications.

spacr.plugins.plugin_from_mapping(value: Mapping[str, Any]) SpacrPlugin[source]

Validate a mapping and return its immutable SpacrPlugin.

spacr.plugins.record_diagnostic(plugin: str, message: str, exception: Any = '', severity: str = 'error') None[source]

Record a model/report/runtime plugin failure without aborting spaCR.

spacr.plugins.reload_plugins() Tuple[SpacrPlugin, ...][source]

Clear the discovery cache and discover again (primarily for tests/dev).

spacr.plugins.report_sections() Tuple[Tuple[str, ReportSectionContribution], ...][source]

Return (plugin_name, section) report contributions.