Plugin Context¶
The PluginContext is the unified interface for passing data to plugins. It uses immutable, copy-on-write semantics to efficiently pass large objects (like System) through plugin pipelines.
Context Fields¶
from r2x_core import PluginContext, PluginConfig, System, DataStore
class Config(PluginConfig):
name: str
# Minimal context (just config)
ctx = PluginContext(config=Config(name="test"))
ctx.config.name
'test'
# Context with system (for transform/export)
system = System(name="my_system")
ctx2 = PluginContext(config=Config(name="test"), system=system)
ctx2.system.name
'my_system'
# Context with data store (for build/export)
store = DataStore()
ctx3 = PluginContext(config=Config(name="test"), store=store)
ctx3.store is not None
True
Field Summary¶
Field |
Type |
Required |
Purpose |
|---|---|---|---|
|
|
Always |
Plugin configuration (typed) |
|
|
Optional |
File I/O operations |
|
|
Optional |
System object (created/modified) |
|
|
Optional |
Source for translation |
|
|
Optional |
Output of translation |
|
|
Optional |
Transformation rules |
|
|
Optional |
Arbitrary metadata |
|
|
Optional |
Skip Pydantic validation |
|
|
Optional |
Auto-add composed components |
Direct Attribute Updates¶
Context fields can be updated directly for memory efficiency. Since PluginContext uses __slots__, memory overhead is minimal:
from r2x_core import PluginContext, PluginConfig, System
class Config(PluginConfig):
pass
# Create context
ctx = PluginContext(config=Config())
# Update system directly
system = System(name="new")
ctx.system = system
ctx.system.name
'new'
# Update metadata
ctx.metadata["step"] = 1
ctx.metadata
{'step': 1}
This is memory-efficient: the System object is not copied, only referenced. The __slots__ design minimizes context overhead.
Plugin Pipeline Pattern¶
Pass context through a plugin pipeline. Context is updated in-place for efficiency:
from r2x_core import Plugin, PluginContext, PluginConfig, System
from rust_ok import Ok
class Config(PluginConfig):
pass
class BuildPlugin(Plugin[Config]):
def on_build(self):
return Ok(System(name="built"))
class TransformPlugin(Plugin[Config]):
def on_transform(self):
# Modify system
return Ok(self.system)
class ExportPlugin(Plugin[Config]):
def on_export(self):
return Ok(None)
# Build
ctx = PluginContext(config=Config())
build_plugin = BuildPlugin.from_context(ctx)
ctx = build_plugin.run()
ctx.system.name
'built'
# Transform
transform_plugin = TransformPlugin.from_context(ctx)
ctx = transform_plugin.run()
ctx.system.name
'built'
# Export
export_plugin = ExportPlugin.from_context(ctx)
ctx = export_plugin.run()
ctx.system.name
'built'
Metadata for Custom Data¶
Use metadata dict for plugins to exchange custom data:
from r2x_core import PluginContext, PluginConfig
class Config(PluginConfig):
pass
ctx = PluginContext(config=Config())
# Add metadata
ctx.metadata["step"] = "build"
ctx.metadata["duration_ms"] = 1234
ctx.metadata["step"]
'build'
# Extend metadata
ctx.metadata["exported"] = True
ctx.metadata
{'step': 'build', 'duration_ms': 1234, 'exported': True}
Parser-Specific Options¶
Some fields control parser behavior:
from r2x_core import PluginContext, PluginConfig
class Config(PluginConfig):
pass
# Skip validation for faster parsing
ctx = PluginContext(
config=Config(),
skip_validation=True,
auto_add_composed_components=False
)
ctx.skip_validation
True
ctx.auto_add_composed_components
False
# Update options
ctx.skip_validation = False
ctx.skip_validation
False
Translation Workflows¶
Translation plugins use source_system, target_system, and rules:
from r2x_core import PluginContext, PluginConfig, System
class Config(PluginConfig):
pass
source = System(name="plexos")
rules = [] # Translation rules
ctx = PluginContext(
config=Config(),
source_system=source,
rules=tuple(rules)
)
ctx.source_system.name
'plexos'
After translation, update target:
from r2x_core import PluginContext, PluginConfig, System
class Config(PluginConfig):
pass
ctx = PluginContext(
config=Config(),
source_system=System(name="plexos")
)
# After translation
target = System(name="sienna")
ctx.target_system = target
ctx.target_system.name
'sienna'
Creating Context from Command-Line Arguments¶
In your Rust CLI, deserialize arguments and create context:
import json
from pathlib import Path
from r2x_core import PluginContext, PluginConfig, DataStore
import tempfile
class MyConfig(PluginConfig):
model_year: int
# From Rust CLI args
args = json.loads('{"model_year": 2030}')
config = MyConfig(**args)
# Use temp directory for DataStore
with tempfile.TemporaryDirectory() as tmpdir:
ctx = PluginContext(
config=config,
store=DataStore(path=tmpdir)
)
print(ctx.config.model_year)
2030
Stdin Handling Pattern¶
When Rust CLI pipes a system via stdin, deserialize at the orchestrator level:
from r2x_core import PluginContext, PluginConfig, System
class Config(PluginConfig):
pass
# In practice, the CLI deserializes a system from stdin
# For this example, we just create one directly
system = System(name="piped_system")
ctx = PluginContext(config=Config(), system=system)
ctx.system.name
'piped_system'
The plugin never sees raw stdin - it just sees a populated ctx.system.
Memory Efficiency¶
PluginContext uses __slots__ to minimize memory overhead. Fields are mutable for efficiency:
from r2x_core import PluginContext, PluginConfig, System
class Config(PluginConfig):
pass
ctx = PluginContext(config=Config())
# Update fields directly
ctx.metadata["key"] = "value"
ctx.metadata
{'key': 'value'}
# System references are not copied
system = System(name="test")
ctx.system = system
ctx.system.name
'test'
The __slots__ design minimizes memory overhead while allowing efficient updates.
Performance Considerations¶
**slots design** - Minimal memory overhead per context instance
Direct assignment - O(1) field updates, no copying
Shared references - System objects are passed by reference, not copied
Type-checked - Generic
ConfigTprovides static type safety
For large systems being passed through many plugins, context updates are efficient.
Best Practices¶
Update fields directly - Simple and efficient
Declare type hints for context fields - Indicates required vs optional
Use metadata for cross-plugin data - Plugins can exchange custom data
Deserialize stdin at orchestrator level - Plugins see populated context
Pass context to plugins - Use
from_context()orrun(ctx)