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

1from __future__ import annotations 

2 

3from typing import Any, TypeVar 

4 

5from starlette import status 

6from starlette.requests import Request 

7 

8from lexigram.logging import get_logger 

9from lexigram.web.exceptions import HTTPError 

10 

11logger = get_logger(__name__) 

12 

13T = TypeVar("T") 

14 

15 

16class RequestState: 

17 """Type-safe request state accessor.""" 

18 

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. 

27 

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) 

35 

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 ) 

52 

53 return getattr(request.state, key) 

54 

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)