or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

code-generation.mdcore-api.mderror-handling.mdindex.mdpreconf-converters.mdstrategies.md

strategies.mddocs/

0

# Strategies and Customization

1

2

Powerful strategies for handling complex type scenarios including union types, subclass hierarchies, and class method integration. These utilities provide advanced configuration options for converters to handle sophisticated typing patterns and custom serialization requirements.

3

4

## Capabilities

5

6

### Union Type Strategies

7

8

Strategies for handling union types with disambiguation and tagging support.

9

10

```python { .api }

11

from cattrs.strategies import configure_tagged_union, configure_union_passthrough, default_tag_generator

12

from attrs import NOTHING

13

14

def configure_tagged_union(

15

union,

16

converter,

17

tag_generator=default_tag_generator,

18

tag_name="_type",

19

default=NOTHING

20

):

21

"""

22

Configure tagged union handling for converters.

23

24

Sets up a converter to handle union types by adding a discriminator tag

25

to distinguish between different union members during serialization.

26

27

Parameters:

28

- union: The union type to configure

29

- converter: The converter to configure

30

- tag_generator: Function to generate tags for union members (default: default_tag_generator)

31

- tag_name: Name of the discriminator field (default: "_type")

32

- default: Optional class to use if tag information is not present

33

"""

34

35

def configure_union_passthrough(union, converter, accept_ints_as_floats=True):

36

"""

37

Configure union passthrough for simple types.

38

39

Allows simple types in unions to pass through without modification,

40

useful for unions containing primitives or simple types that are natively

41

supported by serialization libraries.

42

43

Parameters:

44

- union: The union type to configure

45

- converter: The converter to configure

46

- accept_ints_as_floats: Whether to accept integers as valid floats

47

"""

48

49

def default_tag_generator(union_member_type):

50

"""

51

Default function for generating union tags.

52

53

Parameters:

54

- union_member_type: The type to generate a tag for

55

56

Returns:

57

String tag representing the type

58

"""

59

```

60

61

#### Usage Example

62

63

```python

64

from cattrs.strategies import configure_tagged_union

65

from cattrs import Converter

66

from attrs import define

67

from typing import Union

68

69

@define

70

class Dog:

71

name: str

72

breed: str

73

74

@define

75

class Cat:

76

name: str

77

lives: int

78

79

Animal = Union[Dog, Cat]

80

81

converter = Converter()

82

configure_tagged_union(Animal, converter)

83

84

# Tagged union serialization

85

dog = Dog("Buddy", "Golden Retriever")

86

dog_data = converter.unstructure(dog)

87

# Result: {'name': 'Buddy', 'breed': 'Golden Retriever', '_type': 'Dog'}

88

89

# Deserialization uses tag to determine type

90

dog_copy = converter.structure(dog_data, Animal)

91

# Returns: Dog instance

92

```

93

94

### Subclass Handling

95

96

Strategies for automatically including and handling subclass hierarchies.

97

98

```python { .api }

99

from cattrs.strategies import include_subclasses

100

101

def include_subclasses(

102

base_class,

103

converter,

104

**kwargs

105

):

106

"""

107

Configure converter to handle subclasses automatically.

108

109

Sets up the converter to recognize and handle all subclasses of a base class,

110

enabling polymorphic serialization and deserialization.

111

112

Parameters:

113

- base_class: The base class whose subclasses should be included

114

- converter: The converter to configure

115

- **kwargs: Additional configuration options

116

117

Returns:

118

The configured converter

119

"""

120

```

121

122

#### Usage Example

123

124

```python

125

from cattrs.strategies import include_subclasses

126

from cattrs import Converter

127

from attrs import define

128

129

@define

130

class Shape:

131

color: str

132

133

@define

134

class Circle(Shape):

135

radius: float

136

137

@define

138

class Rectangle(Shape):

139

width: float

140

height: float

141

142

converter = Converter()

143

include_subclasses(Shape, converter)

144

145

# Now all Shape subclasses are handled automatically

146

shapes = [

147

Circle(color="red", radius=5.0),

148

Rectangle(color="blue", width=10.0, height=20.0)

149

]

150

151

# Serialize list of mixed subclasses

152

shapes_data = converter.unstructure(shapes)

153

154

# Deserialize back to correct subclass types

155

shapes_copy = converter.structure(shapes_data, list[Shape])

156

# Returns: [Circle(...), Rectangle(...)]

157

```

158

159

### Class Method Integration

160

161

Strategies for using class-specific (un)structuring methods.

162

163

```python { .api }

164

from cattrs.strategies import use_class_methods

165

166

def use_class_methods(

167

cl,

168

converter,

169

structure_method="from_dict",

170

unstructure_method="to_dict",

171

**kwargs

172

):

173

"""

174

Configure converter to use class-specific (un)structuring methods.

175

176

Allows classes to define their own serialization logic through class methods,

177

which the converter will use instead of the default structuring behavior.

178

179

Parameters:

180

- cl: The class to configure

181

- converter: The converter to configure

182

- structure_method: Name of the class method for structuring (default: "from_dict")

183

- unstructure_method: Name of the instance method for unstructuring (default: "to_dict")

184

- **kwargs: Additional configuration options

185

186

Returns:

187

The configured converter

188

"""

189

```

