Skip to main content

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

Project description

Genro Routes

Genro Routes Logo

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

Genro Routes is a transport-agnostic routing engine that decouples method routing from how those methods are exposed. Define your handlers once, then expose them via HTTP, CLI, WebSocket, or any other transport layer.

The routing logic lives in your application objects - the transport adapter (like genro-asgi for HTTP) simply maps external requests to router entries.

Why Transport-Agnostic?

Traditional web frameworks tightly couple routing to HTTP. Genro Routes separates these concerns:

Layer Responsibility
genro-routes Method registration, hierarchies, plugins, introspection
Transport adapter Protocol handling, request/response mapping

This separation enables:

  • Same handlers, multiple transports - Expose your API via HTTP and CLI without duplication
  • Runtime introspection - Query available routes, generate documentation, build admin UIs
  • Testability - Test business logic without HTTP overhead
  • Flexibility - Swap transports without changing application code

Use Cases

  • HTTP APIs - Via genro-asgi adapter
  • Internal services - Direct method invocation with plugin pipeline
  • CLI tools - Map commands to router entries
  • Admin dashboards - Runtime introspection for dynamic UIs

Key Features

  1. Instance-scoped routers - Each object instantiates its own routers (Router(self, ...)) with isolated state.
  2. Friendly registration - @route(...) accepts explicit names, auto-strips prefixes, and supports custom metadata.
  3. Simple hierarchies - attach_instance(child, name="alias") connects RoutingClass instances with path access (parent.api.node("child/method")).
  4. Plugin pipeline - BasePlugin provides on_decore/wrap_handler hooks and plugins inherit from parents automatically.
  5. Runtime configuration - routing.configure() applies global or per-handler overrides with wildcards and returns reports ("?").
  6. Built-in plugins - logging, pydantic, auth, env, and openapi plugins are included out of the box.
  7. Full coverage - The package ships with a comprehensive test suite and no hidden compatibility layers.

Quick Example

from genro_routes import RoutingClass, Router, route

class OrdersAPI(RoutingClass):
    def __init__(self, label: str):
        self.label = label
        self.api = Router(self, name="orders")

    @route("orders")
    def list(self):
        return ["order-1", "order-2"]

    @route("orders")
    def retrieve(self, ident: str):
        return f"{self.label}:{ident}"

    @route("orders")
    def create(self, payload: dict):
        return {"status": "created", **payload}

orders = OrdersAPI("acme")
print(orders.api.node("list")())        # ["order-1", "order-2"]
print(orders.api.node("retrieve")("42"))  # acme:42

overview = orders.api.nodes()
print(overview["entries"].keys())      # dict_keys(['list', 'retrieve', 'create'])

Hierarchical Routing

Build nested service structures with path access:

class UsersAPI(RoutingClass):
    def __init__(self):
        self.api = Router(self, name="api")

    @route("api")
    def list(self):
        return ["alice", "bob"]

class Application(RoutingClass):
    def __init__(self):
        self.api = Router(self, name="api")
        self.users = UsersAPI()

        # Attach child service
        self.api.attach_instance(self.users, name="users")

app = Application()
print(app.api.node("users/list")())  # ["alice", "bob"]

# Introspect hierarchy
info = app.api.nodes()
print(info["routers"].keys())  # dict_keys(['users'])

Learn by Example

We provide a comprehensive gallery of examples in the examples/ directory:

Read our guide on Why wrap a library with Genro-Routes? for more specialized insights.

Installation

pip install genro-routes

For development:

git clone https://github.com/genropy/genro-routes.git
cd genro-routes
pip install -e ".[all]"

For the Lazy: RoutingClassAuto

Don't want to create a router explicitly? Use RoutingClassAuto - it creates a "main" router automatically:

from genro_routes import RoutingClassAuto, route

class SimpleAPI(RoutingClassAuto):
    @route()  # No router name needed!
    def hello(self):
        return "Hello, World!"

api = SimpleAPI()
api.default_router.node("hello")()  # "Hello, World!"

The auto-created router is named "main" and stored internally to avoid conflicts with your attributes.

Core Concepts

  • Router - Runtime router bound directly to an object via Router(self, name="api")
  • @route("name") - Decorator that marks bound methods for the router with the matching name
  • RoutingClass - Mixin that tracks routers per instance and exposes the routing proxy
  • RoutingClassAuto - Like RoutingClass but auto-creates a "main" router if none defined
  • BasePlugin - Base class for creating plugins with on_decore and wrap_handler hooks
  • obj.routing - Proxy exposed by every RoutingClass that provides helpers like get_router(...) and configure(...) for managing routers/plugins without polluting the instance namespace.
  • RouterNode - Callable wrapper returned by node(), with path, error, doc, metadata properties.
  • NotFound / NotAuthenticated / NotAuthorized / NotAvailable - Exceptions for routing errors (not found, auth required, auth denied, capabilities missing)

