Coverage for src / lexigram / contracts / core / middleware.py: 0%
19 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-15 18:57 +0800
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-15 18:57 +0800
1"""Middleware protocols for Lexigram Framework.
3This module defines the universal middleware contract that is
4transport-agnostic and can be adapted to ASGI, WSGI, or other transports.
5"""
7from __future__ import annotations
9from typing import TYPE_CHECKING, Any, Protocol, TypeVar, runtime_checkable
11if TYPE_CHECKING:
12 from collections.abc import Awaitable, Callable, Coroutine
14T = TypeVar("T")
15TContext = TypeVar("TContext")
16TResult = TypeVar("TResult")
19@runtime_checkable
20class MiddlewareProtocol(Protocol[TContext, TResult]):
21 """Universal middleware contract.
23 Middleware wraps async operations with cross-cutting concerns
24 (logging, metrics, security, etc.). This protocol is transport-agnostic.
26 Example:
27 ```python
28 class LoggingMiddleware(MiddlewareProtocol[dict[str, Any], Any]):
29 async def __call__(
30 self,
31 context: dict[str, Any],
32 next: Callable[[dict[str, Any]], Awaitable[Any]]
33 ) -> Any:
34 print(f"Before: {context}")
35 result = await next(context)
36 print(f"After: {result}")
37 return result
38 ```
39 """
41 async def __call__(
42 self,
43 context: TContext,
44 next: Callable[[TContext], Awaitable[TResult]],
45 ) -> TResult:
46 """Process the context and call the next middleware/handler.
48 Args:
49 context: Arbitrary context (dict, request, scope, etc.)
50 next: Callable to invoke the next middleware or handler
52 Returns:
53 TResult: Result from the next middleware/handler
54 """
55 ...
58Middleware = MiddlewareProtocol[dict[str, Any], Any]
61@runtime_checkable
62class ExceptionFilterChainProtocol(Protocol):
63 """Protocol for exception filter chains."""
65 def add(self, filter_: Any) -> ExceptionFilterChainProtocol:
66 """Add a filter to the chain.
68 Args:
69 filter_: The filter to add (should satisfy ExceptionFilterProtocol protocol).
71 Returns:
72 The filter chain for fluent chaining.
73 """
74 ...
76 async def handle(
77 self,
78 exc: Exception,
79 request: Any,
80 fallback: Callable[..., Any] | None = None,
81 ) -> Any:
82 """Handle an exception by passing it through the chain.
84 Args:
85 exc: The exception to handle.
86 request: The request context.
87 fallback: Optional fallback handler.
89 Returns:
90 The result of the filter chain (e.g. a Response object).
91 """
92 ...
95@runtime_checkable
96class MiddlewarePipelineProtocol(Protocol):
97 """Protocol for an immutable, composable middleware pipeline.
99 Middleware pipelines chain ``async (context, next_handler) -> result``
100 callables so that each middleware wraps the next. Implementations must
101 be immutable — ``add`` returns a **new** pipeline rather than mutating
102 the existing one.
104 Example:
105 ```python
106 pipeline = MiddlewarePipeline()
107 pipeline = pipeline.add(LoggingMiddleware())
108 pipeline = pipeline.add(MetricsMiddleware())
109 result = await pipeline.execute(context, handler)
110 ```
111 """
113 def add(self, middleware: Any) -> MiddlewarePipelineProtocol:
114 """Return a new pipeline with *middleware* appended.
116 Args:
117 middleware: An ``async (context, next_handler) -> result`` callable.
119 Returns:
120 A new pipeline with the middleware added.
121 """
122 ...
124 async def execute(
125 self,
126 context: Any,
127 handler: Callable[..., Coroutine[Any, Any, Any]],
128 ) -> Any:
129 """Execute the pipeline, passing *context* through all middleware.
131 Args:
132 context: The request/context object to process.
133 handler: The final ``async (context) -> result`` handler at the
134 end of the chain.
136 Returns:
137 The result from the handler (possibly transformed by middleware).
138 """
139 ...
141 def __len__(self) -> int:
142 """Return the number of middleware in the pipeline."""
143 ...
146__all__ = [
147 "ExceptionFilterChainProtocol",
148 "Middleware",
149 "MiddlewarePipelineProtocol",
150 "MiddlewareProtocol",
151]