Installable plugins¶
There are two ways to add datasets, processes, workflows, export renderers, and feature collections 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 addit 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 these extension points — datasets,
processes, workflows, exports, and features are auto-discovered when the package is installed.
datasets/, processes/, exports/, and features/ hold importable Python (or, for features/,
YAML templates alongside it), 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
exports/ # optional: pure export renderers
__init__.py
my_export.py # exposes plugin = BaseExportPlugin subclass instance
features/ # optional: feature collection templates and providers
__init__.py
example.yaml # feature templates (id, name, id_property, optional provider + params)
my_provider.py # @feature_provider-decorated callables
A feature template declares id, name, an optional license/attribution, and the
id_property naming its identifier column. One with no provider is metadata only — it
describes a collection an operator registers by hand, and nothing refreshes it. One with a
provider names a registered @feature_provider callable and the params to call it with. A
registered collection is served under GET /features and can be loaded inside a process graph by
id with the load_features process.
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:
Declare open-climate-service as a dependency, but do not pin its VCS source — the consuming
instance decides which OCS revision to run:
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:
Install and discover¶
An operator installs the package — nothing else, no plugins_dir wiring:
OCS auto-discovers every installed package in the open_climate_service.plugins group and loads
its datasets/*.yaml templates, its processes/ (@process-decorated callables), its
workflows/*.json (openEO UDPs), its exports/ renderers (see Export plugins),
and its features/*.yaml templates and @feature_provider-decorated callables. 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.