190

191

#### Usage Example

192

193

```python

194

from cattrs.strategies import use_class_methods

195

from cattrs import Converter

196

from attrs import define

197

from datetime import datetime

198

199

@define

200

class TimestampedData:

201

value: str

202

timestamp: datetime

203

204

@classmethod

205

def from_dict(cls, data):

206

# Custom deserialization logic

207

return cls(

208

value=data["value"],

209

timestamp=datetime.fromisoformat(data["timestamp"])

210

)

211

212

def to_dict(self):

213

# Custom serialization logic

214

return {

215

"value": self.value,

216

"timestamp": self.timestamp.isoformat()

217

}

218

219

converter = Converter()

220

use_class_methods(TimestampedData, converter)

221

222

# Now the converter uses the class's custom methods

223

data = TimestampedData("test", datetime.now())

224

serialized = converter.unstructure(data) # Uses to_dict()

225

deserialized = converter.structure(serialized, TimestampedData) # Uses from_dict()

226

```

227

228

## Advanced Customization Patterns

229

230

### Custom Hook Registration

231

232

```python

233

from cattrs import Converter

234

from typing import Set, List

235

236

def structure_set_from_list(val, _):

237

return set(val)

238

239

def unstructure_set_to_list(val):

240

return list(val)

241

242

converter = Converter()

243

converter.register_structure_hook(Set, structure_set_from_list)

244

converter.register_unstructure_hook(Set, unstructure_set_to_list)

245

246

# Sets are now serialized as lists

247

my_set = {1, 2, 3}

248

list_data = converter.unstructure(my_set) # [1, 2, 3]

249

set_copy = converter.structure(list_data, Set[int]) # {1, 2, 3}

250

```

251

252

### Combining Multiple Strategies

253

254

```python

255

from cattrs.strategies import configure_tagged_union, include_subclasses

256

from cattrs import Converter

257

from typing import Union

258

259

@define

260

class Vehicle:

261

brand: str

262

263

@define

264

class Car(Vehicle):

265

doors: int

266

267

@define

268

class Motorcycle(Vehicle):

269

engine_size: float

270

271

VehicleUnion = Union[Car, Motorcycle]

272

273

converter = Converter()

274

275

# Combine strategies for comprehensive handling

276

include_subclasses(Vehicle, converter)

277

configure_tagged_union(VehicleUnion, converter)

278

279

# Now handles both subclass polymorphism and union discrimination

280

vehicles = [Car("Toyota", 4), Motorcycle("Harley", 1200.0)]

281

vehicle_data = converter.unstructure(vehicles)

282

vehicles_copy = converter.structure(vehicle_data, List[VehicleUnion])

283

```

284

285

### Custom Tag Generation

286

287

```python

288

from cattrs.strategies import configure_tagged_union

289

from cattrs import Converter

290

291

def custom_tag_generator(cls):

292

"""Generate custom tags based on class name."""

293

return f"type_{cls.__name__.lower()}"

294

295

configure_tagged_union(

296

MyUnion,

297

converter,

298

tag_name="object_type",

299

tag_generator=custom_tag_generator

300

)

301

302

# Results in tags like: {"object_type": "type_dog", ...}

303

```

304

305

## Integration with Other Features

306

307

### Error Handling Integration

308

309

Strategies work seamlessly with cattrs error handling, providing detailed validation errors when structuring fails:

310

311

```python

312

from cattrs.strategies import configure_tagged_union

313

from cattrs import structure, ClassValidationError

314

315

try:

316

# Invalid tagged union data

317

bad_data = {"name": "Buddy", "_type": "InvalidType"}

318

animal = structure(bad_data, Animal)

319

except ClassValidationError as e:

320

print(f"Validation failed: {e}")

321

# Provides detailed error about unknown union tag

322

```

323

324

### Performance Considerations

325

326

Strategies integrate with cattrs' code generation system for optimal performance:

327

328

```python

329

from cattrs.gen import make_dict_structure_fn

330

from cattrs.strategies import include_subclasses

331

332

# Subclass handling with optimized code generation

333

converter = Converter()

334

include_subclasses(Shape, converter)

335

336

# Generated functions are optimized for the configured strategies

337

structure_fn = make_dict_structure_fn(Shape, converter)

338

```

339

340

## Types

341

342

```python { .api }

343

from typing import TypeVar, Callable, Any, Union as TypingUnion

344

345

T = TypeVar('T')

346

347

# Type aliases for strategy functions

348

TagGenerator = Callable[[type], str]

349

UnionDiscriminator = Callable[[Any, type], bool]

350

StructureMethod = str

351

UnstructureMethod = str

352

```