or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-components.mdc-extensions.mdcustom-objects.mddumping.mderrors.mdindex.mdloaders-dumpers.mdloading.mdlow-level.mdregistration.md

custom-objects.mddocs/

0

# Custom YAML Objects

1

2

Base classes and utilities for creating custom YAML-serializable Python objects with automatic tag registration. These classes enable seamless integration between Python objects and YAML serialization.

3

4

## Capabilities

5

6

### YAMLObject Base Class

7

8

Base class for creating Python objects that can be automatically serialized to and from YAML with custom tags.

9

10

```python { .api }

11

class YAMLObjectMetaclass(type):

12

"""

13

Metaclass for YAML objects that automatically registers constructors and representers.

14

15

When a class uses this metaclass, it automatically registers itself

16

with the specified loaders and dumpers for seamless YAML integration.

17

"""

18

19

class YAMLObject(metaclass=YAMLObjectMetaclass):

20

"""

21

Base class for custom YAML-serializable objects.

22

23

Subclasses should define class attributes to control YAML behavior:

24

25

Class Attributes:

26

- yaml_loader: Loader class to register constructor with (default: None)

27

- yaml_dumper: Dumper class to register representer with (default: None)

28

- yaml_tag: YAML tag for this object type (required)

29

- yaml_flow_style: Use flow style for serialization (default: None)

30

"""

31

32

yaml_loader: Any = None

33

yaml_dumper: Any = None

34

yaml_tag: str = None

35

yaml_flow_style: bool | None = None

36

37

@classmethod

38

def from_yaml(cls, loader, node):

39

"""

40

Construct object from YAML node.

41

42

Parameters:

43

- loader: YAML loader instance

44

- node: YAML node to construct from

45

46

Returns:

47

- Instance of this class

48

49

Override this method to customize object construction from YAML.

50

"""

51

52

@classmethod

53

def to_yaml(cls, dumper, data):

54

"""

55

Represent object as YAML node.

56

57

Parameters:

58

- dumper: YAML dumper instance

59

- data: Object instance to represent

60

61

Returns:

62

- YAML node representing this object

63

64

Override this method to customize object representation to YAML.

65

"""

66

```

67

68

## Usage Examples

69

70

### Basic Custom Object

71

72

```python

73

import yaml

74

75

class Person(yaml.YAMLObject):

76

"""Custom person object with YAML serialization."""

77

78

yaml_tag = '!Person'

79

yaml_loader = yaml.SafeLoader

80

yaml_dumper = yaml.SafeDumper

81

82

def __init__(self, name, age, email=None):

83

self.name = name

84

self.age = age

85

self.email = email

86

87

def __repr__(self):

88

return f"Person(name='{self.name}', age={self.age}, email='{self.email}')"

89

90

def __eq__(self, other):

91

if not isinstance(other, Person):

92

return False

93

return (self.name == other.name and

94

self.age == other.age and

95

self.email == other.email)

96

97

# Create and serialize object

98

person = Person("John Doe", 30, "john@example.com")

99

100

# Dump to YAML

101

yaml_output = yaml.dump(person, Dumper=yaml.SafeDumper)

102

print(yaml_output)

103

# Output:

104

# !Person

105

# age: 30

106

# email: john@example.com

107

# name: John Doe

108

109

# Load from YAML

110

loaded_person = yaml.load(yaml_output, Loader=yaml.SafeLoader)

111

print(loaded_person) # Person(name='John Doe', age=30, email='john@example.com')

112

print(person == loaded_person) # True

113

```

114

115

### Custom Constructor and Representer

116

117

