Coverage for src / lexigram / contracts / exceptions / provider.py: 0%

59 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-08-15 18:57 +0800

1"""Provider and module exception classes.""" 

2 

3from __future__ import annotations 

4 

5from typing import Any 

6 

7from lexigram.contracts.exceptions.base import LexigramError 

8 

9 

10class ProviderError(LexigramError): 

11 """Provider configuration or lifecycle error.""" 

12 

13 _code = "LEX_ERR_PROV_001" 

14 

15 def __init__(self, message: str = "Provider error", **kwargs: Any) -> None: 

16 super().__init__(message=message, **kwargs) 

17 

18 

19class ModuleError(LexigramError): 

20 """Module configuration or lifecycle error.""" 

21 

22 _code = "LEX_ERR_MOD_001" 

23 

24 def __init__(self, message: str = "Module error", **kwargs: Any) -> None: 

25 super().__init__(message=message, **kwargs) 

26 

27 

28class ModuleImportError(ModuleError): 

29 """A module imports a module that does not exist in the graph. 

30 

31 Raised during module compilation when a module's ``imports`` 

32 references a module class that is not registered in the 

33 application's module graph. 

34 """ 

35 

36 _code = "LEX_ERR_MOD_002" 

37 

38 def __init__( 

39 self, 

40 message: str = "Module import not found", 

41 *, 

42 module_name: str | None = None, 

43 missing_import: str | None = None, 

44 available_modules: list[str] | None = None, 

45 **kwargs: Any, 

46 ) -> None: 

47 details = kwargs.pop("details", {}) 

48 if module_name: 

49 details["module"] = module_name 

50 if missing_import: 

51 details["missing_import"] = missing_import 

52 if available_modules: 

53 details["available_modules"] = available_modules 

54 super().__init__( 

55 message=message, 

56 details=details, 

57 **kwargs, 

58 ) 

59 

60 

61class ModuleExportError(ModuleError): 

62 """A module exports a type not provided by its providers. 

63 

64 Raised during post-registration validation when a module 

65 declares an export that no provider in that module actually 

66 registered in the container. 

67 """ 

68 

69 _code = "LEX_ERR_MOD_003" 

70 

71 def __init__( 

72 self, 

73 message: str = "Module export not registered", 

74 *, 

75 module_name: str | None = None, 

76 missing_export: str | None = None, 

77 registered_types: list[str] | None = None, 

78 **kwargs: Any, 

79 ) -> None: 

80 details = kwargs.pop("details", {}) 

81 if module_name: 

82 details["module"] = module_name 

83 if missing_export: 

84 details["missing_export"] = missing_export 

85 if registered_types: 

86 details["registered_types"] = registered_types 

87 super().__init__( 

88 message=message, 

89 details=details, 

90 **kwargs, 

91 ) 

92 

93 

94class ModuleCycleError(ModuleError): 

95 """Circular dependency detected in the module import graph. 

96 

97 Raised during module compilation when a cycle is detected in 

98 the module import graph, e.g. ``A → B → C → A``. 

99 """ 

100 

101 _code = "LEX_ERR_MOD_004" 

102 

103 def __init__( 

104 self, 

105 message: str = "Circular module dependency detected", 

106 *, 

107 cycle: list[str] | None = None, 

108 **kwargs: Any, 

109 ) -> None: 

110 details = kwargs.pop("details", {}) 

111 if cycle: 

112 details["cycle"] = cycle 

113 super().__init__( 

114 message=message, 

115 details=details, 

116 **kwargs, 

117 ) 

118 

119 

120class ModuleVisibilityError(ModuleError): 

121 """A provider depends on a service not visible to its module. 

122 

123 Raised when a provider in module A tries to inject a type that 

124 is registered by module B but not exported by B, even though 

125 A imports B. 

126 """ 

127 

128 _code = "LEX_ERR_MOD_005" 

129 

130 def __init__( 

131 self, 

132 message: str = "Service not visible to module", 

133 *, 

134 consumer_module: str | None = None, 

135 provider_module: str | None = None, 

136 service_type: str | None = None, 

137 **kwargs: Any, 

138 ) -> None: 

139 details = kwargs.pop("details", {}) 

140 if consumer_module: 

141 details["consumer_module"] = consumer_module 

142 if provider_module: 

143 details["provider_module"] = provider_module 

144 if service_type: 

145 details["service_type"] = service_type 

146 super().__init__( 

147 message=message, 

148 details=details, 

149 **kwargs, 

150 ) 

151 

152 

153class ModuleDuplicateError(ModuleError): 

154 """The same module appears twice with conflicting configurations. 

155 

156 Raised when two :class:`DynamicModule` entries reference the same 

157 module class but with different provider lists, exports, or 

158 other configuration. 

159 """ 

160 

161 _code = "LEX_ERR_MOD_006" 

162 

163 def __init__( 

164 self, 

165 message: str = "Duplicate module with conflicting configuration", 

166 *, 

167 module_name: str | None = None, 

168 **kwargs: Any, 

169 ) -> None: 

170 details = kwargs.pop("details", {}) 

171 if module_name: 

172 details["module"] = module_name 

173 super().__init__( 

174 message=message, 

175 details=details, 

176 **kwargs, 

177 ) 

178 

179 

180__all__ = [ 

181 "ModuleCycleError", 

182 "ModuleDuplicateError", 

183 "ModuleError", 

184 "ModuleExportError", 

185 "ModuleImportError", 

186 "ModuleVisibilityError", 

187 "ProviderError", 

188]