0
# Global Configuration
1
2
Package-wide settings for custom encoders, decoders, and marshmallow fields that apply across all dataclasses unless overridden at the field level. Global configuration provides a centralized way to define type-specific serialization behavior.
3
4
## Capabilities
5
6
### Global Configuration Object
7
8
The `global_config` object provides package-wide settings that affect all dataclass serialization unless overridden.
9
10
```python { .api }
11
class _GlobalConfig:
12
"""
13
Global configuration singleton for package-wide serialization settings.
14
"""
15
encoders: Dict[Union[type, Optional[type]], Callable]
16
decoders: Dict[Union[type, Optional[type]], Callable]
17
mm_fields: Dict[Union[type, Optional[type]], marshmallow.fields.Field]
18
19
global_config: _GlobalConfig
20
```
21
22
The global configuration object contains three main dictionaries:
23
24
- **encoders**: Maps types to functions that convert Python values to JSON-serializable values
25
- **decoders**: Maps types to functions that convert JSON values back to Python values
26
- **mm_fields**: Maps types to marshmallow Field instances for validation and transformation
27
28
## Global Encoder Configuration
29
30
Register custom encoders for specific types that will be used across all dataclasses.
31
32
```python { .api }
33
# Encoder function signature
34
EncoderFunc = Callable[[Any], Any] # Python value -> JSON-serializable value
35
```
36
37
Usage example:
38
39
```python
40
from dataclasses import dataclass
41
from dataclasses_json import dataclass_json, global_config
42
from datetime import date, datetime
43
from decimal import Decimal
44
import uuid
45
46
# Register global encoders
47
global_config.encoders[date] = lambda d: d.isoformat()
48
global_config.encoders[datetime] = lambda dt: dt.isoformat()
49
global_config.encoders[Decimal] = str
50
global_config.encoders[uuid.UUID] = str
51
52
@dataclass_json
53
@dataclass
54
class Document:
55
id: uuid.UUID
56
title: str
57
created_date: date
58
modified_time: datetime
59
price: Decimal
60
61
# All instances automatically use global encoders
62
doc = Document(
63
id=uuid.uuid4(),
64
title="Sample Document",
65
created_date=date.today(),
66
modified_time=datetime.now(),
67
price=Decimal("19.99")
68
)
69
70
json_str = doc.to_json()
71
# Uses global encoders for UUID, date, datetime, and Decimal
72
```
73
74
## Global Decoder Configuration
75
76
Register custom decoders for converting JSON values back to Python types.
77
78
```python { .api }
79
# Decoder function signature
80
DecoderFunc = Callable[[Any], Any] # JSON value -> Python value
81
```
82
83
Usage example:
84
85
```python
86
from datetime import date, datetime
87
from decimal import Decimal
88
import uuid
89
90
# Register global decoders
91
global_config.decoders[date] = lambda s: datetime.fromisoformat(s).date()
92
global_config.decoders[datetime] = datetime.fromisoformat
93
global_config.decoders[Decimal] = Decimal
94
global_config.decoders[uuid.UUID] = uuid.UUID
95
96
# Now all dataclasses can deserialize these types automatically
97
json_data = '''{
98
"id": "123e4567-e89b-12d3-a456-426614174000",
99
"title": "Sample Document",
100
"created_date": "2023-12-01",
101
"modified_time": "2023-12-01T10:30:00",
102
"price": "19.99"
103
}'''
104
105
doc = Document.from_json(json_data)
106
# Automatically converts strings back to proper types
107
```
108
109
## Global Marshmallow Field Configuration
110
111
Register marshmallow fields for types that require validation or complex transformation.
112
113
```python { .api }
114
from marshmallow import fields
115
116
# Common marshmallow field types
117
Field = fields.Field
118
```
119
120
Usage example:
121
122
```python
123
from marshmallow import fields
124
from datetime import datetime
125
from decimal import Decimal
126
127
# Register global marshmallow fields
128
global_config.mm_fields[datetime] = fields.DateTime(format='iso')
129
global_config.mm_fields[Decimal] = fields.Decimal(places=2, rounding='ROUND_HALF_UP')
130
global_config.mm_fields[str] = fields.String(validate=lambda x: len(x.strip()) > 0)
131
132
@dataclass_json
133
@dataclass
134
class Order:
135
timestamp: datetime
136
amount: Decimal
137
description: str
138
139
# Schema validation uses global mm_fields
140
schema = Order.schema()
141
142
# This will validate using global field definitions
143
try:
144
order = schema.loads('{"timestamp": "2023-12-01T10:30:00", "amount": "19.999", "description": ""}')
145
except ValidationError as e:
146
print("Validation failed:", e) # Empty description fails validation
147
```
148
149
## Configuration Priority
150
151
Configuration follows a clear precedence hierarchy:
152
153
1. **Field-level configuration** (highest priority)
154
2. **Global configuration**
155
3. **Library defaults** (lowest priority)
156
157
Example demonstrating precedence:
158
159
```python
160
from dataclasses import dataclass, field
161
from dataclasses_json import dataclass_json, config, global_config
162
from datetime import datetime
163
164
# Global configuration
165
global_config.encoders[datetime] = lambda dt: dt.strftime('%Y-%m-%d')
166
167
@dataclass_json
168
@dataclass
169
class Event:
170
# Uses global encoder (YYYY-MM-DD format)
171
start_time: datetime
172
173
# Field-level config overrides global (ISO format)
174
end_time: datetime = field(metadata=config(
175
encoder=lambda dt: dt.isoformat()
176
))
177
178
event = Event(datetime.now(), datetime.now())
179
json_str = event.to_json()
180
# start_time uses global encoder, end_time uses field-level encoder
181
```
182
183
## Advanced Global Configuration Patterns
184
185
### Type Hierarchy Configuration
186
187
Configure encoders for base classes that apply to all subclasses:
188
189
```python
190
from abc import ABC, abstractmethod
191
192
class SerializableEntity(ABC):
193
@abstractmethod
194
def to_dict(self):
195
pass
196
197
# Global encoder for all SerializableEntity subclasses
198
global_config.encoders[SerializableEntity] = lambda obj: obj.to_dict()
199
200
class User(SerializableEntity):
201
def __init__(self, name: str):
202
self.name = name
203
204
def to_dict(self):
205
return {"name": self.name}
206
207
@dataclass_json
208
@dataclass
209
class Document:
210
author: User # Uses global SerializableEntity encoder
211
title: str
212
```
213
214
### Optional Type Configuration
215
216
Handle Optional types explicitly:
217
218
```python
219
from typing import Optional
220
from datetime import datetime
221
222
# Configure for Optional[datetime]
223
global_config.encoders[Optional[datetime]] = lambda dt: dt.isoformat() if dt else None
224
global_config.decoders[Optional[datetime]] = lambda s: datetime.fromisoformat(s) if s else None
225
226
@dataclass_json
227
@dataclass
228
class Task:
229
name: str
230
due_date: Optional[datetime] = None # Uses Optional[datetime] configuration
231
```
232
233
### Environment-Based Configuration
234
235
Configure based on runtime environment:
236
237
```python
238
import os
239
from datetime import datetime
240
241
# Development vs Production encoders
242
if os.getenv('ENV') == 'development':
243
# Verbose format for debugging
244
global_config.encoders[datetime] = lambda dt: {
245
'timestamp': dt.isoformat(),
246
'readable': dt.strftime('%Y-%m-%d %H:%M:%S'),
247
'timezone': str(dt.tzinfo)
248
}
249
else:
250
# Compact format for production
251
global_config.encoders[datetime] = lambda dt: dt.timestamp()
252
```
253
254
### Configuration Validation
255
256
Validate global configuration at startup:
257
258
```python
259
def validate_global_config():
260
"""Validate that all global encoders have corresponding decoders."""
261
encoder_types = set(global_config.encoders.keys())
262
decoder_types = set(global_config.decoders.keys())
263
264
missing_decoders = encoder_types - decoder_types
265
if missing_decoders:
266
raise ValueError(f"Missing decoders for types: {missing_decoders}")
267
268
# Call during application initialization
269
validate_global_config()
270
```
271
272
### Configuration Backup and Restore
273
274
Save and restore configuration state:
275
276
```python
277
def backup_global_config():
278
"""Create a backup of current global configuration."""
279
return {
280
'encoders': global_config.encoders.copy(),
281
'decoders': global_config.decoders.copy(),
282
'mm_fields': global_config.mm_fields.copy()
283
}
284
285
def restore_global_config(backup):
286
"""Restore global configuration from backup."""
287
global_config.encoders.clear()
288
global_config.encoders.update(backup['encoders'])
289
global_config.decoders.clear()
290
global_config.decoders.update(backup['decoders'])
291
global_config.mm_fields.clear()
292
global_config.mm_fields.update(backup['mm_fields'])
293
294
# Usage in tests
295
def test_with_custom_config():
296
backup = backup_global_config()
297
try:
298
# Modify global config for test
299
global_config.encoders[str] = lambda s: s.upper()
300
# ... run test ...
301
finally:
302
restore_global_config(backup)
303
```
304
305
## Performance Considerations
306
307
Global configuration lookups are performed for every field serialization. For optimal performance:
308
309
- Keep global configuration minimal and focused on frequently used types
310
- Use field-level configuration for one-off customizations
311
- Consider caching for expensive encoder/decoder operations
312
313
```python
314
from functools import lru_cache
315
316
# Cache expensive transformations
317
@lru_cache(maxsize=1000)
318
def expensive_encoder(value):
319
# Complex transformation logic
320
return transformed_value
321
322
global_config.encoders[ComplexType] = expensive_encoder
323
```