```python

118

import yaml

119

120

class Point(yaml.YAMLObject):

121

"""2D point with custom YAML representation."""

122

123

yaml_tag = '!Point'

124

yaml_loader = yaml.SafeLoader

125

yaml_dumper = yaml.SafeDumper

126

yaml_flow_style = True # Use flow style: !Point [x, y]

127

128

def __init__(self, x, y):

129

self.x = x

130

self.y = y

131

132

def __repr__(self):

133

return f"Point({self.x}, {self.y})"

134

135

@classmethod

136

def from_yaml(cls, loader, node):

137

"""Construct Point from YAML sequence [x, y]."""

138

if isinstance(node, yaml.SequenceNode):

139

x, y = loader.construct_sequence(node)

140

return cls(x, y)

141

elif isinstance(node, yaml.MappingNode):

142

data = loader.construct_mapping(node)

143

return cls(data['x'], data['y'])

144

else:

145

raise yaml.constructor.ConstructorError(

146

None, None, f"Expected sequence or mapping, got {node.id}", node.start_mark

147

)

148

149

@classmethod

150

def to_yaml(cls, dumper, data):

151

"""Represent Point as YAML sequence [x, y]."""

152

return dumper.represent_sequence(cls.yaml_tag, [data.x, data.y])

153

154

# Test custom Point serialization

155

points = [Point(1, 2), Point(3.5, -1.0), Point(0, 0)]

156

157

yaml_output = yaml.dump(points, Dumper=yaml.SafeDumper)

158

print(yaml_output)

159

# Output:

160

# - !Point [1, 2]

161

# - !Point [3.5, -1.0]

162

# - !Point [0, 0]

163

164

loaded_points = yaml.load(yaml_output, Loader=yaml.SafeLoader)

165

print(loaded_points) # [Point(1, 2), Point(3.5, -1.0), Point(0, 0)]

166

```

167

168

### Complex Custom Object

169

170

```python

171

import yaml

172

from datetime import datetime

173

174

class Task(yaml.YAMLObject):

175

"""Task object with complex nested data."""

176

177

yaml_tag = '!Task'

178

yaml_loader = yaml.SafeLoader

179

yaml_dumper = yaml.SafeDumper

180

181

def __init__(self, title, description="", priority="medium",

182

due_date=None, completed=False, tags=None):

183

self.title = title

184

self.description = description

185

self.priority = priority

186

self.due_date = due_date

187

self.completed = completed

188

self.tags = tags or []

189

self.created_at = datetime.now()

190

191

def __repr__(self):

192

return f"Task('{self.title}', priority='{self.priority}', completed={self.completed})"

193

194

@classmethod

195

def from_yaml(cls, loader, node):

196

"""Construct Task from YAML mapping."""

197

data = loader.construct_mapping(node, deep=True)

198

199

# Handle datetime string conversion

200

if 'due_date' in data and isinstance(data['due_date'], str):

201

data['due_date'] = datetime.fromisoformat(data['due_date'])

202

if 'created_at' in data and isinstance(data['created_at'], str):

203

data['created_at'] = datetime.fromisoformat(data['created_at'])

204

205

return cls(**data)

206

207

@classmethod

208

def to_yaml(cls, dumper, data):

209

"""Represent Task as YAML mapping."""

210

# Convert datetime objects to ISO strings for YAML compatibility

211

task_data = {

212

'title': data.title,

213

'description': data.description,

214

'priority': data.priority,

215

'completed': data.completed,

216

'tags': data.tags,

217

'created_at': data.created_at.isoformat()

218

}

219

220

if data.due_date:

221

task_data['due_date'] = data.due_date.isoformat()

222

223

return dumper.represent_mapping(cls.yaml_tag, task_data)

224

225

# Create task list

226

tasks = [

227

Task("Write documentation", "Complete API documentation", "high",

228

datetime(2024, 1, 15), tags=["docs", "api"]),

229

Task("Fix bug #123", "Memory leak in parser", "critical",

230

datetime(2024, 1, 10), tags=["bug", "parser"]),

231

Task("Add tests", "Unit tests for new features", "medium",

232

tags=["tests", "quality"])

233

]

234

235

# Serialize to YAML

236

yaml_output = yaml.dump(tasks, Dumper=yaml.SafeDumper, default_flow_style=False)

237

with open('tasks.yaml', 'w') as f:

238

f.write(yaml_output)

239

240

# Load back from YAML

241

with open('tasks.yaml', 'r') as f:

242

loaded_tasks = yaml.load(f, Loader=yaml.SafeLoader)

243

244

print(f"Loaded {len(loaded_tasks)} tasks")

245

for task in loaded_tasks:

246

print(f" {task}")

247

```

