Skip to content

Inspect plugin contracts and discovery

A plugin must be installed and registered before the CLI can discover it. Core defines configuration and lifecycle contracts; the Rust r2x executable owns installed-package discovery and generated command help. This tutorial first inspects a complete class without requiring external model data.

>>> import inspect
>>> from r2x_core import Plugin, PluginConfig, PluginContext, System
>>> from rust_ok import Ok, Result
>>> class DemoConfig(PluginConfig):
... scenario: str = "reference"
>>> class BuildDemo(Plugin[DemoConfig]):
... def on_build(self) -> Result[System, str]:
... return Ok(System(name=self.config.scenario))
>>> plugin = BuildDemo.from_context(PluginContext(config=DemoConfig()))
>>> # Expected: this class implements a build hook, not a hidden parser registry.
>>> sorted(name for name, value in vars(BuildDemo).items() if name.startswith("on_") and callable(value))
['on_build']
>>> list(inspect.signature(BuildDemo.on_build).parameters)
['self']
>>> DemoConfig.model_json_schema()["properties"]["scenario"]["default"]
'reference'
>>> plugin.run().system.name
'reference'

The hook list and schema above come from the actual class and configuration, not a hand-written catalog claiming ReEDS or PLEXOS behavior. A real parser that reads files is demonstrated in Build a parser.

This example uses PEP 695 type parameters and requires Python 3.12 or newer. The ... def line is valid here: it continues the statement started by >>> @expose_plugin. An undecorated top-level definition would start with >>> def. The type parameter bound supports static checking; it is not a replacement for runtime validation of plugin configuration.

>>> from r2x_core import expose_plugin, PluginConfig, System
>>> from rust_ok import Ok, Result
>>> class BaseTransformConfig(PluginConfig):
... pass
>>> class ScaleConfig(BaseTransformConfig):
... factor: float = 1.0
>>> class RotateConfig(BaseTransformConfig):
... angle: float = 0.0
>>> @expose_plugin
... def generic_transform[C: BaseTransformConfig](
... system: System,
... config: C,
... ) -> Result[System, str]:
... """Generic transform accepting constrained config types."""
... system.name = f"{system.name}_transformed"
... return Ok(system)
>>> scale_cfg = ScaleConfig(factor=2.5)
>>> result = generic_transform(System(name="grid"), scale_cfg)
>>> # Expected: the typed transform executed and changed the system name.
>>> result.unwrap().name
'grid_transformed'

For a class parser, the package declares its module and class in r2x.parsers. For an exposed function transform, use r2x.transforms. Names in these entry points are the CLI references; Python class names are not automatically executable commands. The complete CLI fixture package contains actual package metadata and can be installed without licensed model data.

Inspect installed references with the Rust CLI

Section titled “Inspect installed references with the Rust CLI”

After installing that fixture with the CLI tutorial, these commands inspect its real create and scale references:

Terminal window
r2x list r2x-tutorial
r2x run create --help
r2x run scale --help

There is no plugins list subcommand or Python-module CLI invocation. If discovery differs, use CLI troubleshooting. The function-plugin guide explains configuration and callable contracts with checked examples.