Coverage for src / marimushka / dependencies.py: 100%

31 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-02-28 17:41 +0000

1"""Dependency injection container for marimushka. 

2 

3This module provides a dependency container pattern that encapsulates all 

4injectable dependencies used throughout the application. This makes it easier 

5to manage dependencies, test components in isolation, and customize behavior. 

6 

7Example:: 

8 

9 from marimushka.dependencies import Dependencies, create_dependencies 

10 

11 # Use default dependencies 

12 deps = create_dependencies() 

13 

14 # Or customize for testing/special cases 

15 from marimushka.audit import AuditLogger 

16 from marimushka.config import MarimushkaConfig 

17 

18 custom_audit = AuditLogger(log_file=Path("custom_audit.log")) 

19 custom_config = MarimushkaConfig(max_workers=8, timeout=600) 

20 

21 deps = Dependencies( 

22 audit_logger=custom_audit, 

23 config=custom_config 

24 ) 

25 

26 # Use in application 

27 from marimushka.export import main_with_deps 

28 html = main_with_deps(deps, notebooks="notebooks", apps="apps") 

29 

30""" 

31 

32from dataclasses import dataclass, field 

33from pathlib import Path 

34 

35from .audit import AuditLogger, get_audit_logger 

36from .config import MarimushkaConfig 

37 

38 

39@dataclass 

40class Dependencies: 

41 """Container for all injectable dependencies. 

42 

43 This class holds all dependencies that can be injected throughout the 

44 application, providing a single point of configuration and making it 

45 easy to customize behavior for testing or special use cases. 

46 

47 Attributes: 

48 audit_logger: Logger for security-relevant audit events. 

49 config: Configuration settings for export behavior. 

50 

51 Example:: 

52 

53 from marimushka.dependencies import Dependencies 

54 from marimushka.audit import AuditLogger 

55 from marimushka.config import MarimushkaConfig 

56 

57 # Create custom dependencies 

58 deps = Dependencies( 

59 audit_logger=AuditLogger(log_file=Path("my_audit.log")), 

60 config=MarimushkaConfig(max_workers=8) 

61 ) 

62 

63 # Use with export functions 

64 from marimushka.orchestrator import generate_index 

65 html = generate_index( 

66 output=Path("_site"), 

67 template_file=Path("template.html.j2"), 

68 notebooks=[], 

69 audit_logger=deps.audit_logger 

70 ) 

71 

72 """ 

73 

74 audit_logger: AuditLogger = field(default_factory=get_audit_logger) 

75 config: MarimushkaConfig = field(default_factory=MarimushkaConfig) 

76 

77 def with_audit_logger(self, audit_logger: AuditLogger) -> "Dependencies": 

78 """Create a new Dependencies instance with a different audit logger. 

79 

80 Args: 

81 audit_logger: The new audit logger to use. 

82 

83 Returns: 

84 A new Dependencies instance with the specified audit logger. 

85 

86 Example:: 

87 

88 from marimushka.dependencies import create_dependencies 

89 from marimushka.audit import AuditLogger 

90 from pathlib import Path 

91 

92 deps = create_dependencies() 

93 test_deps = deps.with_audit_logger( 

94 AuditLogger(log_file=Path("test_audit.log")) 

95 ) 

96 

97 """ 

98 return Dependencies( 

99 audit_logger=audit_logger, 

100 config=self.config, 

101 ) 

102 

103 def with_config(self, config: MarimushkaConfig) -> "Dependencies": 

104 """Create a new Dependencies instance with a different configuration. 

105 

106 Args: 

107 config: The new configuration to use. 

108 

109 Returns: 

110 A new Dependencies instance with the specified configuration. 

111 

112 Example:: 

113 

114 from marimushka.dependencies import create_dependencies 

115 from marimushka.config import MarimushkaConfig 

116 

117 deps = create_dependencies() 

118 prod_deps = deps.with_config( 

119 MarimushkaConfig(max_workers=16, timeout=900) 

120 ) 

121 

122 """ 

123 return Dependencies( 

124 audit_logger=self.audit_logger, 

125 config=config, 

126 ) 

127 

128 

129def create_dependencies( 

130 audit_log: Path | None = None, 

131 config: MarimushkaConfig | None = None, 

132) -> Dependencies: 

