fast-clean-architecture 1.0.0__py3-none-any.whl → 1.1.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fast_clean_architecture/__init__.py +5 -6
- fast_clean_architecture/analytics.py +260 -0
- fast_clean_architecture/cli.py +563 -46
- fast_clean_architecture/config.py +47 -23
- fast_clean_architecture/error_tracking.py +201 -0
- fast_clean_architecture/exceptions.py +432 -12
- fast_clean_architecture/generators/__init__.py +11 -1
- fast_clean_architecture/generators/component_generator.py +407 -103
- fast_clean_architecture/generators/config_updater.py +186 -38
- fast_clean_architecture/generators/generator_factory.py +223 -0
- fast_clean_architecture/generators/package_generator.py +9 -7
- fast_clean_architecture/generators/template_validator.py +109 -9
- fast_clean_architecture/generators/validation_config.py +5 -3
- fast_clean_architecture/generators/validation_metrics.py +10 -6
- fast_clean_architecture/health.py +169 -0
- fast_clean_architecture/logging_config.py +52 -0
- fast_clean_architecture/metrics.py +108 -0
- fast_clean_architecture/protocols.py +406 -0
- fast_clean_architecture/templates/external.py.j2 +109 -32
- fast_clean_architecture/utils.py +50 -31
- fast_clean_architecture/validation.py +302 -0
- {fast_clean_architecture-1.0.0.dist-info → fast_clean_architecture-1.1.2.dist-info}/METADATA +131 -64
- fast_clean_architecture-1.1.2.dist-info/RECORD +38 -0
- fast_clean_architecture-1.0.0.dist-info/RECORD +0 -30
- {fast_clean_architecture-1.0.0.dist-info → fast_clean_architecture-1.1.2.dist-info}/WHEEL +0 -0
- {fast_clean_architecture-1.0.0.dist-info → fast_clean_architecture-1.1.2.dist-info}/entry_points.txt +0 -0
- {fast_clean_architecture-1.0.0.dist-info → fast_clean_architecture-1.1.2.dist-info}/licenses/LICENSE +0 -0
@@ -1,63 +1,483 @@
|
|
1
1
|
"""Custom exceptions for Fast Clean Architecture."""
|
2
2
|
|
3
|
+
import time
|
4
|
+
import traceback
|
5
|
+
from pathlib import Path
|
6
|
+
from typing import Any, Callable, Dict, Generic, List, Optional, Set, TypeVar, Union
|
7
|
+
|
3
8
|
|
4
9
|
class FastCleanArchitectureError(Exception):
|
5
|
-
"""Base exception for all Fast Clean Architecture errors.
|
10
|
+
"""Base exception for all Fast Clean Architecture errors.
|
11
|
+
|
12
|
+
Enhanced with better context handling, error chaining, and debugging support.
|
13
|
+
"""
|
14
|
+
|
15
|
+
def __init__(
|
16
|
+
self,
|
17
|
+
message: str,
|
18
|
+
context: Optional[Dict[str, Any]] = None,
|
19
|
+
error_code: Optional[str] = None,
|
20
|
+
suggestions: Optional[List[str]] = None,
|
21
|
+
cause: Optional[Exception] = None,
|
22
|
+
):
|
23
|
+
super().__init__(message)
|
24
|
+
self.message = message
|
25
|
+
self.context = context or {}
|
26
|
+
self.error_code = error_code
|
27
|
+
self.suggestions = suggestions or []
|
28
|
+
self.timestamp = time.time()
|
29
|
+
self.cause = cause
|
30
|
+
|
31
|
+
# Add stack trace context for debugging
|
32
|
+
if cause:
|
33
|
+
self.__cause__ = cause
|
34
|
+
|
35
|
+
def add_context(self, key: str, value: Any) -> "FastCleanArchitectureError":
|
36
|
+
"""Add context information to the error."""
|
37
|
+
self.context[key] = value
|
38
|
+
return self
|
39
|
+
|
40
|
+
def add_suggestion(self, suggestion: str) -> "FastCleanArchitectureError":
|
41
|
+
"""Add a suggestion for resolving the error."""
|
42
|
+
self.suggestions.append(suggestion)
|
43
|
+
return self
|
44
|
+
|
45
|
+
def to_dict(self) -> Dict[str, Any]:
|
46
|
+
"""Convert error to dictionary for logging/serialization."""
|
47
|
+
return {
|
48
|
+
"error_type": self.__class__.__name__,
|
49
|
+
"message": self.message,
|
50
|
+
"error_code": self.error_code,
|
51
|
+
"context": self.context,
|
52
|
+
"suggestions": self.suggestions,
|
53
|
+
"timestamp": self.timestamp,
|
54
|
+
"cause": str(self.cause) if self.cause else None,
|
55
|
+
}
|
6
56
|
|
7
|
-
|
57
|
+
def __str__(self) -> str:
|
58
|
+
"""Enhanced string representation with context and suggestions."""
|
59
|
+
parts = [self.message]
|
60
|
+
|
61
|
+
if self.error_code:
|
62
|
+
parts.append(f"Error Code: {self.error_code}")
|
63
|
+
|
64
|
+
if self.context:
|
65
|
+
context_str = ", ".join(f"{k}={v}" for k, v in self.context.items())
|
66
|
+
parts.append(f"Context: {context_str}")
|
67
|
+
|
68
|
+
if self.suggestions:
|
69
|
+
suggestions_str = "; ".join(self.suggestions)
|
70
|
+
parts.append(f"Suggestions: {suggestions_str}")
|
71
|
+
|
72
|
+
return " | ".join(parts)
|
8
73
|
|
9
74
|
|
10
75
|
class ConfigurationError(FastCleanArchitectureError):
|
11
76
|
"""Raised when there's an issue with configuration."""
|
12
77
|
|
13
|
-
|
78
|
+
def __init__(
|
79
|
+
self,
|
80
|
+
message: str,
|
81
|
+
context: Optional[Dict[str, Any]] = None,
|
82
|
+
config_path: Optional[Path] = None,
|
83
|
+
cause: Optional[Exception] = None,
|
84
|
+
):
|
85
|
+
super().__init__(
|
86
|
+
message=message, context=context, error_code="CONFIG_ERROR", cause=cause
|
87
|
+
)
|
88
|
+
if config_path:
|
89
|
+
self.add_context("config_path", str(config_path))
|
90
|
+
self.add_suggestion(f"Check configuration file at: {config_path}")
|
14
91
|
|
15
92
|
|
16
93
|
class ValidationError(FastCleanArchitectureError):
|
17
94
|
"""Raised when validation fails."""
|
18
95
|
|
19
|
-
|
96
|
+
def __init__(
|
97
|
+
self,
|
98
|
+
message: str,
|
99
|
+
context: Optional[Dict[str, Any]] = None,
|
100
|
+
field_name: Optional[str] = None,
|
101
|
+
invalid_value: Optional[Any] = None,
|
102
|
+
cause: Optional[Exception] = None,
|
103
|
+
):
|
104
|
+
super().__init__(
|
105
|
+
message=message, context=context, error_code="VALIDATION_ERROR", cause=cause
|
106
|
+
)
|
107
|
+
if field_name:
|
108
|
+
self.add_context("field_name", field_name)
|
109
|
+
if invalid_value is not None:
|
110
|
+
self.add_context("invalid_value", str(invalid_value))
|
111
|
+
self.add_suggestion("Check the input value format and constraints")
|
112
|
+
|
113
|
+
|
114
|
+
class SecurityError(FastCleanArchitectureError):
|
115
|
+
"""Raised when security validation fails."""
|
116
|
+
|
117
|
+
def __init__(
|
118
|
+
self,
|
119
|
+
message: str,
|
120
|
+
context: Optional[Dict[str, Any]] = None,
|
121
|
+
security_check: Optional[str] = None,
|
122
|
+
cause: Optional[Exception] = None,
|
123
|
+
):
|
124
|
+
super().__init__(
|
125
|
+
message=message,
|
126
|
+
context=context,
|
127
|
+
error_code="SECURITY_ERROR",
|
128
|
+
suggestions=[
|
129
|
+
"Review input for potential security issues",
|
130
|
+
"Check file paths and user input validation",
|
131
|
+
],
|
132
|
+
cause=cause,
|
133
|
+
)
|
134
|
+
if security_check:
|
135
|
+
self.add_context("security_check", security_check)
|
20
136
|
|
21
137
|
|
22
138
|
class FileConflictError(FastCleanArchitectureError):
|
23
139
|
"""Raised when there's a file or directory conflict."""
|
24
140
|
|
25
|
-
|
141
|
+
def __init__(
|
142
|
+
self,
|
143
|
+
message: str,
|
144
|
+
context: Optional[Dict[str, Any]] = None,
|
145
|
+
file_path: Optional[Path] = None,
|
146
|
+
cause: Optional[Exception] = None,
|
147
|
+
):
|
148
|
+
super().__init__(
|
149
|
+
message=message,
|
150
|
+
context=context,
|
151
|
+
error_code="FILE_CONFLICT",
|
152
|
+
suggestions=[
|
153
|
+
"Use --force flag to overwrite",
|
154
|
+
"Choose a different location",
|
155
|
+
"Remove existing files first",
|
156
|
+
],
|
157
|
+
cause=cause,
|
158
|
+
)
|
159
|
+
if file_path:
|
160
|
+
self.add_context("file_path", str(file_path))
|
26
161
|
|
27
162
|
|
28
163
|
class TemplateError(FastCleanArchitectureError):
|
29
164
|
"""Raised when there's an issue with template rendering."""
|
30
165
|
|
31
|
-
|
166
|
+
def __init__(
|
167
|
+
self,
|
168
|
+
message: str,
|
169
|
+
context: Optional[Dict[str, Any]] = None,
|
170
|
+
template_name: Optional[str] = None,
|
171
|
+
cause: Optional[Exception] = None,
|
172
|
+
):
|
173
|
+
super().__init__(
|
174
|
+
message=message, context=context, error_code="TEMPLATE_ERROR", cause=cause
|
175
|
+
)
|
176
|
+
if template_name:
|
177
|
+
self.add_context("template_name", template_name)
|
178
|
+
self.add_suggestion(f"Check template file: {template_name}")
|
32
179
|
|
33
180
|
|
34
181
|
class TemplateValidationError(TemplateError):
|
35
182
|
"""Base class for template validation errors."""
|
36
183
|
|
37
|
-
|
184
|
+
def __init__(
|
185
|
+
self,
|
186
|
+
message: str,
|
187
|
+
context: Optional[Dict[str, Any]] = None,
|
188
|
+
template_name: Optional[str] = None,
|
189
|
+
cause: Optional[Exception] = None,
|
190
|
+
):
|
191
|
+
super().__init__(
|
192
|
+
message=message, context=context, template_name=template_name, cause=cause
|
193
|
+
)
|
194
|
+
self.error_code = "TEMPLATE_VALIDATION_ERROR"
|
38
195
|
|
39
196
|
|
40
197
|
class TemplateMissingVariablesError(TemplateValidationError):
|
41
198
|
"""Raised when required template variables are missing."""
|
42
199
|
|
43
|
-
def __init__(
|
200
|
+
def __init__(
|
201
|
+
self,
|
202
|
+
missing_vars: Set[str],
|
203
|
+
message: Optional[str] = None,
|
204
|
+
template_name: Optional[str] = None,
|
205
|
+
):
|
44
206
|
self.missing_vars = missing_vars
|
45
207
|
if message is None:
|
46
208
|
message = f"Missing required template variables: {', '.join(sorted(missing_vars))}"
|
47
|
-
|
209
|
+
|
210
|
+
super().__init__(message=message, template_name=template_name)
|
211
|
+
self.add_context("missing_variables", list(sorted(missing_vars)))
|
212
|
+
self.add_suggestion("Provide all required template variables")
|
213
|
+
self.error_code = "TEMPLATE_MISSING_VARS"
|
48
214
|
|
49
215
|
|
50
216
|
class TemplateUndefinedVariableError(TemplateValidationError):
|
51
217
|
"""Raised when template contains undefined variables during rendering."""
|
52
218
|
|
53
|
-
def __init__(
|
219
|
+
def __init__(
|
220
|
+
self,
|
221
|
+
variable_name: str,
|
222
|
+
message: Optional[str] = None,
|
223
|
+
template_name: Optional[str] = None,
|
224
|
+
):
|
54
225
|
self.variable_name = variable_name
|
55
226
|
if message is None:
|
56
227
|
message = f"Undefined template variable: {variable_name}"
|
57
|
-
|
228
|
+
|
229
|
+
super().__init__(message=message, template_name=template_name)
|
230
|
+
self.add_context("undefined_variable", variable_name)
|
231
|
+
self.add_suggestion(f"Define variable '{variable_name}' in template context")
|
232
|
+
self.error_code = "TEMPLATE_UNDEFINED_VAR"
|
58
233
|
|
59
234
|
|
60
235
|
class ComponentError(FastCleanArchitectureError):
|
61
236
|
"""Raised when there's an issue with component generation."""
|
62
237
|
|
63
|
-
|
238
|
+
def __init__(
|
239
|
+
self,
|
240
|
+
message: str,
|
241
|
+
context: Optional[Dict[str, Any]] = None,
|
242
|
+
component_name: Optional[str] = None,
|
243
|
+
component_type: Optional[str] = None,
|
244
|
+
cause: Optional[Exception] = None,
|
245
|
+
):
|
246
|
+
super().__init__(
|
247
|
+
message=message, context=context, error_code="COMPONENT_ERROR", cause=cause
|
248
|
+
)
|
249
|
+
if component_name:
|
250
|
+
self.add_context("component_name", component_name)
|
251
|
+
if component_type:
|
252
|
+
self.add_context("component_type", component_type)
|
253
|
+
self.add_suggestion(f"Check {component_type} component requirements")
|
254
|
+
|
255
|
+
|
256
|
+
# Error Handling Utilities
|
257
|
+
def create_secure_error(
|
258
|
+
error_type: str, operation: str, details: Optional[str] = None
|
259
|
+
) -> SecurityError:
|
260
|
+
"""Create a SecurityError with standardized context."""
|
261
|
+
error = SecurityError(
|
262
|
+
f"Security violation during {operation}: {details or error_type}"
|
263
|
+
)
|
264
|
+
error.add_context("error_type", error_type).add_context("operation", operation)
|
265
|
+
return error
|
266
|
+
|
267
|
+
|
268
|
+
def create_validation_error(
|
269
|
+
field: str, value: Any, reason: str, suggestions: Optional[List[str]] = None
|
270
|
+
) -> ValidationError:
|
271
|
+
"""Create a validation error with standardized format."""
|
272
|
+
message = f"Validation failed for {field}: {reason}"
|
273
|
+
error = ValidationError(message=message, field_name=field, invalid_value=value)
|
274
|
+
if suggestions:
|
275
|
+
for suggestion in suggestions:
|
276
|
+
error.add_suggestion(suggestion)
|
277
|
+
return error
|
278
|
+
|
279
|
+
|
280
|
+
def create_config_error(
|
281
|
+
operation: str,
|
282
|
+
details: str,
|
283
|
+
config_path: Optional[Path] = None,
|
284
|
+
cause: Optional[Exception] = None,
|
285
|
+
) -> ConfigurationError:
|
286
|
+
"""Create a configuration error with standardized format."""
|
287
|
+
message = f"Configuration error during {operation}: {details}"
|
288
|
+
return ConfigurationError(
|
289
|
+
message=message,
|
290
|
+
config_path=config_path,
|
291
|
+
cause=cause,
|
292
|
+
context={"operation": operation},
|
293
|
+
)
|
294
|
+
|
295
|
+
|
296
|
+
class ErrorContext:
|
297
|
+
"""Context manager for enhanced error handling."""
|
298
|
+
|
299
|
+
def __init__(self, operation: str, **context: Any) -> None:
|
300
|
+
self.operation = operation
|
301
|
+
self.context = context
|
302
|
+
|
303
|
+
def __enter__(self) -> "ErrorContext":
|
304
|
+
return self
|
305
|
+
|
306
|
+
def __exit__(
|
307
|
+
self,
|
308
|
+
exc_type: Optional[type],
|
309
|
+
exc_val: Optional[Exception],
|
310
|
+
exc_tb: Optional[Any],
|
311
|
+
) -> Optional[bool]:
|
312
|
+
if exc_type and issubclass(exc_type, FastCleanArchitectureError):
|
313
|
+
# Enhance existing FCA errors with context
|
314
|
+
if exc_val and isinstance(exc_val, FastCleanArchitectureError):
|
315
|
+
exc_val.add_context("operation", self.operation)
|
316
|
+
for key, value in self.context.items():
|
317
|
+
exc_val.add_context(key, value)
|
318
|
+
elif exc_type and exc_type != FastCleanArchitectureError:
|
319
|
+
# Wrap other exceptions in FCA error
|
320
|
+
enhanced_error = FastCleanArchitectureError(
|
321
|
+
message=f"Error during {self.operation}: {str(exc_val)}",
|
322
|
+
context=self.context,
|
323
|
+
cause=exc_val,
|
324
|
+
)
|
325
|
+
raise enhanced_error from exc_val
|
326
|
+
return None
|
327
|
+
|
328
|
+
|
329
|
+
# Result Pattern for Better Error Handling
|
330
|
+
T = TypeVar("T")
|
331
|
+
E = TypeVar("E", bound=Exception)
|
332
|
+
|
333
|
+
|
334
|
+
_SENTINEL = object() # Module-level sentinel to detect when no value is provided
|
335
|
+
|
336
|
+
|
337
|
+
class Result(Generic[T, E]):
|
338
|
+
"""Result type for better error handling without exceptions."""
|
339
|
+
|
340
|
+
def __init__(self, value: Union[T, object] = _SENTINEL, error: Optional[E] = None):
|
341
|
+
# Check if both value and error are explicitly provided
|
342
|
+
if value is not _SENTINEL and error is not None:
|
343
|
+
raise ValueError("Result cannot have both value and error")
|
344
|
+
# Check if neither value nor error are provided
|
345
|
+
if value is _SENTINEL and error is None:
|
346
|
+
raise ValueError("Result must have either value or error")
|
347
|
+
|
348
|
+
# Set the actual values
|
349
|
+
self._value: Optional[T] = value if value is not _SENTINEL else None # type: ignore
|
350
|
+
self._error = error
|
351
|
+
|
352
|
+
@classmethod
|
353
|
+
def success(cls, value: T) -> "Result[T, E]":
|
354
|
+
"""Create a successful result."""
|
355
|
+
return cls(value=value)
|
356
|
+
|
357
|
+
@classmethod
|
358
|
+
def failure(cls, error: E) -> "Result[T, E]":
|
359
|
+
"""Create a failed result."""
|
360
|
+
return cls(error=error)
|
361
|
+
|
362
|
+
@property
|
363
|
+
def is_success(self) -> bool:
|
364
|
+
"""Check if result is successful."""
|
365
|
+
return self._error is None
|
366
|
+
|
367
|
+
@property
|
368
|
+
def is_failure(self) -> bool:
|
369
|
+
"""Check if result is a failure."""
|
370
|
+
return self._error is not None
|
371
|
+
|
372
|
+
def unwrap(self) -> T:
|
373
|
+
"""Get the value, raising the error if failed."""
|
374
|
+
if self._error is not None:
|
375
|
+
raise self._error
|
376
|
+
return self._value # type: ignore
|
377
|
+
|
378
|
+
def unwrap_or(self, default: T) -> T:
|
379
|
+
"""Get the value or return default if failed."""
|
380
|
+
return self._value if self._error is None else default # type: ignore
|
381
|
+
|
382
|
+
def map(self, func: Callable[[T], Any]) -> "Result[Any, Exception]":
|
383
|
+
"""Transform the value if successful."""
|
384
|
+
if self._error is not None:
|
385
|
+
return Result.failure(self._error)
|
386
|
+
try:
|
387
|
+
return Result.success(func(self._value)) # type: ignore
|
388
|
+
except Exception as e:
|
389
|
+
return Result.failure(e)
|
390
|
+
|
391
|
+
def and_then(self, func: Callable[[T], "Result[Any, E]"]) -> "Result[Any, E]":
|
392
|
+
"""Chain operations that return Results."""
|
393
|
+
if self._error is not None:
|
394
|
+
return Result.failure(self._error)
|
395
|
+
return func(self._value) # type: ignore
|
396
|
+
|
397
|
+
@property
|
398
|
+
def error(self) -> Optional[E]:
|
399
|
+
"""Get the error if any."""
|
400
|
+
return self._error
|
401
|
+
|
402
|
+
@property
|
403
|
+
def value(self) -> Optional[T]:
|
404
|
+
"""Get the value if successful."""
|
405
|
+
return self._value
|
406
|
+
|
407
|
+
def map_error(self, func: Callable[[E], Any]) -> "Result[T, Exception]":
|
408
|
+
"""Transform the error if failed."""
|
409
|
+
if self._error is None:
|
410
|
+
return Result.success(self._value) # type: ignore
|
411
|
+
try:
|
412
|
+
new_error = func(self._error)
|
413
|
+
if isinstance(new_error, Exception):
|
414
|
+
return Result.failure(new_error)
|
415
|
+
else:
|
416
|
+
return Result.failure(Exception(str(new_error)))
|
417
|
+
except Exception as e:
|
418
|
+
return Result.failure(e)
|
419
|
+
|
420
|
+
def or_else(self, func: Callable[[E], "Result[T, Any]"]) -> "Result[T, Any]":
|
421
|
+
"""Provide alternative result if failed."""
|
422
|
+
if self._error is None:
|
423
|
+
return Result.success(self._value) # type: ignore
|
424
|
+
return func(self._error)
|
425
|
+
|
426
|
+
def inspect(self, func: Callable[[T], None]) -> "Result[T, E]":
|
427
|
+
"""Inspect the value without changing the result."""
|
428
|
+
if self._error is None and self._value is not None:
|
429
|
+
func(self._value)
|
430
|
+
return self
|
431
|
+
|
432
|
+
def inspect_error(self, func: Callable[[E], None]) -> "Result[T, E]":
|
433
|
+
"""Inspect the error without changing the result."""
|
434
|
+
if self._error is not None:
|
435
|
+
func(self._error)
|
436
|
+
return self
|
437
|
+
|
438
|
+
def to_dict(self) -> Dict[str, Any]:
|
439
|
+
"""Convert result to dictionary for serialization."""
|
440
|
+
if self.is_success:
|
441
|
+
return {"success": True, "value": self._value, "error": None}
|
442
|
+
else:
|
443
|
+
error_dict = None
|
444
|
+
if isinstance(self._error, FastCleanArchitectureError):
|
445
|
+
error_dict = self._error.to_dict()
|
446
|
+
else:
|
447
|
+
error_dict = {
|
448
|
+
"error_type": self._error.__class__.__name__,
|
449
|
+
"message": str(self._error),
|
450
|
+
}
|
451
|
+
|
452
|
+
return {"success": False, "value": None, "error": error_dict}
|
453
|
+
|
454
|
+
|
455
|
+
# Utility functions for Result pattern
|
456
|
+
def safe_execute(func: Callable[[], T]) -> Result[T, Exception]:
|
457
|
+
"""Safely execute a function and return a Result."""
|
458
|
+
try:
|
459
|
+
result = func()
|
460
|
+
return Result.success(result)
|
461
|
+
except Exception as e:
|
462
|
+
return Result.failure(e)
|
463
|
+
|
464
|
+
|
465
|
+
def combine_results(results: List[Result[T, Exception]]) -> Result[List[T], Exception]:
|
466
|
+
"""Combine multiple results into a single result with a list of values."""
|
467
|
+
values: List[T] = []
|
468
|
+
for result in results:
|
469
|
+
if result.is_failure:
|
470
|
+
return Result.failure(result._error) # type: ignore
|
471
|
+
if result._value is not None:
|
472
|
+
values.append(result._value)
|
473
|
+
return Result.success(values)
|
474
|
+
|
475
|
+
|
476
|
+
def first_success(*results: Result[T, Exception]) -> Result[T, Exception]:
|
477
|
+
"""Return the first successful result, or the last error if all fail."""
|
478
|
+
last_error = None
|
479
|
+
for result in results:
|
480
|
+
if result.is_success:
|
481
|
+
return result
|
482
|
+
last_error = result.error
|
483
|
+
return Result.failure(last_error or Exception("All results failed"))
|
@@ -1,11 +1,21 @@
|
|
1
1
|
"""Code generators for Fast Clean Architecture."""
|
2
2
|
|
3
|
-
from
|
3
|
+
from ..protocols import ComponentGeneratorProtocol
|
4
4
|
from .component_generator import ComponentGenerator
|
5
5
|
from .config_updater import ConfigUpdater
|
6
|
+
from .generator_factory import (
|
7
|
+
DependencyContainer,
|
8
|
+
GeneratorFactory,
|
9
|
+
create_generator_factory,
|
10
|
+
)
|
11
|
+
from .package_generator import PackageGenerator
|
6
12
|
|
7
13
|
__all__ = [
|
8
14
|
"PackageGenerator",
|
9
15
|
"ComponentGenerator",
|
10
16
|
"ConfigUpdater",
|
17
|
+
"GeneratorFactory",
|
18
|
+
"DependencyContainer",
|
19
|
+
"create_generator_factory",
|
20
|
+
"ComponentGeneratorProtocol",
|
11
21
|
]
|