Coverage for src/lexigram/web/dependencies/state.py: 71%
21 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
1from __future__ import annotations
3from typing import Any, TypeVar
5from starlette import status
6from starlette.requests import Request
8from lexigram.logging import get_logger
9from lexigram.web.exceptions import HTTPError
11logger = get_logger(__name__)
13T = TypeVar("T")
16class RequestState:
17 """Type-safe request state accessor."""
19 @staticmethod
20 def get(
21 request: Request,
22 key: str,
23 type_: type[T], # noqa: ARG004
24 default: T | None = None,
25 ) -> T | None:
26 """Get value from request state with default.
28 Args:
29 request: The incoming HTTP request.
30 key: State attribute name.
31 type_: Expected return type (used for static analysis).
32 default: Fallback when the attribute is absent.
33 """
34 return getattr(request.state, key, default)
36 @staticmethod
37 def require(
38 request: Request,
39 key: str,
40 error_message: str | None = None,
41 ) -> Any:
42 """Get value from request state or raise HTTP 500."""
43 if not hasattr(request.state, key):
44 logger.error(
45 "Required request state '%s' not set. Check middleware configuration.",
46 key,
47 )
48 raise HTTPError(
49 status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
50 detail=error_message or f"Internal error: {key} not initialized",
51 )
53 return getattr(request.state, key)
55 @staticmethod
56 def set(
57 request: Request,
58 key: str,
59 value: Any,
60 ) -> None:
61 """Set value in request state."""
62 setattr(request.state, key, value)