"""Decorator helpers for marking routed methods.
This module contains only marker helpers; no router mutation happens at
decoration time.
``route(router, *, name=None, **kwargs)``
Returns a decorator storing metadata on the function under ``_route_decorator_kw``
as a list of dicts. Each payload starts with ``{"name": router}``.
- Explicit logical name: if ``name`` is provided, the payload sets ``entry_name``
to that value. Otherwise the handler name defaults to the function name.
- Extra ``**kwargs`` are copied verbatim into the payload (e.g. plugin flags).
- Multiple routers can target the same function by stacking decorators.
- The decorator returns the original function unchanged aside from the marker.
Re-exports
----------
This module re-exports ``RoutingClass`` and ``Router`` for convenience so user
code can import everything from one place.
"""
from __future__ import annotations
from collections.abc import Callable
from typing import Any
from .router import Router
from .routing import RoutingClass
__all__ = ["route", "RoutingClass", "Router"]
[docs]
def route(
router: str | None = None,
*,
name: str | None = None,
endpoint_id: str | None = None,
**kwargs: Any,
) -> Callable[[Callable], Callable]:
"""Mark a bound method for inclusion in the given router.
Args:
router: Router identifier (e.g. ``"api"``). If None, uses the default
router (only works if the class has exactly one router).
name: Optional explicit entry name (overrides function name/prefix stripping).
endpoint_id: Optional globally unique identifier for reverse lookup.
When set, the handler can be resolved via ``router.node("@endpoint_id")``.
**kwargs: Extra metadata merged into handler entry (e.g. plugin flags).
Returns:
Decorator that marks the function for the specified router.
Example::
@route("api")
def list_users(self):
return ["alice", "bob"]
@route("api", name="custom_name", logging_enabled=False)
def get_user(self, user_id):
return {"id": user_id}
# With single router - @route() works without arguments:
class Table(RoutingClass):
def __init__(self):
self.api = Router(self, name="api") # single router
@route() # Uses the only router automatically
def add(self, data):
...
# With multiple routers - must specify:
class Service(RoutingClass):
def __init__(self):
self.api = Router(self, name="api")
self.admin = Router(self, name="admin")
@route("api") # Must specify router name
def public(self):
...
"""
def decorator(func: Callable) -> Callable:
markers = list(getattr(func, "_route_decorator_kw", []))
payload: dict[str, Any] = {"name": router} # None means "use default_router"
if name is not None:
payload["entry_name"] = name
if endpoint_id is not None:
payload["endpoint_id"] = endpoint_id
for key, value in kwargs.items():
payload[key] = value
markers.append(payload)
setattr(func, "_route_decorator_kw", markers) # noqa: B010
return func
return decorator