statica 1.3.0__tar.gz → 1.4.0__tar.gz
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.
- {statica-1.3.0/statica.egg-info → statica-1.4.0}/PKG-INFO +106 -52
- {statica-1.3.0 → statica-1.4.0}/README.md +105 -51
- {statica-1.3.0 → statica-1.4.0}/pyproject.toml +1 -1
- statica-1.4.0/statica/config.py +33 -0
- {statica-1.3.0 → statica-1.4.0}/statica/core.py +110 -54
- {statica-1.3.0 → statica-1.4.0}/statica/validation.py +55 -16
- {statica-1.3.0 → statica-1.4.0/statica.egg-info}/PKG-INFO +106 -52
- {statica-1.3.0 → statica-1.4.0}/statica.egg-info/SOURCES.txt +3 -0
- {statica-1.3.0 → statica-1.4.0}/tests/test_aliasing.py +28 -48
- statica-1.4.0/tests/test_config.py +221 -0
- statica-1.4.0/tests/test_default_field.py +514 -0
- {statica-1.3.0 → statica-1.4.0}/tests/test_descriptor.py +6 -6
- {statica-1.3.0 → statica-1.4.0}/tests/test_validate_or_raise.py +1 -1
- {statica-1.3.0 → statica-1.4.0}/LICENSE +0 -0
- {statica-1.3.0 → statica-1.4.0}/setup.cfg +0 -0
- {statica-1.3.0 → statica-1.4.0}/statica/__init__.py +0 -0
- {statica-1.3.0 → statica-1.4.0}/statica/exceptions.py +0 -0
- {statica-1.3.0 → statica-1.4.0}/statica.egg-info/dependency_links.txt +0 -0
- {statica-1.3.0 → statica-1.4.0}/statica.egg-info/top_level.txt +0 -0
- {statica-1.3.0 → statica-1.4.0}/tests/test_python_behavior.py +0 -0
- {statica-1.3.0 → statica-1.4.0}/tests/test_statica.py +0 -0
- {statica-1.3.0 → statica-1.4.0}/tests/test_validation.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: statica
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.0
|
|
4
4
|
Summary: A minimalistic data validation library
|
|
5
5
|
Author-email: Marcel Kröker <kroeker.marcel@gmail.com>
|
|
6
6
|
License: MIT License
|
|
@@ -47,7 +47,7 @@ Statica is a Python library for defining and validating structured data with typ
|
|
|
47
47
|
Why Statica?
|
|
48
48
|
----------------------------------------------------------------------------------------
|
|
49
49
|
|
|
50
|
-
Statica was created to address the need for a lightweight, flexible, and dependency-free alternative to
|
|
50
|
+
Statica was created to address the need for a lightweight, flexible, and dependency-free alternative to Pydantic.
|
|
51
51
|
While pydantic is a powerful tool for data validation and parsing, Statica offers some distinct advantages in specific situations:
|
|
52
52
|
|
|
53
53
|
1. **Lightweight**: Statica has zero dependencies, making it ideal for projects where minimizing external dependencies is a priority.
|
|
@@ -61,6 +61,7 @@ Features
|
|
|
61
61
|
|
|
62
62
|
- **Type Validation**: Automatically validates types for attributes based on type hints.
|
|
63
63
|
- **Constraint Validation**: Define constraints like minimum/maximum length, value ranges, and more.
|
|
64
|
+
- **Default Field Values**: Set default values for fields that are used when not explicitly provided.
|
|
64
65
|
- **Customizable Error Handling**: Use custom exception classes for type and constraint errors.
|
|
65
66
|
- **Flexible Field Descriptors**: Add constraints, casting, and other behaviors to your fields.
|
|
66
67
|
- **Optional Fields**: Support for optional fields with default values.
|
|
@@ -152,13 +153,92 @@ payload = OptionalPayload()
|
|
|
152
153
|
print(payload.name) # Output: None
|
|
153
154
|
```
|
|
154
155
|
|
|
156
|
+
### Default Field Values
|
|
157
|
+
|
|
158
|
+
You can specify default values for fields in two ways:
|
|
159
|
+
|
|
160
|
+
#### Using Field() with default parameter
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
class User(Statica):
|
|
164
|
+
name: str
|
|
165
|
+
age: int = Field(default=25)
|
|
166
|
+
active: bool = Field(default=True)
|
|
167
|
+
|
|
168
|
+
# Using direct initialization
|
|
169
|
+
user = User(name="John")
|
|
170
|
+
print(user.name) # Output: "John"
|
|
171
|
+
print(user.age) # Output: 25
|
|
172
|
+
print(user.active) # Output: True
|
|
173
|
+
|
|
174
|
+
# Using from_map
|
|
175
|
+
user = User.from_map({"name": "Jane"})
|
|
176
|
+
print(user.age) # Output: 25 (default used)
|
|
177
|
+
|
|
178
|
+
# Explicit values override defaults
|
|
179
|
+
user = User(name="Bob", age=30, active=False)
|
|
180
|
+
print(user.age) # Output: 30
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
#### Direct assignment (without Field)
|
|
184
|
+
|
|
185
|
+
You can also assign default values directly to fields without using `Field()`:
|
|
186
|
+
|
|
187
|
+
```python
|
|
188
|
+
class Config(Statica):
|
|
189
|
+
name: str
|
|
190
|
+
timeout: float = 30.0 # Direct assignment
|
|
191
|
+
retries: int = 3 # Direct assignment
|
|
192
|
+
debug: bool = False # Direct assignment
|
|
193
|
+
|
|
194
|
+
config = Config(name="server")
|
|
195
|
+
print(config.timeout) # Output: 30.0
|
|
196
|
+
print(config.retries) # Output: 3
|
|
197
|
+
print(config.debug) # Output: False
|
|
198
|
+
|
|
199
|
+
# Works with from_map too
|
|
200
|
+
config = Config.from_map({"name": "api-server"})
|
|
201
|
+
print(config.timeout) # Output: 30.0 (default used)
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Both approaches work identically and can be mixed within the same class. Use `Field(default=...)` when you need additional constraints or options, and direct assignment for simple defaults.
|
|
205
|
+
|
|
206
|
+
#### Validation and Safety
|
|
207
|
+
|
|
208
|
+
Default values are validated against any constraints you've defined:
|
|
209
|
+
|
|
210
|
+
```python
|
|
211
|
+
class Config(Statica):
|
|
212
|
+
timeout: float = Field(default=30.0, min_value=1.0, max_value=120.0)
|
|
213
|
+
retries: int = Field(default=3, min_value=1)
|
|
214
|
+
|
|
215
|
+
config = Config() # Uses defaults: timeout=30.0, retries=3
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
For mutable default values (like lists, dicts, sets), Statica automatically creates copies to prevent shared state issues:
|
|
219
|
+
|
|
220
|
+
```python
|
|
221
|
+
class UserProfile(Statica):
|
|
222
|
+
name: str
|
|
223
|
+
tags: list[str] = Field(default=[]) # or tags: list[str] = []
|
|
224
|
+
|
|
225
|
+
user1 = UserProfile(name="Alice")
|
|
226
|
+
user2 = UserProfile(name="Bob")
|
|
227
|
+
|
|
228
|
+
user1.tags.append("admin")
|
|
229
|
+
print(user1.tags) # Output: ["admin"]
|
|
230
|
+
print(user2.tags) # Output: [] (not affected)
|
|
231
|
+
```
|
|
232
|
+
|
|
155
233
|
### Field Constraints
|
|
156
234
|
|
|
157
|
-
You can specify constraints on fields:
|
|
235
|
+
You can specify constraints and options on fields:
|
|
158
236
|
|
|
237
|
+
- **Default Values**: `default` (using `Field()`) or direct assignment
|
|
159
238
|
- **String Constraints**: `min_length`, `max_length`, `strip_whitespace`
|
|
160
239
|
- **Numeric Constraints**: `min_value`, `max_value`
|
|
161
240
|
- **Casting**: `cast_to`
|
|
241
|
+
- **Aliasing**: `alias`
|
|
162
242
|
|
|
163
243
|
```python
|
|
164
244
|
class StringTest(Statica):
|
|
@@ -166,6 +246,15 @@ class StringTest(Statica):
|
|
|
166
246
|
|
|
167
247
|
class IntTest(Statica):
|
|
168
248
|
num: int = Field(min_value=1, max_value=10, cast_to=int)
|
|
249
|
+
|
|
250
|
+
class DefaultTest(Statica):
|
|
251
|
+
# Using Field() for defaults with constraints
|
|
252
|
+
status: str = Field(default="active")
|
|
253
|
+
priority: int = Field(default=1, min_value=1, max_value=5)
|
|
254
|
+
|
|
255
|
+
# Direct assignment for simple defaults
|
|
256
|
+
timeout: float = 30.0
|
|
257
|
+
retries: int = 3
|
|
169
258
|
```
|
|
170
259
|
|
|
171
260
|
### Custom Error Classes
|
|
@@ -212,68 +301,27 @@ Aliasing
|
|
|
212
301
|
Statica supports field aliasing, allowing you to map different field names for parsing and serialization.
|
|
213
302
|
This is particularly useful when working with external APIs that use different naming conventions.
|
|
214
303
|
|
|
215
|
-
### Basic Aliases
|
|
216
|
-
|
|
217
304
|
Use the `alias` parameter to define an alternative name for both parsing and serialization:
|
|
218
305
|
|
|
219
306
|
```python
|
|
220
307
|
class User(Statica):
|
|
221
308
|
full_name: str = Field(alias="fullName")
|
|
222
|
-
age: int = Field(alias="userAge")
|
|
309
|
+
age: int = Field(alias="userAge", default=25)
|
|
223
310
|
|
|
224
311
|
# Parse data with aliases
|
|
225
|
-
data = {"fullName": "John Doe"
|
|
312
|
+
data = {"fullName": "John Doe"} # userAge not provided, uses default
|
|
226
313
|
user = User.from_map(data)
|
|
227
314
|
print(user.full_name) # Output: "John Doe"
|
|
228
|
-
print(user.age) # Output:
|
|
315
|
+
print(user.age) # Output: 25
|
|
229
316
|
|
|
230
|
-
# Serialize back with aliases
|
|
317
|
+
# Serialize back with aliases (uses the alias for serialization by default)
|
|
231
318
|
result = user.to_dict()
|
|
232
|
-
print(result) # Output: {"fullName": "John Doe", "userAge":
|
|
233
|
-
```
|
|
319
|
+
print(result) # Output: {"fullName": "John Doe", "userAge": 25}
|
|
234
320
|
|
|
235
|
-
|
|
321
|
+
# Serialize without aliases
|
|
322
|
+
result_no_alias = user.to_dict(with_aliases=False)
|
|
323
|
+
print(result_no_alias) # Output: {"full_name": "John Doe", "age": 25}
|
|
236
324
|
|
|
237
|
-
You can define different aliases for parsing and serialization:
|
|
238
|
-
|
|
239
|
-
```python
|
|
240
|
-
class APIModel(Statica):
|
|
241
|
-
user_name: str = Field(
|
|
242
|
-
alias_for_parsing="userName",
|
|
243
|
-
alias_for_serialization="username"
|
|
244
|
-
)
|
|
245
|
-
user_id: int = Field(alias_for_parsing="userId")
|
|
246
|
-
|
|
247
|
-
# Parse from camelCase API response
|
|
248
|
-
api_data = {"userName": "alice", "userId": 123}
|
|
249
|
-
model = APIModel.from_map(api_data)
|
|
250
|
-
|
|
251
|
-
# Serialize to snake_case for internal use
|
|
252
|
-
internal_data = model.to_dict()
|
|
253
|
-
print(internal_data) # Output: {"username": "alice", "user_id": 123}
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
### Alias Priority
|
|
257
|
-
|
|
258
|
-
When multiple alias types are defined, the priority is:
|
|
259
|
-
1. `alias_for_parsing` for parsing operations
|
|
260
|
-
2. `alias_for_serialization` for serialization operations
|
|
261
|
-
3. `alias` as a fallback for both operations
|
|
262
|
-
|
|
263
|
-
```python
|
|
264
|
-
class PriorityExample(Statica):
|
|
265
|
-
field_name: str = Field(
|
|
266
|
-
alias="generalAlias",
|
|
267
|
-
alias_for_parsing="parseAlias",
|
|
268
|
-
alias_for_serialization="serializeAlias"
|
|
269
|
-
)
|
|
270
|
-
|
|
271
|
-
# Uses alias_for_parsing
|
|
272
|
-
instance = PriorityExample.from_map({"parseAlias": "value"})
|
|
273
|
-
|
|
274
|
-
# Uses alias_for_serialization
|
|
275
|
-
result = instance.to_dict()
|
|
276
|
-
print(result) # Output: {"serializeAlias": "value"}
|
|
277
325
|
```
|
|
278
326
|
|
|
279
327
|
Advanced Usage
|
|
@@ -296,6 +344,12 @@ print(instance.num) # Output: 42
|
|
|
296
344
|
```
|
|
297
345
|
|
|
298
346
|
|
|
347
|
+
Design Decisions
|
|
348
|
+
----------------------------------------------------------------------------------------
|
|
349
|
+
|
|
350
|
+
- [Inheritance over Decorators](docs/decision-inheritance-over-decorators.md)
|
|
351
|
+
|
|
352
|
+
|
|
299
353
|
Contributing
|
|
300
354
|
----------------------------------------------------------------------------------------
|
|
301
355
|
|
|
@@ -11,7 +11,7 @@ Statica is a Python library for defining and validating structured data with typ
|
|
|
11
11
|
Why Statica?
|
|
12
12
|
----------------------------------------------------------------------------------------
|
|
13
13
|
|
|
14
|
-
Statica was created to address the need for a lightweight, flexible, and dependency-free alternative to
|
|
14
|
+
Statica was created to address the need for a lightweight, flexible, and dependency-free alternative to Pydantic.
|
|
15
15
|
While pydantic is a powerful tool for data validation and parsing, Statica offers some distinct advantages in specific situations:
|
|
16
16
|
|
|
17
17
|
1. **Lightweight**: Statica has zero dependencies, making it ideal for projects where minimizing external dependencies is a priority.
|
|
@@ -25,6 +25,7 @@ Features
|
|
|
25
25
|
|
|
26
26
|
- **Type Validation**: Automatically validates types for attributes based on type hints.
|
|
27
27
|
- **Constraint Validation**: Define constraints like minimum/maximum length, value ranges, and more.
|
|
28
|
+
- **Default Field Values**: Set default values for fields that are used when not explicitly provided.
|
|
28
29
|
- **Customizable Error Handling**: Use custom exception classes for type and constraint errors.
|
|
29
30
|
- **Flexible Field Descriptors**: Add constraints, casting, and other behaviors to your fields.
|
|
30
31
|
- **Optional Fields**: Support for optional fields with default values.
|
|
@@ -116,13 +117,92 @@ payload = OptionalPayload()
|
|
|
116
117
|
print(payload.name) # Output: None
|
|
117
118
|
```
|
|
118
119
|
|
|
120
|
+
### Default Field Values
|
|
121
|
+
|
|
122
|
+
You can specify default values for fields in two ways:
|
|
123
|
+
|
|
124
|
+
#### Using Field() with default parameter
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
class User(Statica):
|
|
128
|
+
name: str
|
|
129
|
+
age: int = Field(default=25)
|
|
130
|
+
active: bool = Field(default=True)
|
|
131
|
+
|
|
132
|
+
# Using direct initialization
|
|
133
|
+
user = User(name="John")
|
|
134
|
+
print(user.name) # Output: "John"
|
|
135
|
+
print(user.age) # Output: 25
|
|
136
|
+
print(user.active) # Output: True
|
|
137
|
+
|
|
138
|
+
# Using from_map
|
|
139
|
+
user = User.from_map({"name": "Jane"})
|
|
140
|
+
print(user.age) # Output: 25 (default used)
|
|
141
|
+
|
|
142
|
+
# Explicit values override defaults
|
|
143
|
+
user = User(name="Bob", age=30, active=False)
|
|
144
|
+
print(user.age) # Output: 30
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
#### Direct assignment (without Field)
|
|
148
|
+
|
|
149
|
+
You can also assign default values directly to fields without using `Field()`:
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
class Config(Statica):
|
|
153
|
+
name: str
|
|
154
|
+
timeout: float = 30.0 # Direct assignment
|
|
155
|
+
retries: int = 3 # Direct assignment
|
|
156
|
+
debug: bool = False # Direct assignment
|
|
157
|
+
|
|
158
|
+
config = Config(name="server")
|
|
159
|
+
print(config.timeout) # Output: 30.0
|
|
160
|
+
print(config.retries) # Output: 3
|
|
161
|
+
print(config.debug) # Output: False
|
|
162
|
+
|
|
163
|
+
# Works with from_map too
|
|
164
|
+
config = Config.from_map({"name": "api-server"})
|
|
165
|
+
print(config.timeout) # Output: 30.0 (default used)
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Both approaches work identically and can be mixed within the same class. Use `Field(default=...)` when you need additional constraints or options, and direct assignment for simple defaults.
|
|
169
|
+
|
|
170
|
+
#### Validation and Safety
|
|
171
|
+
|
|
172
|
+
Default values are validated against any constraints you've defined:
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
class Config(Statica):
|
|
176
|
+
timeout: float = Field(default=30.0, min_value=1.0, max_value=120.0)
|
|
177
|
+
retries: int = Field(default=3, min_value=1)
|
|
178
|
+
|
|
179
|
+
config = Config() # Uses defaults: timeout=30.0, retries=3
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
For mutable default values (like lists, dicts, sets), Statica automatically creates copies to prevent shared state issues:
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
class UserProfile(Statica):
|
|
186
|
+
name: str
|
|
187
|
+
tags: list[str] = Field(default=[]) # or tags: list[str] = []
|
|
188
|
+
|
|
189
|
+
user1 = UserProfile(name="Alice")
|
|
190
|
+
user2 = UserProfile(name="Bob")
|
|
191
|
+
|
|
192
|
+
user1.tags.append("admin")
|
|
193
|
+
print(user1.tags) # Output: ["admin"]
|
|
194
|
+
print(user2.tags) # Output: [] (not affected)
|
|
195
|
+
```
|
|
196
|
+
|
|
119
197
|
### Field Constraints
|
|
120
198
|
|
|
121
|
-
You can specify constraints on fields:
|
|
199
|
+
You can specify constraints and options on fields:
|
|
122
200
|
|
|
201
|
+
- **Default Values**: `default` (using `Field()`) or direct assignment
|
|
123
202
|
- **String Constraints**: `min_length`, `max_length`, `strip_whitespace`
|
|
124
203
|
- **Numeric Constraints**: `min_value`, `max_value`
|
|
125
204
|
- **Casting**: `cast_to`
|
|
205
|
+
- **Aliasing**: `alias`
|
|
126
206
|
|
|
127
207
|
```python
|
|
128
208
|
class StringTest(Statica):
|
|
@@ -130,6 +210,15 @@ class StringTest(Statica):
|
|
|
130
210
|
|
|
131
211
|
class IntTest(Statica):
|
|
132
212
|
num: int = Field(min_value=1, max_value=10, cast_to=int)
|
|
213
|
+
|
|
214
|
+
class DefaultTest(Statica):
|
|
215
|
+
# Using Field() for defaults with constraints
|
|
216
|
+
status: str = Field(default="active")
|
|
217
|
+
priority: int = Field(default=1, min_value=1, max_value=5)
|
|
218
|
+
|
|
219
|
+
# Direct assignment for simple defaults
|
|
220
|
+
timeout: float = 30.0
|
|
221
|
+
retries: int = 3
|
|
133
222
|
```
|
|
134
223
|
|
|
135
224
|
### Custom Error Classes
|
|
@@ -176,68 +265,27 @@ Aliasing
|
|
|
176
265
|
Statica supports field aliasing, allowing you to map different field names for parsing and serialization.
|
|
177
266
|
This is particularly useful when working with external APIs that use different naming conventions.
|
|
178
267
|
|
|
179
|
-
### Basic Aliases
|
|
180
|
-
|
|
181
268
|
Use the `alias` parameter to define an alternative name for both parsing and serialization:
|
|
182
269
|
|
|
183
270
|
```python
|
|
184
271
|
class User(Statica):
|
|
185
272
|
full_name: str = Field(alias="fullName")
|
|
186
|
-
age: int = Field(alias="userAge")
|
|
273
|
+
age: int = Field(alias="userAge", default=25)
|
|
187
274
|
|
|
188
275
|
# Parse data with aliases
|
|
189
|
-
data = {"fullName": "John Doe"
|
|
276
|
+
data = {"fullName": "John Doe"} # userAge not provided, uses default
|
|
190
277
|
user = User.from_map(data)
|
|
191
278
|
print(user.full_name) # Output: "John Doe"
|
|
192
|
-
print(user.age) # Output:
|
|
279
|
+
print(user.age) # Output: 25
|
|
193
280
|
|
|
194
|
-
# Serialize back with aliases
|
|
281
|
+
# Serialize back with aliases (uses the alias for serialization by default)
|
|
195
282
|
result = user.to_dict()
|
|
196
|
-
print(result) # Output: {"fullName": "John Doe", "userAge":
|
|
197
|
-
```
|
|
283
|
+
print(result) # Output: {"fullName": "John Doe", "userAge": 25}
|
|
198
284
|
|
|
199
|
-
|
|
285
|
+
# Serialize without aliases
|
|
286
|
+
result_no_alias = user.to_dict(with_aliases=False)
|
|
287
|
+
print(result_no_alias) # Output: {"full_name": "John Doe", "age": 25}
|
|
200
288
|
|
|
201
|
-
You can define different aliases for parsing and serialization:
|
|
202
|
-
|
|
203
|
-
```python
|
|
204
|
-
class APIModel(Statica):
|
|
205
|
-
user_name: str = Field(
|
|
206
|
-
alias_for_parsing="userName",
|
|
207
|
-
alias_for_serialization="username"
|
|
208
|
-
)
|
|
209
|
-
user_id: int = Field(alias_for_parsing="userId")
|
|
210
|
-
|
|
211
|
-
# Parse from camelCase API response
|
|
212
|
-
api_data = {"userName": "alice", "userId": 123}
|
|
213
|
-
model = APIModel.from_map(api_data)
|
|
214
|
-
|
|
215
|
-
# Serialize to snake_case for internal use
|
|
216
|
-
internal_data = model.to_dict()
|
|
217
|
-
print(internal_data) # Output: {"username": "alice", "user_id": 123}
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
### Alias Priority
|
|
221
|
-
|
|
222
|
-
When multiple alias types are defined, the priority is:
|
|
223
|
-
1. `alias_for_parsing` for parsing operations
|
|
224
|
-
2. `alias_for_serialization` for serialization operations
|
|
225
|
-
3. `alias` as a fallback for both operations
|
|
226
|
-
|
|
227
|
-
```python
|
|
228
|
-
class PriorityExample(Statica):
|
|
229
|
-
field_name: str = Field(
|
|
230
|
-
alias="generalAlias",
|
|
231
|
-
alias_for_parsing="parseAlias",
|
|
232
|
-
alias_for_serialization="serializeAlias"
|
|
233
|
-
)
|
|
234
|
-
|
|
235
|
-
# Uses alias_for_parsing
|
|
236
|
-
instance = PriorityExample.from_map({"parseAlias": "value"})
|
|
237
|
-
|
|
238
|
-
# Uses alias_for_serialization
|
|
239
|
-
result = instance.to_dict()
|
|
240
|
-
print(result) # Output: {"serializeAlias": "value"}
|
|
241
289
|
```
|
|
242
290
|
|
|
243
291
|
Advanced Usage
|
|
@@ -260,6 +308,12 @@ print(instance.num) # Output: 42
|
|
|
260
308
|
```
|
|
261
309
|
|
|
262
310
|
|
|
311
|
+
Design Decisions
|
|
312
|
+
----------------------------------------------------------------------------------------
|
|
313
|
+
|
|
314
|
+
- [Inheritance over Decorators](docs/decision-inheritance-over-decorators.md)
|
|
315
|
+
|
|
316
|
+
|
|
263
317
|
Contributing
|
|
264
318
|
----------------------------------------------------------------------------------------
|
|
265
319
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass(frozen=True, slots=True)
|
|
7
|
+
class StaticaConfig:
|
|
8
|
+
type_error_message: str
|
|
9
|
+
min_length_error_message: str
|
|
10
|
+
max_length_error_message: str
|
|
11
|
+
min_value_error_message: str
|
|
12
|
+
max_value_error_message: str
|
|
13
|
+
|
|
14
|
+
@classmethod
|
|
15
|
+
def create(
|
|
16
|
+
cls,
|
|
17
|
+
*,
|
|
18
|
+
type_error_message: str = "expected type '{expected_type}', got '{found_type}'",
|
|
19
|
+
min_length_error_message: str = "{field_name} length must be at least {min_length}",
|
|
20
|
+
max_length_error_message: str = "{field_name} length must be at most {max_length}",
|
|
21
|
+
min_value_error_message: str = "{field_name} must be at least {min_value}",
|
|
22
|
+
max_value_error_message: str = "{field_name} must be at most {max_value}",
|
|
23
|
+
) -> StaticaConfig:
|
|
24
|
+
return cls(
|
|
25
|
+
type_error_message=type_error_message,
|
|
26
|
+
min_length_error_message=min_length_error_message,
|
|
27
|
+
max_length_error_message=max_length_error_message,
|
|
28
|
+
min_value_error_message=min_value_error_message,
|
|
29
|
+
max_value_error_message=max_value_error_message,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
default_config = StaticaConfig.create()
|