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> format

  • Global 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 plugin

  • api:logging/foo - Apply only to the foo handler

  • api:logging/b* - Apply to handlers matching glob pattern b*

Selectors:

  • _all_ (case-insensitive) - Global plugin settings

  • Handler 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

api:logging/* (all handlers)

?

Any single character

api:logging/get_? (get_a, get_b, …)

[abc]

Any char in brackets

api:logging/get_[123]

[!abc]

Any char NOT in brackets

api:logging/[!_]* (not starting with _)

admin_*

Prefix match

api:logging/admin_*

*_detail

Suffix match

api:logging/*_detail

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 flags

  • threshold, 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:

  • target key - Configuration target string

  • Additional 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 syntax

  • AttributeError - Router or plugin not found

  • KeyError - 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