Plugin Configuration
Configure plugins at runtime through a unified API with support for global settings, per-handler overrides, and batch updates.
Overview
Genro Routes provides routing.configure() for runtime plugin configuration with:
Target syntax:
<router>:<plugin>/<selector>formatGlobal configuration: Apply to all handlers with
_all_Handler-specific overrides: Target individual handlers
Glob patterns: Match multiple handlers with wildcards
Batch updates: Configure multiple targets in one call
Introspection: Query configuration with
"?"
Target Syntax
A configuration target has three parts:
<router_name>:<plugin_name>/<selector>
Examples:
api:logging/_all_- Apply to all handlers in the logging pluginapi:logging/foo- Apply only to thefoohandlerapi:logging/b*- Apply to handlers matching glob patternb*
Selectors:
_all_(case-insensitive) - Global plugin settingsHandler name - Specific handler (e.g.,
foo,bar)Glob pattern - Multiple handlers (e.g.,
admin_*,*_detail)
Glob Pattern Syntax
The selector part of the target supports fnmatch glob patterns for matching multiple handlers:
Pattern |
Matches |
Example Target |
|---|---|---|
|
Any string |
|
|
Any single character |
|
|
Any char in brackets |
|
|
Any char NOT in brackets |
|
|
Prefix match |
|
|
Suffix match |
|
Glob pattern examples:
class Service(RoutingClass):
def __init__(self):
self.api = Router(self, name="api").plug("logging")
@route("api")
def admin_list(self): pass
@route("api")
def admin_create(self): pass
@route("api")
def admin_delete(self): pass
@route("api")
def user_profile(self): pass
@route("api")
def user_settings(self): pass
svc = Service()
# Disable logging for all admin handlers
svc.routing.configure("api:logging/admin_*", enabled=False)
# Set debug level for all user handlers
svc.routing.configure("api:logging/user_*", level="debug")
# Configure multiple patterns with comma-separated selector
svc.routing.configure("api:logging/admin_list,admin_create", threshold=5)
Combining patterns:
# Multiple comma-separated patterns in selector
svc.routing.configure("api:logging/admin_*,user_*", enabled=True)
# Each pattern is matched independently:
# admin_* matches: admin_list, admin_create, admin_delete
# user_* matches: user_profile, user_settings
Basic Configuration
Configure plugins using keyword arguments:
from genro_routes import RoutingClass, Router, route
class ConfService(RoutingClass):
def __init__(self):
self.api = Router(self, name="api").plug("logging")
@route("api")
def foo(self):
return "foo"
@route("api")
def bar(self):
return "bar"
svc = ConfService()
# Global configuration - applies to all handlers
svc.routing.configure("api:logging/_all_", threshold=10)
assert svc.api.logging.configuration()["threshold"] == 10
# Handler-specific configuration
svc.routing.configure("api:logging/foo", enabled=False)
assert svc.api.logging.configuration("foo")["enabled"] is False
# Glob pattern configuration
svc.routing.configure("api:logging/b*", mode="strict")
assert svc.api.logging.configuration("bar")["mode"] == "strict"
Configuration keys depend on the plugin. Common keys:
enabled- Enable/disable plugin for handler(s)flags- Plugin-specific flagsthreshold,mode,level- Plugin-specific settings
Batch Updates
Configure multiple targets with a list of dictionaries:
# JSON-friendly batch configuration
payload = [
{"target": "api:logging/_all_", "flags": "trace"},
{"target": "api:logging/foo", "limit": 5},
]
result = svc.routing.configure(payload)
assert len(result) == 2
assert svc.api.logging.configuration("foo")["limit"] == 5
Each dictionary must have:
targetkey - Configuration target stringAdditional keys - Configuration options
Returns: List of configuration results (one per target)
Use cases:
External configuration files (JSON, YAML)
HTTP API endpoints
CLI configuration commands
Orchestration layers
Introspection
Query the router and plugin structure with "?":
class Leaf(RoutingClass):
def __init__(self):
self.api = Router(self, name="api").plug("logging")
@route("api")
def ping(self):
return "leaf"
class Root(RoutingClass):
def __init__(self):
self.api = Router(self, name="api").plug("logging")
self.leaf = Leaf()
self.attach_instance(self.leaf, name="leaf")
@route("api")
def root_ping(self):
return "root"
svc = Root()
# Get full configuration tree
info = svc.routing.configure("?")
assert "api" in info
assert info["api"]["plugins"]
assert "leaf" in info["api"]["routers"]
Returns nested dictionary with:
Router names
Attached plugins and their configurations
Child routers
Registered handlers
Exposing Configuration API
Create a dedicated configuration endpoint:
class ConfigAPI(RoutingClass):
def __init__(self):
self.api = Router(self, name="api").plug("logging")
self.admin = Router(self, name="admin")
@route("admin")
def configure_plugin(self, target: str, **options):
"""Configure plugins via API endpoint."""
result = self.routing.configure(target, **options)
return {"status": "ok", "result": result}
config = ConfigAPI()
# Call via router
result = config.admin.node("configure_plugin")("api:logging/_all_", enabled=True)
assert result["status"] == "ok"
Benefits:
External configuration without code changes
Runtime adjustments
API-driven configuration management
Dynamic plugin tuning
Error Handling
Invalid targets raise exceptions:
ValueError- Malformed target syntaxAttributeError- Router or plugin not foundKeyError- Selector matches no handlers
Validation:
# Router name cannot be empty
try:
svc.routing.configure(":logging/_all_", enabled=True)
except ValueError:
pass # Expected
# Plugin must exist
try:
svc.routing.configure("api:nonexistent/_all_", enabled=True)
except AttributeError:
pass # Expected
# Selector must match at least one handler
try:
svc.routing.configure("api:logging/nonexistent", enabled=True)
except KeyError:
pass # Expected
Best Practices
Global defaults, specific overrides:
# Set defaults for all handlers
svc.routing.configure("api:logging/_all_", enabled=True, level="info")
# Override for specific handlers
svc.routing.configure("api:logging/debug_*", level="debug")
svc.routing.configure("api:logging/admin_*", enabled=False)
Configuration from files:
import json
# Load from JSON configuration
with open("plugin_config.json") as f:
config = json.load(f)
# Apply batch configuration
svc.routing.configure(config["plugins"])
Gradual rollout:
# Enable new feature for test handlers only
svc.routing.configure("api:new_feature/test_*", enabled=True)
# Expand to all after validation
svc.routing.configure("api:new_feature/_all_", enabled=True)
Next Steps
Plugin Development - Create custom plugins
Built-in Plugins - LoggingPlugin and PydanticPlugin reference
API Reference - Complete API documentation