Coverage for src/lexigram/web/filters/decorators.py: 42%
19 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-25 04:37 +0800
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-25 04:37 +0800
1"""Exception filter decorators for Lexigram Framework.
3Provides @use_filters for applying exception filters to controllers and handlers.
4"""
6from __future__ import annotations
8from collections.abc import Callable
9import inspect
10from typing import Any, TypeVar, cast
12from lexigram.contracts.web.protocols import ExceptionFilterProtocol
14T = TypeVar("T")
17def use_filters(*filter_instances: ExceptionFilterProtocol) -> Callable[[T], T]:
18 """Decorator to apply exception filters to a class or method.
20 Filters handle exceptions raised during the request lifecycle.
21 Filters applied at the class level are inherited by all handler methods.
23 Example::
25 @use_filters(HttpExceptionFilter())
26 class UserController(Controller):
27 @get("/users/{id}")
28 async def get(self, id: str):
29 ...
31 Args:
32 *filter_instances: ExceptionFilterProtocol instances to apply.
34 Returns:
35 The decorated class or function.
36 """
38 def decorator(target: T) -> T:
39 if inspect.isclass(target):
40 # Apply to all methods of the class
41 for name, method in inspect.getmembers(
42 target,
43 predicate=inspect.isfunction,
44 ):
45 if not name.startswith("_"):
46 existing: list[ExceptionFilterProtocol] = (
47 getattr(method, "__filters__", None) or []
48 )
49 cast("Any", method).__filters__ = list(filter_instances) + existing
50 return cast("T", target)
52 # Apply to a single function/method
53 existing_filters: list[ExceptionFilterProtocol] = (
54 getattr(target, "__filters__", None) or []
55 )
56 cast("Any", target).__filters__ = existing_filters + list(filter_instances)
57 return target
59 return decorator
62__all__ = ["use_filters"]