CI / Formatting (push) Successful in 5s
CI / Linting (push) Successful in 5s
CI / Tests (Python 3.12) (push) Successful in 2m48s
CI / Tests (Python 3.13) (push) Successful in 2m49s
CI / Tests (Python 3.14) (push) Successful in 2m43s
CI / Type Checking (push) Successful in 9s
CI / Spelling (push) Successful in 5s
103 lines
3.3 KiB
Python
103 lines
3.3 KiB
Python
# Copyright 2026 Logan Fick
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
"""HTTP route registration decorators and types for Owlbot.
|
|
|
|
This module provides the module-facing API for HTTP routes:
|
|
- @on_route decorator for registering route handlers
|
|
- RouteInfo dataclass for route metadata
|
|
- RouteHandler type alias
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from typing import TYPE_CHECKING, Any, TypedDict
|
|
|
|
if TYPE_CHECKING:
|
|
from collections.abc import Awaitable, Callable
|
|
|
|
from aiohttp import web
|
|
|
|
from .context import RouteContext
|
|
|
|
|
|
class RouteMark(TypedDict):
|
|
"""Type for the route marker attribute set by @on_route."""
|
|
|
|
path: str
|
|
methods: list[str] | None
|
|
streaming: bool
|
|
requires_session: bool
|
|
requires_authenticated: bool
|
|
requires_moderator: bool
|
|
|
|
|
|
# Handler type: receives RouteContext, returns a response or dict (auto-JSON).
|
|
type RouteHandler = (
|
|
"Callable[[RouteContext], Awaitable[web.StreamResponse | dict[str, Any] | None]]"
|
|
)
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class RouteInfo:
|
|
"""Metadata about a registered route."""
|
|
|
|
path: str # Path relative to module namespace (e.g., "/stats").
|
|
full_path: str # Full path including namespace (e.g., "/owlbot/mymodule/stats").
|
|
methods: frozenset[str] # HTTP methods (GET, POST, etc.).
|
|
handler: RouteHandler
|
|
module_name: str
|
|
streaming: bool = False
|
|
requires_session: bool = False
|
|
requires_authenticated: bool = False
|
|
requires_moderator: bool = False
|
|
|
|
|
|
def on_route(
|
|
path: str,
|
|
*,
|
|
methods: list[str] | None = None,
|
|
streaming: bool = False,
|
|
requires_session: bool = False,
|
|
requires_authenticated: bool = False,
|
|
requires_moderator: bool = False,
|
|
) -> Callable[[RouteHandler], RouteHandler]:
|
|
"""Register an HTTP route handler.
|
|
|
|
Routes are namespaced under /owlbot/<module_name>/<path>.
|
|
|
|
:param path: URL path (relative to module namespace, e.g., "/stats").
|
|
:param methods: List of HTTP methods to accept. Default: ["GET"].
|
|
:param streaming: If True, the handler bypasses the dispatch timeout.
|
|
The handler is responsible for its own keepalives and cleanup.
|
|
:return: Decorator that marks the function for registration.
|
|
"""
|
|
|
|
def decorator(func: RouteHandler) -> RouteHandler:
|
|
# Mark the function with route info for deferred registration.
|
|
# methods=None is resolved to ["GET"] by RouteRegistry.register().
|
|
# Framework decorator marker; private to module authors.
|
|
func._owlbot_route = RouteMark( # type: ignore[attr-defined] # noqa: SLF001
|
|
path=path,
|
|
methods=methods,
|
|
streaming=streaming,
|
|
requires_session=requires_session,
|
|
requires_authenticated=requires_authenticated,
|
|
requires_moderator=requires_moderator,
|
|
)
|
|
return func
|
|
|
|
return decorator
|