0
# Dataclass Enhancements (edc)
1
2
Enhanced dataclass functionality with automatic type casting, context management, improved representation, and advanced field handling for robust data structures in Python applications.
3
4
## Capabilities
5
6
### Enhanced Dataclass Decorator
7
8
Improved dataclass decorator with additional functionality.
9
10
```python { .api }
11
def dataclass(
12
cls: type | None = None,
13
*,
14
init: bool = True,
15
repr: bool = True,
16
eq: bool = True,
17
order: bool = False,
18
unsafe_hash: bool = False,
19
frozen: bool = False,
20
**kwargs
21
) -> type:
22
"""
23
Enhanced dataclass decorator with additional features.
24
25
Args:
26
cls: Class to decorate (when used without parentheses)
27
init: Generate __init__ method
28
repr: Generate __repr__ method
29
eq: Generate __eq__ method
30
order: Generate ordering methods
31
unsafe_hash: Generate __hash__ method
32
frozen: Make instances immutable
33
**kwargs: Additional dataclass options
34
35
Returns:
36
Enhanced dataclass with improved functionality
37
"""
38
```
39
40
### Field Definition
41
42
Enhanced field definition with additional options.
43
44
```python { .api }
45
def field(
46
*,
47
default: Any = MISSING,
48
default_factory: Callable[[], Any] = MISSING,
49
init: bool = True,
50
repr: bool = True,
51
hash: bool | None = None,
52
compare: bool = True,
53
metadata: dict | None = None,
54
**kwargs
55
) -> Any:
56
"""
57
Enhanced field definition for dataclasses.
58
59
Args:
60
default: Default value for the field
61
default_factory: Function to generate default value
62
init: Include field in __init__
63
repr: Include field in __repr__
64
hash: Include field in __hash__
65
compare: Include field in comparison methods
66
metadata: Additional field metadata
67
**kwargs: Additional field options
68
69
Returns:
70
Field descriptor with enhanced functionality
71
"""
72
```
73
74
### Automatic Type Casting
75
76
Automatic type casting functionality for dataclass fields.
77
78
```python { .api }
79
class AutoCast:
80
"""
81
Automatic type casting for dataclass fields.
82
83
Enables automatic conversion of field values to specified types
84
during dataclass instantiation.
85
"""
86
def __init__(self, cast_fn: Callable[[Any], Any]) -> None:
87
"""
88
Initialize AutoCast with casting function.
89
90
Args:
91
cast_fn: Function to cast values to desired type
92
"""
93
self.cast_fn = cast_fn
94
95
def __call__(self, value: Any) -> Any:
96
"""
97
Apply casting function to value.
98
99
Args:
100
value: Value to cast
101
102
Returns:
103
Casted value
104
"""
105
return self.cast_fn(value)
106
```
107
108
### Enhanced Representation
109
110
Improved string representation for dataclasses.
111
112
```python { .api }
113
def repr(obj: Any) -> str:
114
"""
115
Generate enhanced string representation for objects.
116
117
Args:
118
obj: Object to represent
119
120
Returns:
121
Enhanced string representation with better formatting
122
"""
123
```
124
125
### Context Management
126
127
Context variable and stack management for dataclasses.
128
129
```python { .api }
130
class ContextVar:
131
"""
132
Context variable implementation for managing state.
133
134
Provides thread-safe context-local storage for dataclass instances.
135
"""
136
def __init__(self, name: str, default: Any = None) -> None:
137
"""
138
Initialize context variable.
139
140
Args:
141
name: Name of the context variable
142
default: Default value when not set
143
"""
144
self.name = name
145
self.default = default
146
147
def get(self, default: Any = None) -> Any:
148
"""
149
Get current context value.
150
151
Args:
152
default: Default value if not set
153
154
Returns:
155
Current context value or default
156
"""
157
...
158
159
def set(self, value: Any) -> Any:
160
"""
161
Set context value.
162
163
Args:
164
value: Value to set in context
165
166
Returns:
167
Token for resetting context
168
"""
169
...
170
171
class ContextStack:
172
"""
173
Context stack for managing nested contexts.
174
175
Manages multiple context levels with automatic cleanup.
176
"""
177
def __init__(self) -> None:
178
"""Initialize empty context stack."""
179
...
180
181
def push(self, context: dict[str, Any]) -> None:
182
"""
183
Push new context level.
184
185
Args:
186
context: Dictionary of context variables
187
"""
188
...
189
190
def pop(self) -> dict[str, Any]:
191
"""
192
Pop current context level.
193
194
Returns:
195
Popped context dictionary
196
"""
197
...
198
199
def current(self) -> dict[str, Any]:
200
"""
201
Get current context level.
202
203
Returns:
204
Current context dictionary
205
"""
206
...
207
```
208
209
## Usage Examples
210
211
### Basic Enhanced Dataclass
212
213
```python
214
from etils import edc
215
from typing import Optional
216
217
@edc.dataclass
218
class Person:
219
"""Example person dataclass with enhanced features."""
220
name: str
221
age: int
222
email: Optional[str] = edc.field(default=None, repr=False)
223
score: float = edc.field(default_factory=lambda: 0.0)
224
225
def __post_init__(self):
226
"""Validate data after initialization."""
227
if self.age < 0:
228
raise ValueError("Age cannot be negative")
229
230
# Usage
231
person = Person("Alice", 25, "alice@example.com")
232
print(edc.repr(person)) # Enhanced representation
233
```
234
235
### Automatic Type Casting
236
237
```python
238
from etils import edc
239
from typing import List
240
241
# Define casting functions
242
string_to_int = edc.AutoCast(int)
243
string_to_float = edc.AutoCast(float)
244
any_to_string = edc.AutoCast(str)
245
246
@edc.dataclass
247
class DataRecord:
248
"""Data record with automatic type casting."""
249
id: int = edc.field(default_factory=lambda: string_to_int("0"))
250
value: float = edc.field(default_factory=lambda: string_to_float("0.0"))
251
label: str = edc.field(default_factory=lambda: any_to_string(""))
252
253
# Automatic casting during initialization
254
record = DataRecord()
255
record.id = string_to_int("42") # "42" -> 42
256
record.value = string_to_float("3.14") # "3.14" -> 3.14
257
record.label = any_to_string(123) # 123 -> "123"
258
```
259
260
### Advanced Field Configuration
261
262
```python
263
from etils import edc
264
from typing import List, Dict
265
import json
266
267
@edc.dataclass
268
class Configuration:
269
"""Configuration class with advanced field handling."""
270
271
# Required field with validation
272
name: str = edc.field(
273
metadata={"description": "Configuration name", "required": True}
274
)
275
276
# Optional field excluded from repr
277
secret_key: str = edc.field(
278
default="",
279
repr=False,
280
metadata={"sensitive": True}
281
)
282
283
# Field with custom default factory
284
settings: Dict[str, str] = edc.field(
285
default_factory=dict,
286
metadata={"description": "Configuration settings"}
287
)
288
289
# Field excluded from comparison
290
timestamp: float = edc.field(
291
default_factory=lambda: time.time(),
292
compare=False,
293
metadata={"auto_generated": True}
294
)
295
296
# List field with validation
297
tags: List[str] = edc.field(
298
default_factory=list,
299
metadata={"description": "Configuration tags"}
300
)
301
302
# Usage with field metadata
303
config = Configuration(name="prod_config")
304
config.settings["debug"] = "false"
305
config.tags.append("production")
306
307
# Access field metadata
308
field_info = Configuration.__dataclass_fields__
309
print(field_info['name'].metadata) # Field metadata
310
```
311
312
### Context Management
313
314
```python
315
from etils import edc
316
import threading
317
318
# Context variable for user information
319
current_user = edc.ContextVar('current_user', default="anonymous")
320
321
@edc.dataclass
322
class UserSession:
323
"""User session with context management."""
324
user_id: str
325
permissions: List[str] = edc.field(default_factory=list)
326
327
def __enter__(self):
328
"""Enter context and set current user."""
329
self.token = current_user.set(self.user_id)
330
return self
331
332
def __exit__(self, exc_type, exc_val, exc_tb):
333
"""Exit context and restore previous user."""
334
current_user.set(self.token)
335
336
# Context stack for nested operations
337
context_stack = edc.ContextStack()
338
339
def with_user_context():
340
"""Function that uses user context."""
341
user = current_user.get()
342
print(f"Current user: {user}")
343
344
# Usage
345
with UserSession("alice", ["read", "write"]):
346
with_user_context() # Prints: Current user: alice
347
348
with UserSession("bob", ["read"]):
349
with_user_context() # Prints: Current user: bob
350
351
with_user_context() # Prints: Current user: alice
352
353
with_user_context() # Prints: Current user: anonymous
354
```
355
356
### Enhanced Representation
357
358
```python
359
from etils import edc
360
from typing import Optional, List
361
362
@edc.dataclass
363
class Product:
364
"""Product with enhanced representation."""
365
id: int
366
name: str
367
price: float
368
description: Optional[str] = None
369
tags: List[str] = edc.field(default_factory=list)
370
metadata: dict = edc.field(default_factory=dict, repr=False)
371
372
product = Product(
373
id=1,
374
name="Laptop",
375
price=999.99,
376
description="High-performance laptop",
377
tags=["electronics", "computers"]
378
)
379
380
# Enhanced representation
381
print(edc.repr(product))
382
# Produces well-formatted, readable output
383
384
# Standard dataclass repr would be less readable
385
print(repr(product))
386
```
387
388
### Complex Nested Dataclasses
389
390
```python
391
from etils import edc
392
from typing import List, Optional
393
from datetime import datetime
394
395
@edc.dataclass
396
class Address:
397
"""Address dataclass."""
398
street: str
399
city: str
400
country: str
401
postal_code: str = ""
402
403
@edc.dataclass
404
class Contact:
405
"""Contact information."""
406
email: str
407
phone: Optional[str] = None
408
address: Optional[Address] = None
409
410
@edc.dataclass
411
class User:
412
"""Complete user profile with nested dataclasses."""
413
id: int
414
username: str
415
contact: Contact
416
created_at: datetime = edc.field(default_factory=datetime.now)
417
is_active: bool = edc.field(default=True, compare=False)
418
permissions: List[str] = edc.field(default_factory=list, repr=False)
419
420
# Create nested structure
421
user = User(
422
id=1,
423
username="alice",
424
contact=Contact(
425
email="alice@example.com",
426
phone="+1-555-0123",
427
address=Address(
428
street="123 Main St",
429
city="New York",
430
country="USA",
431
postal_code="10001"
432
)
433
)
434
)
435
436
print(edc.repr(user)) # Enhanced nested representation
437
```
438
439
### Type Casting Integration
440
441
```python
442
from etils import edc
443
from typing import Union
444
import json
445
446
# Custom casting functions
447
def parse_json_string(value: Union[str, dict]) -> dict:
448
"""Parse JSON string or return dict as-is."""
449
if isinstance(value, str):
450
return json.loads(value)
451
return value
452
453
def ensure_list(value: Union[str, List]) -> List:
454
"""Ensure value is a list."""
455
if isinstance(value, str):
456
return [value]
457
return value
458
459
@edc.dataclass
460
class ApiRequest:
461
"""API request with automatic type casting."""
462
endpoint: str
463
params: dict = edc.field(
464
default_factory=dict,
465
metadata={"cast": parse_json_string}
466
)
467
headers: List[str] = edc.field(
468
default_factory=list,
469
metadata={"cast": ensure_list}
470
)
471
472
# Use casting during construction
473
request = ApiRequest(
474
endpoint="/api/users",
475
params='{"limit": 10, "offset": 0}', # JSON string -> dict
476
headers="Authorization: Bearer token" # string -> list
477
)
478
479
# Manual casting with AutoCast
480
json_caster = edc.AutoCast(parse_json_string)
481
list_caster = edc.AutoCast(ensure_list)
482
483
request.params = json_caster('{"filter": "active"}')
484
request.headers = list_caster("Content-Type: application/json")
485
```