Source code for gobstopper.core.blueprint

"""
Blueprint system for grouping routes and related hooks.

Provides a Flask-like Blueprint API that allows organizing routes into reusable
modules that can be registered on an application with an optional URL prefix.
"""
from __future__ import annotations

from typing import Callable, Any, Awaitable
import re
from pathlib import Path

from ..http.routing import RouteHandler

# Type aliases consistent with app.py
Handler = Callable[..., Any]
Middleware = Callable[["Request", Callable[["Request"], Awaitable[Any]]], Awaitable["Response"]]
MiddlewareTuple = tuple[Middleware, int]


[docs] class Blueprint: """A collection of routes and hooks that can be registered on a Gobstopper app. Args: name: Identifier for the blueprint. url_prefix: Optional URL prefix applied when registering on an app. """
[docs] def __init__(self, name: str, url_prefix: str | None = None, *, static_folder: str | None = None, template_folder: str | None = None): self.name = name self.url_prefix = url_prefix self.routes: list[RouteHandler] = [] self.before_request_handlers: list[Handler] = [] self.after_request_handlers: list[Handler] = [] self.middleware: list[MiddlewareTuple] = [] self.children: list[tuple[Blueprint, str | None]] = [] self.static_folder = static_folder self.template_folder = template_folder
# ---- Route registration ----
[docs] def route(self, path: str, methods: list[str] | None = None): if methods is None: methods = ["GET"] def decorator(func: Handler) -> Handler: handler = RouteHandler(path, func, methods) # pick up any @use middleware attached to the function for mw, prio in getattr(func, '__route_middleware__', []) or []: handler.use(mw, prio) self.routes.append(handler) return func return decorator
[docs] def get(self, path: str): return self.route(path, ["GET"])
[docs] def post(self, path: str): return self.route(path, ["POST"])
[docs] def put(self, path: str): return self.route(path, ["PUT"])
[docs] def delete(self, path: str): return self.route(path, ["DELETE"])
[docs] def patch(self, path: str): return self.route(path, ["PATCH"])
[docs] def options(self, path: str): return self.route(path, ["OPTIONS"])
[docs] def websocket(self, path: str): def decorator(func: Handler) -> Handler: handler = RouteHandler(path, func, [], is_websocket=True) # pick up route-level middleware for mw, prio in getattr(func, '__route_middleware__', []) or []: handler.use(mw, prio) self.routes.append(handler) return func return decorator
# ---- Nested blueprints and mounts ----
[docs] def register_blueprint(self, blueprint: "Blueprint", url_prefix: str | None = None): self.children.append((blueprint, url_prefix)) return blueprint
# ---- Hooks and middleware (applied at app level upon registration) ----
[docs] def before_request(self, func: Handler) -> Handler: self.before_request_handlers.append(func) return func
[docs] def after_request(self, func: Handler) -> Handler: self.after_request_handlers.append(func) return func
[docs] def add_middleware(self, middleware: Middleware, priority: int = 0): self.middleware.append((middleware, priority)) self.middleware.sort(key=lambda item: item[1], reverse=True)
def _join_paths(prefix: str | None, path: str) -> str: if not prefix: return path if not prefix.startswith("/"): prefix = "/" + prefix if prefix.endswith("/"): prefix = prefix[:-1] if not path.startswith("/"): path = "/" + path return prefix + path