grimoire-model 0.2.0__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.
- grimoire_model/__init__.py +260 -0
- grimoire_model/core/__init__.py +1 -0
- grimoire_model/core/exceptions.py +287 -0
- grimoire_model/core/model.py +473 -0
- grimoire_model/core/registry.py +380 -0
- grimoire_model/core/schema.py +367 -0
- grimoire_model/logging.py +39 -0
- grimoire_model/resolvers/__init__.py +1 -0
- grimoire_model/resolvers/derived.py +528 -0
- grimoire_model/resolvers/template.py +402 -0
- grimoire_model/utils/__init__.py +1 -0
- grimoire_model/utils/inheritance.py +446 -0
- grimoire_model/utils/paths.py +332 -0
- grimoire_model/validation/__init__.py +1 -0
- grimoire_model/validation/validators.py +440 -0
- grimoire_model-0.2.0.dist-info/METADATA +647 -0
- grimoire_model-0.2.0.dist-info/RECORD +20 -0
- grimoire_model-0.2.0.dist-info/WHEEL +5 -0
- grimoire_model-0.2.0.dist-info/licenses/LICENSE +21 -0
- grimoire_model-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Grimoire Model Package
|
|
3
|
+
|
|
4
|
+
A dict-like model system with schema validation, derived fields, and inheritance
|
|
5
|
+
designed for integration with grimoire-context.
|
|
6
|
+
|
|
7
|
+
Key Features:
|
|
8
|
+
- Dict-like interface (MutableMapping)
|
|
9
|
+
- Schema validation with Pydantic
|
|
10
|
+
- Reactive derived fields with dependency tracking
|
|
11
|
+
- Model inheritance support
|
|
12
|
+
- Template-based field expressions using Jinja2
|
|
13
|
+
- Immutable operations with pyrsistent
|
|
14
|
+
- Dependency injection for extensibility
|
|
15
|
+
|
|
16
|
+
Example Usage:
|
|
17
|
+
from grimoire_model import GrimoireModel, ModelDefinition, create_model
|
|
18
|
+
|
|
19
|
+
# Define model schema
|
|
20
|
+
character_def = ModelDefinition(
|
|
21
|
+
id="character",
|
|
22
|
+
name="Player Character",
|
|
23
|
+
attributes={
|
|
24
|
+
"name": {"type": "str", "required": True},
|
|
25
|
+
"level": {"type": "int", "default": 1},
|
|
26
|
+
"hp": {"type": "int", "default": 8},
|
|
27
|
+
"max_hp": {"type": "int", "derived": "{{ level * 8 }}"}
|
|
28
|
+
}
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
# Create model instance
|
|
32
|
+
character = create_model(character_def, {"name": "Aragorn", "level": 5})
|
|
33
|
+
|
|
34
|
+
# Use as dict
|
|
35
|
+
character['level'] = 6 # Automatically updates max_hp derived field
|
|
36
|
+
print(character['max_hp']) # 48
|
|
37
|
+
|
|
38
|
+
# Works with grimoire-context
|
|
39
|
+
from grimoire_context import GrimoireContext
|
|
40
|
+
context = GrimoireContext({'character': character})
|
|
41
|
+
context.set_variable('character.level', 7)
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
__version__ = "0.2.0"
|
|
45
|
+
__author__ = "The Wyrd One"
|
|
46
|
+
__email__ = "wyrdbound@proton.me"
|
|
47
|
+
|
|
48
|
+
# Core exports
|
|
49
|
+
from .core.exceptions import (
|
|
50
|
+
ConfigurationError,
|
|
51
|
+
DependencyError,
|
|
52
|
+
GrimoireModelError,
|
|
53
|
+
InheritanceError,
|
|
54
|
+
ModelValidationError,
|
|
55
|
+
TemplateResolutionError,
|
|
56
|
+
)
|
|
57
|
+
from .core.model import (
|
|
58
|
+
GrimoireModel,
|
|
59
|
+
create_model,
|
|
60
|
+
)
|
|
61
|
+
from .core.registry import (
|
|
62
|
+
ModelRegistry,
|
|
63
|
+
clear_registry,
|
|
64
|
+
get_default_registry,
|
|
65
|
+
get_model,
|
|
66
|
+
get_model_registry, # Kept for backward compatibility
|
|
67
|
+
register_model,
|
|
68
|
+
)
|
|
69
|
+
from .core.schema import (
|
|
70
|
+
AttributeDefinition,
|
|
71
|
+
ModelDefinition,
|
|
72
|
+
ValidationRule,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
# Logging configuration
|
|
76
|
+
from .logging import clear_logger_injection, get_logger, inject_logger, logger
|
|
77
|
+
from .resolvers.derived import (
|
|
78
|
+
BatchedDerivedFieldResolver,
|
|
79
|
+
DependencyInfo,
|
|
80
|
+
DerivedFieldResolver,
|
|
81
|
+
ObservableValue,
|
|
82
|
+
create_derived_field_resolver,
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# Resolver exports
|
|
86
|
+
from .resolvers.template import (
|
|
87
|
+
CachingTemplateResolver,
|
|
88
|
+
Jinja2TemplateResolver,
|
|
89
|
+
ModelContextTemplateResolver,
|
|
90
|
+
TemplateResolver,
|
|
91
|
+
create_template_resolver,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
# Utility exports
|
|
95
|
+
from .utils.inheritance import resolve_model_inheritance
|
|
96
|
+
from .utils.paths import (
|
|
97
|
+
delete_nested_value,
|
|
98
|
+
flatten_dict,
|
|
99
|
+
get_nested_value,
|
|
100
|
+
has_nested_value,
|
|
101
|
+
set_nested_value,
|
|
102
|
+
unflatten_dict,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
# Validation exports
|
|
106
|
+
from .validation.validators import (
|
|
107
|
+
EnumValidator,
|
|
108
|
+
LengthValidator,
|
|
109
|
+
PatternValidator,
|
|
110
|
+
RangeValidator,
|
|
111
|
+
RequiredValidator,
|
|
112
|
+
TypeValidator,
|
|
113
|
+
ValidationEngine,
|
|
114
|
+
get_validation_engine,
|
|
115
|
+
validate_field_value,
|
|
116
|
+
validate_model_data,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
__all__ = [
|
|
120
|
+
# Core classes
|
|
121
|
+
"GrimoireModel",
|
|
122
|
+
"ModelDefinition",
|
|
123
|
+
"AttributeDefinition",
|
|
124
|
+
"ValidationRule",
|
|
125
|
+
# Factory functions
|
|
126
|
+
"create_model",
|
|
127
|
+
"create_template_resolver",
|
|
128
|
+
"create_derived_field_resolver",
|
|
129
|
+
# Registry
|
|
130
|
+
"ModelRegistry",
|
|
131
|
+
"get_default_registry",
|
|
132
|
+
"get_model_registry", # Backward compatibility
|
|
133
|
+
"clear_registry",
|
|
134
|
+
"register_model",
|
|
135
|
+
"get_model",
|
|
136
|
+
# Logging
|
|
137
|
+
"logger",
|
|
138
|
+
"get_logger",
|
|
139
|
+
"inject_logger",
|
|
140
|
+
"clear_logger_injection",
|
|
141
|
+
# Exceptions
|
|
142
|
+
"GrimoireModelError",
|
|
143
|
+
"ModelValidationError",
|
|
144
|
+
"TemplateResolutionError",
|
|
145
|
+
"InheritanceError",
|
|
146
|
+
"DependencyError",
|
|
147
|
+
"ConfigurationError",
|
|
148
|
+
# Resolvers
|
|
149
|
+
"TemplateResolver",
|
|
150
|
+
"Jinja2TemplateResolver",
|
|
151
|
+
"ModelContextTemplateResolver",
|
|
152
|
+
"CachingTemplateResolver",
|
|
153
|
+
"DerivedFieldResolver",
|
|
154
|
+
"BatchedDerivedFieldResolver",
|
|
155
|
+
"ObservableValue",
|
|
156
|
+
"DependencyInfo",
|
|
157
|
+
# Utilities
|
|
158
|
+
"resolve_model_inheritance",
|
|
159
|
+
"get_nested_value",
|
|
160
|
+
"set_nested_value",
|
|
161
|
+
"has_nested_value",
|
|
162
|
+
"delete_nested_value",
|
|
163
|
+
"flatten_dict",
|
|
164
|
+
"unflatten_dict",
|
|
165
|
+
# Validators
|
|
166
|
+
"TypeValidator",
|
|
167
|
+
"RangeValidator",
|
|
168
|
+
"EnumValidator",
|
|
169
|
+
"RequiredValidator",
|
|
170
|
+
"PatternValidator",
|
|
171
|
+
"LengthValidator",
|
|
172
|
+
"ValidationEngine",
|
|
173
|
+
"validate_field_value",
|
|
174
|
+
"validate_model_data",
|
|
175
|
+
"get_validation_engine",
|
|
176
|
+
]
|
|
177
|
+
|
|
178
|
+
# Package metadata
|
|
179
|
+
__meta__ = {
|
|
180
|
+
"name": "grimoire-model",
|
|
181
|
+
"version": __version__,
|
|
182
|
+
"description": (
|
|
183
|
+
"Dict-like model system with validation and derived fields for Grimoire"
|
|
184
|
+
),
|
|
185
|
+
"long_description": __doc__,
|
|
186
|
+
"author": __author__,
|
|
187
|
+
"author_email": __email__,
|
|
188
|
+
"license": "MIT",
|
|
189
|
+
"url": "https://github.com/wyrdbound/grimoire-model",
|
|
190
|
+
"classifiers": [
|
|
191
|
+
"Development Status :: 3 - Alpha",
|
|
192
|
+
"Intended Audience :: Developers",
|
|
193
|
+
"License :: OSI Approved :: MIT License",
|
|
194
|
+
"Programming Language :: Python :: 3",
|
|
195
|
+
"Programming Language :: Python :: 3.8",
|
|
196
|
+
"Programming Language :: Python :: 3.9",
|
|
197
|
+
"Programming Language :: Python :: 3.10",
|
|
198
|
+
"Programming Language :: Python :: 3.11",
|
|
199
|
+
"Programming Language :: Python :: 3.12",
|
|
200
|
+
"Topic :: Games/Entertainment :: Role-Playing",
|
|
201
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
202
|
+
],
|
|
203
|
+
"keywords": "gaming rpg tabletop model validation schema",
|
|
204
|
+
"python_requires": ">=3.8",
|
|
205
|
+
"install_requires": [
|
|
206
|
+
"pydantic>=2.0.0",
|
|
207
|
+
"pyrsistent>=0.19.0",
|
|
208
|
+
"jinja2>=3.1.0",
|
|
209
|
+
"pyyaml>=6.0",
|
|
210
|
+
],
|
|
211
|
+
"extras_require": {
|
|
212
|
+
"dev": [
|
|
213
|
+
"pytest>=7.0.0",
|
|
214
|
+
"pytest-cov>=4.0.0",
|
|
215
|
+
"ruff>=0.1.0",
|
|
216
|
+
"mypy>=1.0.0",
|
|
217
|
+
"black>=22.0.0",
|
|
218
|
+
],
|
|
219
|
+
"docs": [
|
|
220
|
+
"sphinx>=4.0.0",
|
|
221
|
+
"sphinx-rtd-theme>=1.0.0",
|
|
222
|
+
"sphinxcontrib-napoleon>=0.7",
|
|
223
|
+
],
|
|
224
|
+
"test": [
|
|
225
|
+
"pytest>=7.0.0",
|
|
226
|
+
"pytest-cov>=4.0.0",
|
|
227
|
+
"pytest-mock>=3.0.0",
|
|
228
|
+
"hypothesis>=6.0.0",
|
|
229
|
+
],
|
|
230
|
+
},
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
# Integration helpers for grimoire-context
|
|
235
|
+
def register_with_grimoire_context():
|
|
236
|
+
"""Register GrimoireModel as a compatible value type with grimoire-context.
|
|
237
|
+
|
|
238
|
+
This function should be called if you want seamless integration between
|
|
239
|
+
grimoire-model and grimoire-context packages.
|
|
240
|
+
"""
|
|
241
|
+
try:
|
|
242
|
+
from grimoire_context import GrimoireContext # type: ignore[import-not-found]
|
|
243
|
+
|
|
244
|
+
# Register our model as a compatible dict-like type
|
|
245
|
+
if hasattr(GrimoireContext, "register_dict_like_type"):
|
|
246
|
+
GrimoireContext.register_dict_like_type(GrimoireModel) # type: ignore
|
|
247
|
+
|
|
248
|
+
return True
|
|
249
|
+
except ImportError:
|
|
250
|
+
# grimoire-context not available
|
|
251
|
+
return False
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
# Optional auto-registration
|
|
255
|
+
try:
|
|
256
|
+
# Try to register automatically if grimoire-context is available
|
|
257
|
+
register_with_grimoire_context()
|
|
258
|
+
except Exception:
|
|
259
|
+
# Silently ignore registration failures
|
|
260
|
+
pass
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Core components of grimoire-model package."""
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Core exceptions for grimoire-model package.
|
|
3
|
+
|
|
4
|
+
Provides a hierarchy of exceptions for different error conditions that can occur
|
|
5
|
+
during model operations, validation, template resolution, and inheritance processing.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any, Dict, List, Optional
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class GrimoireModelError(Exception):
|
|
12
|
+
"""Base exception for grimoire-model package.
|
|
13
|
+
|
|
14
|
+
All other exceptions in this package inherit from this base class.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(self, message: str, context: Optional[Dict[str, Any]] = None):
|
|
18
|
+
"""Initialize with message and optional context information.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
message: Human-readable error description
|
|
22
|
+
context: Additional context information for debugging
|
|
23
|
+
"""
|
|
24
|
+
super().__init__(message)
|
|
25
|
+
self.message = message
|
|
26
|
+
self.context = context or {}
|
|
27
|
+
|
|
28
|
+
def __str__(self) -> str:
|
|
29
|
+
"""Return string representation with context if available."""
|
|
30
|
+
if self.context:
|
|
31
|
+
context_str = ", ".join(f"{k}={v}" for k, v in self.context.items())
|
|
32
|
+
return f"{self.message} (context: {context_str})"
|
|
33
|
+
return self.message
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class ModelValidationError(GrimoireModelError):
|
|
37
|
+
"""Raised when model validation fails.
|
|
38
|
+
|
|
39
|
+
This exception is raised when:
|
|
40
|
+
- Required fields are missing
|
|
41
|
+
- Field values don't match their defined types
|
|
42
|
+
- Value constraints are violated (range, enum, etc.)
|
|
43
|
+
- Cross-field validation rules fail
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(
|
|
47
|
+
self,
|
|
48
|
+
message: str,
|
|
49
|
+
field_name: Optional[str] = None,
|
|
50
|
+
field_value: Any = None,
|
|
51
|
+
validation_errors: Optional[List[str]] = None,
|
|
52
|
+
context: Optional[Dict[str, Any]] = None,
|
|
53
|
+
):
|
|
54
|
+
"""Initialize validation error with field-specific information.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
message: Primary error message
|
|
58
|
+
field_name: Name of the field that failed validation
|
|
59
|
+
field_value: Value that caused the validation failure
|
|
60
|
+
validation_errors: List of specific validation error messages
|
|
61
|
+
context: Additional context information
|
|
62
|
+
"""
|
|
63
|
+
super().__init__(message, context)
|
|
64
|
+
self.field_name = field_name
|
|
65
|
+
self.field_value = field_value
|
|
66
|
+
self.validation_errors = validation_errors or []
|
|
67
|
+
|
|
68
|
+
def __str__(self) -> str:
|
|
69
|
+
"""Return detailed string representation including field information."""
|
|
70
|
+
parts = [self.message]
|
|
71
|
+
|
|
72
|
+
if self.field_name:
|
|
73
|
+
parts.append(f"field: {self.field_name}")
|
|
74
|
+
|
|
75
|
+
if self.field_value is not None:
|
|
76
|
+
parts.append(f"value: {self.field_value}")
|
|
77
|
+
|
|
78
|
+
if self.validation_errors:
|
|
79
|
+
error_list = ", ".join(self.validation_errors)
|
|
80
|
+
parts.append(f"errors: [{error_list}]")
|
|
81
|
+
|
|
82
|
+
if self.context:
|
|
83
|
+
context_str = ", ".join(f"{k}={v}" for k, v in self.context.items())
|
|
84
|
+
parts.append(f"context: {context_str}")
|
|
85
|
+
|
|
86
|
+
return " | ".join(parts)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class TemplateResolutionError(GrimoireModelError):
|
|
90
|
+
"""Raised when template resolution fails.
|
|
91
|
+
|
|
92
|
+
This exception is raised when:
|
|
93
|
+
- Template syntax is invalid
|
|
94
|
+
- Referenced variables are undefined
|
|
95
|
+
- Template evaluation produces an error
|
|
96
|
+
- Circular template dependencies are detected
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
def __init__(
|
|
100
|
+
self,
|
|
101
|
+
message: str,
|
|
102
|
+
template_str: Optional[str] = None,
|
|
103
|
+
template_variables: Optional[List[str]] = None,
|
|
104
|
+
context: Optional[Dict[str, Any]] = None,
|
|
105
|
+
):
|
|
106
|
+
"""Initialize template resolution error.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
message: Primary error message
|
|
110
|
+
template_str: The template string that failed to resolve
|
|
111
|
+
template_variables: Variables referenced in the template
|
|
112
|
+
context: Additional context information
|
|
113
|
+
"""
|
|
114
|
+
super().__init__(message, context)
|
|
115
|
+
self.template_str = template_str
|
|
116
|
+
self.template_variables = template_variables or []
|
|
117
|
+
|
|
118
|
+
def __str__(self) -> str:
|
|
119
|
+
"""Return detailed string representation including template information."""
|
|
120
|
+
parts = [self.message]
|
|
121
|
+
|
|
122
|
+
if self.template_str:
|
|
123
|
+
parts.append(f"template: {self.template_str}")
|
|
124
|
+
|
|
125
|
+
if self.template_variables:
|
|
126
|
+
vars_str = ", ".join(self.template_variables)
|
|
127
|
+
parts.append(f"variables: [{vars_str}]")
|
|
128
|
+
|
|
129
|
+
if self.context:
|
|
130
|
+
context_str = ", ".join(f"{k}={v}" for k, v in self.context.items())
|
|
131
|
+
parts.append(f"context: {context_str}")
|
|
132
|
+
|
|
133
|
+
return " | ".join(parts)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class InheritanceError(GrimoireModelError):
|
|
137
|
+
"""Raised when model inheritance resolution fails.
|
|
138
|
+
|
|
139
|
+
This exception is raised when:
|
|
140
|
+
- Parent model definitions are not found
|
|
141
|
+
- Circular inheritance dependencies are detected
|
|
142
|
+
- Inheritance conflicts cannot be resolved
|
|
143
|
+
- Model registry is incomplete or corrupted
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
def __init__(
|
|
147
|
+
self,
|
|
148
|
+
message: str,
|
|
149
|
+
model_id: Optional[str] = None,
|
|
150
|
+
parent_ids: Optional[List[str]] = None,
|
|
151
|
+
inheritance_chain: Optional[List[str]] = None,
|
|
152
|
+
context: Optional[Dict[str, Any]] = None,
|
|
153
|
+
):
|
|
154
|
+
"""Initialize inheritance error.
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
message: Primary error message
|
|
158
|
+
model_id: ID of the model that failed inheritance resolution
|
|
159
|
+
parent_ids: List of parent model IDs
|
|
160
|
+
inheritance_chain: The inheritance chain being resolved
|
|
161
|
+
context: Additional context information
|
|
162
|
+
"""
|
|
163
|
+
super().__init__(message, context)
|
|
164
|
+
self.model_id = model_id
|
|
165
|
+
self.parent_ids = parent_ids or []
|
|
166
|
+
self.inheritance_chain = inheritance_chain or []
|
|
167
|
+
|
|
168
|
+
def __str__(self) -> str:
|
|
169
|
+
"""Return detailed string representation including inheritance information."""
|
|
170
|
+
parts = [self.message]
|
|
171
|
+
|
|
172
|
+
if self.model_id:
|
|
173
|
+
parts.append(f"model: {self.model_id}")
|
|
174
|
+
|
|
175
|
+
if self.parent_ids:
|
|
176
|
+
parents_str = ", ".join(self.parent_ids)
|
|
177
|
+
parts.append(f"parents: [{parents_str}]")
|
|
178
|
+
|
|
179
|
+
if self.inheritance_chain:
|
|
180
|
+
chain_str = " -> ".join(self.inheritance_chain)
|
|
181
|
+
parts.append(f"chain: {chain_str}")
|
|
182
|
+
|
|
183
|
+
if self.context:
|
|
184
|
+
context_str = ", ".join(f"{k}={v}" for k, v in self.context.items())
|
|
185
|
+
parts.append(f"context: {context_str}")
|
|
186
|
+
|
|
187
|
+
return " | ".join(parts)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
class DependencyError(GrimoireModelError):
|
|
191
|
+
"""Raised when derived field dependencies cannot be resolved.
|
|
192
|
+
|
|
193
|
+
This exception is raised when:
|
|
194
|
+
- Circular dependencies are detected between derived fields
|
|
195
|
+
- Required dependencies are missing or undefined
|
|
196
|
+
- Dependency graph cannot be topologically sorted
|
|
197
|
+
- Dependency computation fails
|
|
198
|
+
"""
|
|
199
|
+
|
|
200
|
+
def __init__(
|
|
201
|
+
self,
|
|
202
|
+
message: str,
|
|
203
|
+
field_name: Optional[str] = None,
|
|
204
|
+
dependencies: Optional[List[str]] = None,
|
|
205
|
+
dependency_chain: Optional[List[str]] = None,
|
|
206
|
+
context: Optional[Dict[str, Any]] = None,
|
|
207
|
+
):
|
|
208
|
+
"""Initialize dependency error.
|
|
209
|
+
|
|
210
|
+
Args:
|
|
211
|
+
message: Primary error message
|
|
212
|
+
field_name: Name of the field with dependency issues
|
|
213
|
+
dependencies: List of field dependencies
|
|
214
|
+
dependency_chain: The dependency chain that failed
|
|
215
|
+
context: Additional context information
|
|
216
|
+
"""
|
|
217
|
+
super().__init__(message, context)
|
|
218
|
+
self.field_name = field_name
|
|
219
|
+
self.dependencies = dependencies or []
|
|
220
|
+
self.dependency_chain = dependency_chain or []
|
|
221
|
+
|
|
222
|
+
def __str__(self) -> str:
|
|
223
|
+
"""Return detailed string representation including dependency information."""
|
|
224
|
+
parts = [self.message]
|
|
225
|
+
|
|
226
|
+
if self.field_name:
|
|
227
|
+
parts.append(f"field: {self.field_name}")
|
|
228
|
+
|
|
229
|
+
if self.dependencies:
|
|
230
|
+
deps_str = ", ".join(self.dependencies)
|
|
231
|
+
parts.append(f"deps: [{deps_str}]")
|
|
232
|
+
|
|
233
|
+
if self.dependency_chain:
|
|
234
|
+
chain_str = " -> ".join(self.dependency_chain)
|
|
235
|
+
parts.append(f"chain: {chain_str}")
|
|
236
|
+
|
|
237
|
+
if self.context:
|
|
238
|
+
context_str = ", ".join(f"{k}={v}" for k, v in self.context.items())
|
|
239
|
+
parts.append(f"context: {context_str}")
|
|
240
|
+
|
|
241
|
+
return " | ".join(parts)
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
class ConfigurationError(GrimoireModelError):
|
|
245
|
+
"""Raised when configuration is invalid or incomplete.
|
|
246
|
+
|
|
247
|
+
This exception is raised when:
|
|
248
|
+
- Required configuration parameters are missing
|
|
249
|
+
- Configuration values are invalid
|
|
250
|
+
- Dependency injection setup fails
|
|
251
|
+
- Resolver configuration is incomplete
|
|
252
|
+
"""
|
|
253
|
+
|
|
254
|
+
def __init__(
|
|
255
|
+
self,
|
|
256
|
+
message: str,
|
|
257
|
+
config_key: Optional[str] = None,
|
|
258
|
+
config_value: Any = None,
|
|
259
|
+
context: Optional[Dict[str, Any]] = None,
|
|
260
|
+
):
|
|
261
|
+
"""Initialize configuration error.
|
|
262
|
+
|
|
263
|
+
Args:
|
|
264
|
+
message: Primary error message
|
|
265
|
+
config_key: Configuration key that caused the error
|
|
266
|
+
config_value: Invalid configuration value
|
|
267
|
+
context: Additional context information
|
|
268
|
+
"""
|
|
269
|
+
super().__init__(message, context)
|
|
270
|
+
self.config_key = config_key
|
|
271
|
+
self.config_value = config_value
|
|
272
|
+
|
|
273
|
+
def __str__(self) -> str:
|
|
274
|
+
"""Return detailed string representation including configuration information."""
|
|
275
|
+
parts = [self.message]
|
|
276
|
+
|
|
277
|
+
if self.config_key:
|
|
278
|
+
parts.append(f"key: {self.config_key}")
|
|
279
|
+
|
|
280
|
+
if self.config_value is not None:
|
|
281
|
+
parts.append(f"value: {self.config_value}")
|
|
282
|
+
|
|
283
|
+
if self.context:
|
|
284
|
+
context_str = ", ".join(f"{k}={v}" for k, v in self.context.items())
|
|
285
|
+
parts.append(f"context: {context_str}")
|
|
286
|
+
|
|
287
|
+
return " | ".join(parts)
|