Plugin author guide
A SpectroChemPy plugin is a Python package that exposes an entry point in the
spectrochempy.plugins group and returns declarative contributions.
Minimal structure
myplugin/
pyproject.toml
src/myplugin/__init__.py
src/myplugin/readers.py
The entry point points to a plugin class:
[project.entry-points."spectrochempy.plugins"]
myplugin = "myplugin:MyPlugin"
The plugin declares contributions:
from spectrochempy.api.plugins import SpectroChemPyPlugin
from .readers import read_myformat
class MyPlugin(SpectroChemPyPlugin):
name = "myplugin"
version = "0.1.0"
def register_readers(self) -> list[dict]:
return [
{
"name": "myformat",
"func": read_myformat,
"description": "Read MyFormat files",
"extensions": [".myf"],
},
]
Readers and writers
Readers create datasets and belong under scp.<plugin> or compatibility
scp.read_<format> aliases. They should not be dataset accessors.
A plugin may contribute a short I/O namespace through its io_namespaces
attribute so that scp.<namespace>.read(...) delegates to a reader:
io_namespaces = {
"myformat": {"read": "myplugin.read_myformat"},
}
The short namespace must not collide with any public scp symbol
(integration/analysis functions, NDDataset methods exposed at root,
compatibility aliases, public submodules, or core I/O namespaces). A
conflicting short namespace is rejected with a warning at registration, but
the reader remains available through its explicit scp.read_<format>
function.
For the other plugin-provided root surfaces (root_exports, reader exports,
analysis/simulation extensions), the collision is avoided through
resolution priority: scp.__getattr__ always resolves a public core
symbol before any plugin-provided name, regardless of plugin installation or
discovery order. These surfaces are not rejected at registration time; the
core symbol simply wins on access.
For example spectrochempy-nmr does not register a simpson I/O
namespace because scp.simpson is the core numerical-integration function;
the SIMPSON reader is exposed only as scp.read_simpson /
scp.nmr.read_simpson.
Use plugin handlers for format-specific filename inference:
def register_handlers(self) -> dict:
return {
"importer.resolve_directory_target": resolve_directory_target,
"importer.infer_filetype_key": infer_filetype_key,
}
Accessors
Dataset accessors are for operations that act on an existing
NDDataset:
def register_accessors(self) -> list[dict]:
return [
{
"namespace": "myplugin",
"name": "normalize",
"func": normalize_dataset,
"description": "Normalize a dataset using MyPlugin rules",
},
]
Handlers
Handlers are named extension points used when the core must delegate behavior without importing plugin-specific types or conventions:
def register_handlers(self) -> dict:
return {
"ndmath.execution_branch": execution_branch,
"ndmath.execute": execute_numeric_branch,
"importer.remote_download_target": remote_download_target,
}
Numeric backends and unit contexts
Use numeric backend handlers when plugin data needs a non-standard execution path, such as quaternion arrays. Use unit contexts when unit conversion needs domain metadata, such as an acquisition frequency.
Official and third-party plugins
Official plugins are maintained with SpectroChemPy and may receive coordinated
docs, examples, tests, and temporary compatibility aliases. Third-party plugins
use the same public API but should document their own namespaces and avoid
depending on private spectrochempy.plugins internals.
See also Plugin architecture, Plugin accessors, Numeric backends, and Unit contexts.