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 PyPI downloads

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.2.0.tar.gz (20.8 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.2.0-py3-none-any.whl (15.3 kB view details)

Uploaded Python 3

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

Hashes for smartroute-0.2.0.tar.gz
Algorithm Hash digest
SHA256 5902bfaceced1ffc41ccb3d96b804eb64508db96f424f7d9af713af510cd2083
MD5 72b141c8ae769642d386dca26fa2e7c7
BLAKE2b-256 1d8d3dc51ba1f7167e8158e0e54569d2c9a28482dc354c4978e8e8ddcb8e3a64

See more details on using hashes here.

Provenance

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

Hashes for smartroute-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a2b8aa3ac945d1edd41e673f37418a5c6e8071ef9a73ffae588ca8a16431fb9d
MD5 878b482e38f7a72b9ff39bfdb250d2cf
BLAKE2b-256 76f1b7d0fc191f051a08077fdfa380a9f58a92ef3778a5d58371f0d8d4ed4f6b

See more details on using hashes here.

Provenance

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