Skip to main content

Instance-scoped routing engine for Python with hierarchical handlers and composable plugins

Project description

SmartRoute

SmartRoute Logo

PyPI version Tests codecov Documentation Python 3.10+ License: MIT Code style: black

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 BoundRouter with 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 @route decorator 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 router
  • RoutedClass – Mixin that auto-finalizes routers on the class
  • BoundRouter – Runtime instance bound to un oggetto con get(), call(), add_child(), describe() e members() (runtime map).
  • BasePlugin – Base class for creating plugins with on_decore and wrap_handler hooks

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

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 pluginget(..., 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

smartroute-0.3.1.tar.gz (22.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

smartroute-0.3.1-py3-none-any.whl (16.3 kB view details)

Uploaded Python 3

File details

Details for the file smartroute-0.3.1.tar.gz.

File metadata

  • Download URL: smartroute-0.3.1.tar.gz
  • Upload date:
  • Size: 22.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for smartroute-0.3.1.tar.gz
Algorithm Hash digest
SHA256 f285c82bb103bd16a561bbd3e9f664dfab15936d8338da43d51c8804dce06eb1
MD5 7da682bc8fc38f2583e850375548491a
BLAKE2b-256 b1865826d91d3abb13d1af792462418b3e993a1b8d2fa37ac088b9d823c7c827

See more details on using hashes here.

Provenance

The following attestation bundles were made for smartroute-0.3.1.tar.gz:

Publisher: publish.yml on genropy/smartroute

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file smartroute-0.3.1-py3-none-any.whl.

File metadata

  • Download URL: smartroute-0.3.1-py3-none-any.whl
  • Upload date:
  • Size: 16.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for smartroute-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 704d62f2e7846100172c6359aab039f26fcdc026d62af52b27d53debf52dccb7
MD5 3953bf462afd4ff598b5df5746fcd967
BLAKE2b-256 625bb6fac77bd4ed19a60f0c66ebbe7a3b2b7a6381fddde8e31264c3225c0834

See more details on using hashes here.

Provenance

The following attestation bundles were made for smartroute-0.3.1-py3-none-any.whl:

Publisher: publish.yml on genropy/smartroute

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page