248

249

### Multiple Loaders and Dumpers

250

251

```python

252

import yaml

253

254

class Config(yaml.YAMLObject):

255

"""Configuration object that works with multiple loaders."""

256

257

yaml_tag = '!Config'

258

# Register with multiple loaders/dumpers

259

yaml_loader = [yaml.SafeLoader, yaml.FullLoader]

260

yaml_dumper = [yaml.SafeDumper, yaml.Dumper]

261

262

def __init__(self, **kwargs):

263

self.settings = kwargs

264

265

def __getitem__(self, key):

266

return self.settings[key]

267

268

def __setitem__(self, key, value):

269

self.settings[key] = value

270

271

def __repr__(self):

272

return f"Config({self.settings})"

273

274

@classmethod

275

def from_yaml(cls, loader, node):

276

"""Construct Config from YAML mapping."""

277

settings = loader.construct_mapping(node, deep=True)

278

return cls(**settings)

279

280

@classmethod

281

def to_yaml(cls, dumper, data):

282

"""Represent Config as YAML mapping."""

283

return dumper.represent_mapping(cls.yaml_tag, data.settings)

284

285

# Usage with different loaders

286

config_yaml = """

287

!Config

288

database:

289

host: localhost

290

port: 5432

291

name: myapp

292

features:

293

- authentication

294

- logging

295

- metrics

296

debug: true

297

"""

298

299

# Works with SafeLoader

300

config1 = yaml.load(config_yaml, Loader=yaml.SafeLoader)

301

print(f"SafeLoader: {config1}")

302

303

# Works with FullLoader

304

config2 = yaml.load(config_yaml, Loader=yaml.FullLoader)

305

print(f"FullLoader: {config2}")

306

307

# Both produce same result

308

print(f"Same config: {config1.settings == config2.settings}")

309

```

310

311

### Inheritance with YAML Objects

312

313

```python

314

import yaml

315

316

class Vehicle(yaml.YAMLObject):

317

"""Base vehicle class."""

318

319

yaml_tag = '!Vehicle'

320

yaml_loader = yaml.SafeLoader

321

yaml_dumper = yaml.SafeDumper

322

323

def __init__(self, make, model, year):

324

self.make = make

325

self.model = model

326

self.year = year

327

328

def __repr__(self):

329

return f"{self.__class__.__name__}({self.make} {self.model} {self.year})"

330

331

class Car(Vehicle):

332

"""Car subclass with additional properties."""

333

334

yaml_tag = '!Car'

335

336

def __init__(self, make, model, year, doors=4):

337

super().__init__(make, model, year)

338

self.doors = doors

339

340

class Motorcycle(Vehicle):

341

"""Motorcycle subclass with additional properties."""

342

343

yaml_tag = '!Motorcycle'

344

345

def __init__(self, make, model, year, engine_cc=None):

346

super().__init__(make, model, year)

347

self.engine_cc = engine_cc

348

349

# Create vehicle fleet

350

vehicles = [

351

Car("Toyota", "Camry", 2023, doors=4),

352

Motorcycle("Honda", "CBR600RR", 2022, engine_cc=600),

353

Vehicle("Generic", "Unknown", 2020)

354

]

355

356

# Serialize with specific tags

357

yaml_output = yaml.dump(vehicles, Dumper=yaml.SafeDumper)

358

print(yaml_output)

359

# Output:

360

# - !Car

361

# doors: 4

362

# make: Toyota

363

# model: Camry

364

# year: 2023

365

# - !Motorcycle

366

# engine_cc: 600

367

# make: Honda

368

# model: CBR600RR

369

# year: 2022

370

# - !Vehicle

371

# make: Generic

372

# model: Unknown

373

# year: 2020

374

375

# Load back with correct types

376

loaded_vehicles = yaml.load(yaml_output, Loader=yaml.SafeLoader)

377

for vehicle in loaded_vehicles:

378

print(f"{type(vehicle).__name__}: {vehicle}")

379

```