Coverage for fastblocks/initializers.py: 26%

74 statements  

« prev     ^ index     » next       coverage.py v7.10.6, created at 2025-09-21 04:50 -0700

1"""Application initialization components for FastBlocks. 

2 

3This module contains classes responsible for initializing various aspects 

4of a FastBlocks application, separating concerns from the main application class. 

5""" 

6 

7import logging 

8import typing as t 

9 

10from acb import register_pkg 

11from acb.adapters import get_installed_adapter 

12from acb.config import AdapterBase, Config 

13from acb.depends import depends 

14from starception import install_error_handler 

15from starlette.applications import Starlette 

16 

17 

18class ApplicationInitializer: 

19 def __init__(self, app: Starlette, **kwargs: t.Any) -> None: 

20 self.app = app 

21 self.kwargs = kwargs 

22 self.config: t.Any | None = None 

23 self.logger: t.Any | None = None 

24 self.depends: t.Any | None = None 

25 self._acb_modules: tuple[t.Any, ...] = () 

26 

27 def initialize(self) -> None: 

28 self._load_acb_modules() 

29 self._setup_dependencies() 

30 self._configure_error_handling() 

31 self._configure_debug_mode() 

32 self._initialize_starlette() 

33 self._configure_exception_handlers() 

34 self._setup_models() 

35 self._configure_logging() 

36 

37 def _load_acb_modules(self) -> None: 

38 try: 

39 logger_class: type[t.Any] | None = depends.get("logger").__class__ 

40 from acb.logger import InterceptHandler 

41 

42 interceptor_class: type[t.Any] | None = InterceptHandler 

43 except Exception: 

44 logger_class = None 

45 interceptor_class = None 

46 self._acb_modules = ( 

47 register_pkg, 

48 get_installed_adapter, 

49 Config, 

50 AdapterBase, 

51 interceptor_class, 

52 logger_class, 

53 depends, 

54 ) 

55 

56 def _setup_dependencies(self) -> None: 

57 self.config = ( 

58 self.kwargs.get("config") 

59 if self.kwargs.get("config") is not None 

60 else depends.get("config") 

61 ) 

62 self.logger = ( 

63 self.kwargs.get("logger") 

64 if self.kwargs.get("logger") is not None 

65 else depends.get("logger") 

66 ) 

67 self.depends = depends 

68 

69 def _configure_error_handling(self) -> None: 

70 if not getattr(self.config, "deployed", False) or not getattr( 

71 getattr(self.config, "debug", None), 

72 "production", 

73 False, 

74 ): 

75 install_error_handler() 

76 

77 def _configure_debug_mode(self) -> None: 

78 debug_config = getattr(self.config, "debug", None) 

79 self.app.debug = ( 

80 getattr(debug_config, "fastblocks", False) if debug_config else False 

81 ) 

82 if self.logger: 

83 self.logger.warning(f"Fastblocks debug: {self.app.debug}") 

84 

85 def _initialize_starlette(self) -> None: 

86 Starlette.__init__( 

87 self.app, 

88 debug=self.app.debug, 

89 routes=[], 

90 middleware=self.kwargs.get("middleware") 

91 if self.kwargs.get("middleware") is not None 

92 else [], 

93 lifespan=self.kwargs.get("lifespan"), 

94 exception_handlers=self.kwargs.get("exception_handlers") 

95 if self.kwargs.get("exception_handlers") is not None 

96 else {}, 

97 ) 

98 middleware = self.kwargs.get("middleware") 

99 self.app.user_middleware = list(middleware) if middleware is not None else [] 

100 

101 def _configure_exception_handlers(self) -> None: 

102 from .exceptions import handle_exception 

103 

104 exception_handlers = self.kwargs.get("exception_handlers") 

105 if exception_handlers is None: 

106 exception_handlers = { 

107 404: handle_exception, 

108 500: handle_exception, 

109 } 

110 object.__setattr__(self.app, "exception_handlers", exception_handlers) 

111 

112 def _setup_models(self) -> None: 

113 try: 

114 models = self.depends.get("models") 

115 except Exception: 

116 models = None 

117 object.__setattr__(self.app, "models", models) 

118 

119 def _configure_logging(self) -> None: 

120 if get_installed_adapter("logfire"): 

121 from logfire import instrument_starlette # type: ignore[import-untyped] 

122 

123 instrument_starlette(self.app) 

124 interceptor_class = self._acb_modules[4] 

125 if interceptor_class: 

126 for logger_name in ( 

127 "uvicorn", 

128 "uvicorn.access", 

129 "granian", 

130 "granian.access", 

131 ): 

132 server_logger = logging.getLogger(logger_name) 

133 server_logger.handlers.clear() 

134 server_logger.addHandler(interceptor_class()) 

135 server_logger.setLevel(logging.DEBUG) 

136 server_logger.propagate = False