Pattern Highlights

  • Explicit naming + prefixes - @route("api", name="detail") and Router(self, prefix="handle_") separate method names from public route names.
  • Explicit instance hierarchies - self.api.attach_instance(self.child, name="alias") connects RoutingClass instances with parent tracking and auto-detachment.
  • Branch routers - Router(self, branch=True) creates pure organizational nodes without handlers.
  • Built-in and custom plugins - Router(self, ...).plug("logging"), Router(self, ...).plug("pydantic"), or custom plugins.
  • Runtime configuration - routing.configure("api:logging/_all_", enabled=False) applies targeted overrides with wildcards or batch updates.
  • Lazy binding - Routers auto-bind on first use; no explicit bind() call needed.

Documentation

Testing

Genro Routes ships with a comprehensive test suite:

PYTHONPATH=src pytest --cov=src/genro_routes --cov-report=term-missing

All examples in documentation are verified by the test suite.

Repository Structure

genro-routes/
├── src/genro_routes/
│   ├── __init__.py          # Public API exports
│   ├── exceptions.py        # NotFound, NotAuthorized, NotAuthenticated, NotAvailable
│   ├── core/                # Core router implementation
│   │   ├── base_router.py   # BaseRouter (plugin-free runtime)
│   │   ├── router.py        # Router (with plugin support)
│   │   ├── router_node.py   # RouterNode (callable wrapper from node())
│   │   ├── router_interface.py  # RouterInterface (abstract base)
│   │   ├── context.py       # RoutingContext (abstract execution context)
│   │   ├── decorators.py    # @route decorator
│   │   └── routing.py       # RoutingClass, RoutingClassAuto, ResultWrapper
│   └── plugins/             # Built-in plugins
│       ├── _base_plugin.py  # BasePlugin, MethodEntry
│       ├── logging.py       # LoggingPlugin
│       ├── pydantic.py      # PydanticPlugin
│       ├── auth.py          # AuthPlugin
│       ├── env.py           # EnvPlugin (+ CapabilitiesSet)
│       └── openapi.py       # OpenAPIPlugin (+ OpenAPITranslator)
├── examples/                # Example applications
├── tests/                   # Comprehensive test suite
└── docs/                    # Documentation (Sphinx)

Project Status

Genro Routes is currently in beta. The core API is stable with complete documentation.

  • Python Support: 3.10, 3.11, 3.12, 3.13
  • License: Apache 2.0

Current Limitations

  • Instance methods only - Routers assume decorated functions are bound methods (no static/class method or free function support)
  • Minimal plugin system - Intentionally simple; advanced features must be added manually

Roadmap

  • genro-asgi - ASGI adapter for HTTP exposure (in development)
  • Additional plugins (async, storage, audit trail, metrics)
  • CLI adapter for command-line exposure
  • Example applications and use cases

Contributing

Contributions are welcome! Please see CONTRIBUTING.md for guidelines.

License

Apache License 2.0 - see LICENSE for details.

Origin

This project was originally developed as "smartroute" under MIT license and has been renamed and relicensed.

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

genro_routes-0.15.0.tar.gz (2.8 MB view details)

Uploaded Source

Built Distribution

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

genro_routes-0.15.0-py3-none-any.whl (62.0 kB view details)

Uploaded Python 3

File details

Details for the file genro_routes-0.15.0.tar.gz.

File metadata

  • Download URL: genro_routes-0.15.0.tar.gz
  • Upload date:
  • Size: 2.8 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for genro_routes-0.15.0.tar.gz
Algorithm Hash digest
SHA256 59a16d251e3f9d719a55e0f4209086d0ce086e9e8771baac390164610fa1e476
MD5 bdbc9ee3de1d7f260eb78b821145f2ea
BLAKE2b-256 c424671b57a0f926d012001e48c0578ee8c9530a14127672d65ecb551a02f0fa

See more details on using hashes here.

Provenance

The following attestation bundles were made for genro_routes-0.15.0.tar.gz:

Publisher: publish.yml on genropy/genro-routes

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

File details

Details for the file genro_routes-0.15.0-py3-none-any.whl.

File metadata

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

File hashes

Hashes for genro_routes-0.15.0-py3-none-any.whl
Algorithm Hash digest
SHA256 6bc6a49c57675d46d65371873547a5cc981ff02a68feaa486ecdc31a9384d8b3
MD5 c4078bd326078caea3900f2a5e130293
BLAKE2b-256 d36efdb0f11db74ce65f638e6e390aed8e57eb7dc4d71fb183574486226dbe80

See more details on using hashes here.

Provenance

The following attestation bundles were made for genro_routes-0.15.0-py3-none-any.whl:

Publisher: publish.yml on genropy/genro-routes

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