133 """Create a Dependencies container with optional customization. 

134 

135 This factory function provides a convenient way to create Dependencies 

136 with common customizations while falling back to sensible defaults. 

137 

138 Args: 

139 audit_log: Optional path to audit log file. If provided, creates 

140 an AuditLogger that writes to this file. 

141 config: Optional custom configuration. If None, uses default config. 

142 

143 Returns: 

144 A Dependencies instance with the specified settings. 

145 

146 Example:: 

147 

148 from pathlib import Path 

149 from marimushka.dependencies import create_dependencies 

150 from marimushka.config import MarimushkaConfig 

151 

152 # Simple usage with audit log 

153 deps = create_dependencies(audit_log=Path("audit.log")) 

154 

155 # With custom config 

156 config = MarimushkaConfig(max_workers=8, parallel=True) 

157 deps = create_dependencies(config=config) 

158 

159 # With both 

160 deps = create_dependencies( 

161 audit_log=Path("audit.log"), 

162 config=MarimushkaConfig(timeout=600) 

163 ) 

164 

165 """ 

166 audit_logger = AuditLogger(log_file=audit_log) if audit_log is not None else get_audit_logger() 

167 

168 if config is None: 

169 config = MarimushkaConfig() 

170 

171 return Dependencies( 

172 audit_logger=audit_logger, 

173 config=config, 

174 ) 

175 

176 

177def create_dependencies_from_config_file( 

178 config_path: Path, 

179 audit_log: Path | None = None, 

180) -> Dependencies: 

181 """Create Dependencies by loading configuration from a TOML file. 

182 

183 This function loads configuration from a file and creates a Dependencies 

184 container with it, optionally overriding the audit log path. 

185 

186 Args: 

187 config_path: Path to the TOML configuration file. 

188 audit_log: Optional override for audit log path. If None, uses 

189 the audit log path from the config file (if specified). 

190 

191 Returns: 

192 A Dependencies instance with settings from the config file. 

193 

194 Raises: 

195 FileNotFoundError: If config_path doesn't exist. 

196 ValueError: If the config file is invalid. 

197 

198 Example:: 

199 

200 from pathlib import Path 

201 from marimushka.dependencies import create_dependencies_from_config_file 

202 

203 # Load from config file 

204 deps = create_dependencies_from_config_file( 

205 Path(".marimushka.toml") 

206 ) 

207 

208 # Override audit log 

209 deps = create_dependencies_from_config_file( 

210 config_path=Path(".marimushka.toml"), 

211 audit_log=Path("custom_audit.log") 

212 ) 

213 

214 """ 

215 config = MarimushkaConfig.from_file(config_path) 

216 

217 # Determine audit log path 

218 if audit_log is not None: 

219 # Use explicit override 

220 audit_log_path = audit_log 

221 elif config.audit_log is not None: 

222 # Use path from config 

223 audit_log_path = Path(config.audit_log) 

224 else: 

225 # No audit log file 

226 audit_log_path = None 

227 

228 # Create audit logger 

229 if audit_log_path is not None: 

230 audit_logger = AuditLogger( 

231 enabled=config.audit_enabled, 

232 log_file=audit_log_path, 

233 ) 

234 else: 

235 audit_logger = AuditLogger(enabled=config.audit_enabled) 

236 

237 return Dependencies( 

238 audit_logger=audit_logger, 

239 config=config, 

240 ) 

241 

242 

243def create_test_dependencies(tmp_dir: Path) -> Dependencies: 

244 """Create Dependencies suitable for testing. 

245 

246 This function creates a Dependencies container configured for testing, 

247 with audit logging to a temporary file and default configuration. 

248 

249 Args: 

250 tmp_dir: Temporary directory for test artifacts. 

251 

252 Returns: 

253 A Dependencies instance configured for testing. 

254 

255 Example:: 

256 

257 from pathlib import Path 

258 from marimushka.dependencies import create_test_dependencies 

259 

260 def test_something(tmp_path): 

261 deps = create_test_dependencies(tmp_path) 

262 # Use deps in your test... 

263 

264 """ 

265 audit_log = tmp_dir / "test_audit.log" 

266 return Dependencies( 

267 audit_logger=AuditLogger(enabled=True, log_file=audit_log), 

268 config=MarimushkaConfig(), 

269 )