Files
Owlbot/owlbot/api/routes.py
T
LogalDeveloper 5443aa86ce
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
Added browser sessions and protected route support.
2026-05-04 21:01:57 -04:00

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