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 an object with get(), call(), add_child(), etc.
  • 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.0.tar.gz (22.2 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.0-py3-none-any.whl (16.2 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: smartroute-0.3.0.tar.gz
  • Upload date:
  • Size: 22.2 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.0.tar.gz
Algorithm Hash digest
SHA256 07d731de4f15a1cbf11c013105de3a0e2a027527472ea08959b6d49c9e11a1c5
MD5 4e05d4cc3b646fc4e02325544f414a8a
BLAKE2b-256 1a41ad5c92fb8fb83ddccd1d6d766755d512bf3b939d23f4be7f2092cddd79f0

See more details on using hashes here.

Provenance

The following attestation bundles were made for smartroute-0.3.0.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.0-py3-none-any.whl.

File metadata

  • Download URL: smartroute-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 16.2 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.0-py3-none-any.whl
Algorithm Hash digest
SHA256 cc237d73c2da82506fdc24729ab94f44155ac320414bae9687b93f5f24732e43
MD5 5c771269e91999d1eca0f6b281274aab
BLAKE2b-256 8ab4fa43293161473065a96e5152f5385ecdd1c3ea8182cd17683220b1f0282e

See more details on using hashes here.

Provenance

The following attestation bundles were made for smartroute-0.3.0-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