Instance-scoped routing engine for Python with hierarchical handlers and composable plugins
Project description
SmartRoute
SmartRoute is an instance-scoped routing engine for Python that enables dynamic method dispatch through a plugin-based architecture. It's the successor to SmartSwitch, designed with instance isolation and composability at its core.
What is SmartRoute?
SmartRoute allows you to organize and dispatch method calls dynamically based on string identifiers (routes). Each object instance gets its own isolated router with independent plugin stacks, making it ideal for building modular, extensible services where behavior can be customized per-instance without global state.
Key Features
- Instance-scoped routers – Every object gets an isolated
BoundRouterwith its own plugin stack and configuration - Hierarchical organization – Build router trees with
add_child()and dotted path traversal (root.api.get("users.list")) - Composable plugins – Hook into decoration and handler execution with
BasePlugin(logging, validation, metrics) - Plugin inheritance – Plugins propagate automatically from parent to child routers
- Flexible registration – Use
@routedecorator with aliases, prefixes, and custom names - Runtime configuration – Enable/disable plugins per-handler, set runtime data dynamically
- SmartAsync support – Optional integration with async execution
- High test coverage – >95% statement coverage with comprehensive edge case tests
Quick Example
from smartroute import RoutedClass, Router, route
class Service(RoutedClass):
api = Router(name="service")
def __init__(self, label: str):
self.label = label
@route("api")
def describe(self):
return f"service:{self.label}"
# Each instance is isolated
first = Service("alpha")
second = Service("beta")
assert first.api.get("describe")() == "service:alpha"
assert second.api.get("describe")() == "service:beta"
Installation
pip install smartroute
For development:
git clone https://github.com/genropy/smartroute.git
cd smartroute
pip install -e ".[all]"
To use the Pydantic plugin:
pip install smartroute[pydantic]
Core Concepts
Router– Descriptor for defining routers on classes@route("name")– Decorator to register methods with a routerRoutedClass– Mixin that auto-finalizes routers on the classBoundRouter– Runtime instance bound to an object withget(),call(),add_child(), etc.BasePlugin– Base class for creating plugins withon_decoreandwrap_handlerhooks
Examples
Basic Routing with Aliases
from smartroute import RoutedClass, Router, route
class SubService(RoutedClass):
routes = Router(prefix="handle_")
def __init__(self, prefix: str):
self.prefix = prefix
@route("routes")
def handle_list(self):
return f"{self.prefix}:list"
@route("routes", alias="detail")
def handle_detail(self, ident: int):
return f"{self.prefix}:detail:{ident}"
sub = SubService("users")
assert sub.routes.get("list")() == "users:list"
assert sub.routes.get("detail")(10) == "users:detail:10"
Hierarchical Routers
class RootAPI(RoutedClass):
api = Router(name="root")
def __init__(self):
users = SubService("users")
products = SubService("products")
self.api.add_child(users, name="users")
self.api.add_child(products, name="products")
root = RootAPI()
assert root.api.get("users.list")() == "users:list"
assert root.api.get("products.detail")(5) == "products:detail:5"
Bulk Child Registration
class RootAPI(RoutedClass):
api = Router(name="root")
def __init__(self):
self.users = SubService("users")
self.products = SubService("products")
# Register multiple children via dict
self.api.add_child({
"users": self.users,
"products": self.products
})
root = RootAPI()
assert root.api.get("users.list")() == "users:list"
Plugins
Built-in plugins (logging, pydantic) are pre-registered and can be used by name:
class PluginService(RoutedClass):
api = Router(name="plugin").plug("logging")
@route("api")
def do_work(self):
return "ok"
svc = PluginService()
result = svc.api.get("do_work")() # Logged automatically
Pydantic Validation
class ValidateService(RoutedClass):
api = Router(name="validate").plug("pydantic")
@route("api")
def concat(self, text: str, number: int = 1) -> str:
return f"{text}:{number}"
svc = ValidateService()
assert svc.api.get("concat")("hello", 3) == "hello:3"
assert svc.api.get("concat")("hi") == "hi:1" # Default works
# Invalid types raise ValidationError
# svc.api.get("concat")(123, "oops") # ValidationError!
Custom Plugins
Create your own plugins by subclassing BasePlugin:
from smartroute import RoutedClass, Router, route
from smartroute.core import BasePlugin, MethodEntry # Not public API
class MetricsPlugin(BasePlugin):
def __init__(self):
super().__init__(name="metrics")
self.call_counts = {}
def on_decore(self, router, func, entry: MethodEntry):
"""Called during route registration"""
self.call_counts[entry.name] = 0
def wrap_handler(self, router, entry: MethodEntry, call_next):
"""Wrap handler execution"""
def wrapper(*args, **kwargs):
self.call_counts[entry.name] += 1
return call_next(*args, **kwargs)
return wrapper
# Register your plugin
Router.register_plugin("metrics", MetricsPlugin)
# Use it like built-in plugins
class Service(RoutedClass):
api = Router().plug("metrics")
@route("api")
def work(self): return "done"
svc = Service()
svc.api.get("work")()
print(svc.api.metrics.call_counts) # {"work": 1}
See llm-docs/PATTERNS.md#pattern-12-custom-plugin-development for more examples.
Documentation
- Full Documentation – Complete guides, tutorials, and API reference
- Quick Start – Get started in 5 minutes
- LLM Reference – Token-optimized reference for AI code generation
- API Details – Complete API reference generated from tests
- Usage Patterns – Common patterns extracted from test suite
Testing
SmartRoute targets >95% statement coverage (currently ~98%):
PYTHONPATH=src pytest --cov=src/smartroute --cov-report=term-missing
All examples in documentation are verified by the test suite.
Repository Structure
smartroute/
├── src/smartroute/
│ ├── core/ # Core router implementation
│ │ ├── router.py # Router and BoundRouter
│ │ ├── decorators.py # @route and @routers decorators
│ │ └── base.py # BasePlugin and MethodEntry
│ └── plugins/ # Built-in plugins
│ ├── logging.py # LoggingPlugin
│ └── pydantic.py # PydanticPlugin
├── tests/ # Test suite (>95% coverage)
│ ├── test_switcher_basic.py # Core functionality
│ ├── test_router_edge_cases.py # Edge cases
│ ├── test_plugins_new.py # Plugin system
│ └── test_pydantic_plugin.py # Pydantic validation
├── docs/ # Human documentation (Sphinx)
├── llm-docs/ # LLM-optimized documentation
└── examples/ # Example implementations
Project Status
SmartRoute is currently in alpha (v0.1.0). The core API is stable, but documentation and additional plugins are still being developed.
- Test Coverage: >95%
- Python Support: 3.10, 3.11, 3.12
- License: MIT
Current Limitations
- Instance methods only – Routers assume decorated functions are bound methods (no static/class method or free function support)
- No SmartAsync plugin –
get(..., use_smartasync=True)is optional but there's no dedicated SmartAsync plugin - Minimal plugin system – Intentionally simple; advanced features (e.g., Pydantic declarative config) must be added manually
Roadmap
- Complete Sphinx documentation with tutorials and API reference
- Additional plugins (async, storage, audit trail, metrics)
- Benchmarks and performance comparison with SmartSwitch
- Migration guide from SmartSwitch
Contributing
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
License
MIT License - see LICENSE for details.
Acknowledgments
SmartRoute is the successor to SmartSwitch, designed with lessons learned from production use.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file smartroute-0.2.0.tar.gz.
File metadata
- Download URL: smartroute-0.2.0.tar.gz
- Upload date:
- Size: 20.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5902bfaceced1ffc41ccb3d96b804eb64508db96f424f7d9af713af510cd2083
|
|
| MD5 |
72b141c8ae769642d386dca26fa2e7c7
|
|
| BLAKE2b-256 |
1d8d3dc51ba1f7167e8158e0e54569d2c9a28482dc354c4978e8e8ddcb8e3a64
|
Provenance
The following attestation bundles were made for smartroute-0.2.0.tar.gz:
Publisher:
publish.yml on genropy/smartroute
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
smartroute-0.2.0.tar.gz -
Subject digest:
5902bfaceced1ffc41ccb3d96b804eb64508db96f424f7d9af713af510cd2083 - Sigstore transparency entry: 705428895
- Sigstore integration time:
-
Permalink:
genropy/smartroute@4dee910610c91275b3e577923b4a617a38cf702d -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/genropy
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@4dee910610c91275b3e577923b4a617a38cf702d -
Trigger Event:
push
-
Statement type:
File details
Details for the file smartroute-0.2.0-py3-none-any.whl.
File metadata
- Download URL: smartroute-0.2.0-py3-none-any.whl
- Upload date:
- Size: 15.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a2b8aa3ac945d1edd41e673f37418a5c6e8071ef9a73ffae588ca8a16431fb9d
|
|
| MD5 |
878b482e38f7a72b9ff39bfdb250d2cf
|
|
| BLAKE2b-256 |
76f1b7d0fc191f051a08077fdfa380a9f58a92ef3778a5d58371f0d8d4ed4f6b
|
Provenance
The following attestation bundles were made for smartroute-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on genropy/smartroute
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
smartroute-0.2.0-py3-none-any.whl -
Subject digest:
a2b8aa3ac945d1edd41e673f37418a5c6e8071ef9a73ffae588ca8a16431fb9d - Sigstore transparency entry: 705428897
- Sigstore integration time:
-
Permalink:
genropy/smartroute@4dee910610c91275b3e577923b4a617a38cf702d -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/genropy
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@4dee910610c91275b3e577923b4a617a38cf702d -
Trigger Event:
push
-
Statement type: