Skip to content

Installable plugins

There are two ways to add datasets, processes, and workflows to an instance, and they complement each other:

  • plugins_dir — drop files into the instance's local plugins folder. Ideal for instance-specific customisation, one-off datasets, or overriding a built-in. No packaging. See Extensibility and Adding custom datasets.
  • Installable packages — package a plugin and publish it, so any instance can uv add it and OCS discovers it automatically (#118). Ideal for a reusable plugin shared across countries or organisations, versioned independently.

This guide covers the installable-package option. It is purely additive — plugins_dir keeps working exactly as before, and still takes precedence (see Precedence).

Package layout

An importable package can ship any combination of the three extension points — datasets, processes, and workflows are all auto-discovered when the package is installed. datasets/ and processes/ hold importable Python, so each needs an __init__.py (workflows/ is plain JSON and does not):

osc_example_plugin/
  __init__.py
  datasets/
    __init__.py
    example.py           # your BaseDatasetPlugin subclass
    example.yaml         # dataset templates
  processes/             # optional: @process-decorated callables
    __init__.py
    my_process.py
  workflows/             # optional: openEO UDP JSON graphs
    my_workflow.json

The layout mirrors plugins_dir, so migrating a plugins_dir-based plugin to a distributable package is mostly moving the files into a package and adding the entry point below.

Declare the entry point

In the package's pyproject.toml, point an entry point in the open_climate_service.plugins group at the top-level package:

[project.entry-points."open_climate_service.plugins"]
example = "osc_example_plugin"

Declare open-climate-service as a dependency, but do not pin its VCS source — the consuming instance decides which OCS revision to run:

dependencies = ["open-climate-service"]

The ingestion.plugin in a dataset template uses the class's full dotted path (not a plugins_dir-relative one), since the package is installed on PYTHONPATH:

ingestion:
  plugin: osc_example_plugin.datasets.example.ExamplePlugin

Install and discover

An operator installs the package — nothing else, no plugins_dir wiring:

uv add osc-example-plugin

OCS auto-discovers every installed package in the open_climate_service.plugins group and loads its datasets/*.yaml templates, its processes/ (@process-decorated callables), and its workflows/*.json (openEO UDPs). The ingestion plugin class is importable by dotted path because the package is installed. The datasets then appear in /datasets and can be ingested like any built-in.

Precedence

Templates are merged in increasing order of precedence, per extension point:

built-in → installed plugins → instance plugins_dir

So plugins_dir always wins on an id conflict — an operator can drop a YAML into their local plugins/datasets/ to override an installed plugin's dataset. Overrides are logged at load time.

Naming convention

  • Distribution name: osc-<name>-plugin
  • Import package: osc_<name>_plugin

For example, osc-senorge-plugin / osc_senorge_plugin.

Reference implementation

The seNorge plugin is the reference implementation: it ships the seNorge 2018 datasets (source + derived) and the ingestion plugin, and is consumed by the Norway instance via uv add.