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

registration.mddocs/

0

# Registration Functions

1

2

Functions for registering custom constructors, representers, and resolvers to extend YAML processing capabilities. These functions allow you to customize how PyYAML handles specific data types and tags.

3

4

## Capabilities

5

6

### Constructor Registration

7

8

Functions for registering custom constructors that convert YAML nodes to Python objects.

9

10

```python { .api }

11

def add_constructor(tag: str, constructor: Callable, Loader=None) -> None:

12

"""

13

Register a constructor for a specific YAML tag.

14

15

Parameters:

16

- tag: YAML tag to handle (e.g., '!custom', 'tag:yaml.org,2002:python/tuple')

17

- constructor: Function that takes (loader, node) and returns Python object

18

- Loader: Loader class to register with (defaults to global registration)

19

20

The constructor function signature:

21

def constructor(loader, node) -> Any

22

"""

23

24

def add_multi_constructor(tag_prefix: str, multi_constructor: Callable, Loader=None) -> None:

25

"""

26

Register a multi-constructor for YAML tags with a common prefix.

27

28

Parameters:

29

- tag_prefix: YAML tag prefix to handle (e.g., '!python/', 'tag:example.com,2002:')

30

- multi_constructor: Function that takes (loader, tag_suffix, node) and returns Python object

31

- Loader: Loader class to register with (defaults to global registration)

32

33

The multi-constructor function signature:

34

def multi_constructor(loader, tag_suffix, node) -> Any

35

"""

36

```

37

38

### Representer Registration

39

40

Functions for registering custom representers that convert Python objects to YAML nodes.

41

42

```python { .api }

43

def add_representer(data_type: type, representer: Callable, Dumper=None) -> None:

44

"""

45

Register a representer for a specific Python type.

46

47

Parameters:

48

- data_type: Python type to handle (e.g., MyClass, datetime.date)

49

- representer: Function that takes (dumper, data) and returns YAML node

50

- Dumper: Dumper class to register with (defaults to global registration)

51

52

The representer function signature:

53

def representer(dumper, data) -> Node

54

"""

55

56

def add_multi_representer(data_type: type, multi_representer: Callable, Dumper=None) -> None:

57

"""

58

Register a multi-representer for a type and its subclasses.

59

60

Parameters:

61

- data_type: Base Python type to handle (will also handle subclasses)

62

- multi_representer: Function that takes (dumper, data) and returns YAML node

63

- Dumper: Dumper class to register with (defaults to global registration)

64

65

The multi-representer function signature:

66

def multi_representer(dumper, data) -> Node

67

"""

68

```

69

70

### Resolver Registration

71

72

Functions for registering custom resolvers that determine YAML tags for values.

73

74

```python { .api }

75

def add_implicit_resolver(tag: str, regexp: Pattern[str], first=None, Loader=None, Dumper=None) -> None:

76

"""

77

Register an implicit resolver that assigns tags based on value patterns.

78

79

Parameters:

80

- tag: YAML tag to assign when pattern matches

81

- regexp: Compiled regular expression pattern to match against scalar values

82

- first: Set of possible first characters (optimization hint)

83

- Loader: Loader class to register with (defaults to global registration)

84

- Dumper: Dumper class to register with (defaults to global registration)

85

"""

86

87

def add_path_resolver(tag: str, path: Iterable[Any], kind=None, Loader=None, Dumper=None) -> None:

88

"""

89

Register a path resolver that assigns tags based on document path.

90

91

Parameters:

92

- tag: YAML tag to assign when path matches

93

- path: Sequence describing the path in the document structure

94

- kind: Node kind to match (ScalarNode, SequenceNode, MappingNode)

95

- Loader: Loader class to register with (defaults to global registration)

96

- Dumper: Dumper class to register with (defaults to global registration)

97

"""

98

```

99

100

## Usage Examples

101

102

### Custom Constructor Registration

103

104

```python

105

import yaml

106

import re

107

from datetime import datetime

108

109

# Constructor for custom date format

110

def date_constructor(loader, node):

111

"""Construct datetime from custom date format."""

112

value = loader.construct_scalar(node)

113

return datetime.strptime(value, '%Y-%m-%d %H:%M:%S')

114

115

# Register constructor for custom tag

116

yaml.add_constructor('!datetime', date_constructor, Loader=yaml.SafeLoader)

117

118

# Test custom constructor

119

yaml_input = """

120

created: !datetime 2024-01-15 14:30:00

121

modified: !datetime 2024-01-16 09:15:30

122

"""

123

124

data = yaml.load(yaml_input, Loader=yaml.SafeLoader)

125

print(f"Created: {data['created']} (type: {type(data['created'])})")

126

print(f"Modified: {data['modified']} (type: {type(data['modified'])})")

127

```

128

129

### Multi-Constructor Registration

130

131

```python

132

import yaml

133

import importlib

134

135

def python_object_constructor(loader, tag_suffix, node):

136

"""Construct Python objects from module.class notation."""

137

if tag_suffix == 'object':

138

# Handle !!python/object:module.Class

139

class_name = loader.construct_scalar(node)

140

module_name, class_name = class_name.rsplit('.', 1)

141

module = importlib.import_module(module_name)

142

cls = getattr(module, class_name)

143

return cls()

144

elif tag_suffix == 'apply':

145

# Handle !!python/apply:function [args]

146

function_name = node.value[0].value

147

args = loader.construct_sequence(node.value[1])

148

module_name, func_name = function_name.rsplit('.', 1)

149

module = importlib.import_module(module_name)

150

func = getattr(module, func_name)

151

return func(*args)

152

else:

153

raise yaml.constructor.ConstructorError(

154

None, None, f"Unknown python tag suffix: {tag_suffix}", node.start_mark

155

)

156

157

# Register multi-constructor for python tags

158

yaml.add_multi_constructor('tag:yaml.org,2002:python/',

159

python_object_constructor,

160

Loader=yaml.UnsafeLoader)

161

162

# Test (UNSAFE - only for demonstration)

163

yaml_input = """

164

date_func: !!python/object:datetime.date

165

result: !!python/apply:datetime.date [2024, 1, 15]

166

"""

167

168

# This would work with UnsafeLoader (not recommended for untrusted input)

169

# data = yaml.load(yaml_input, Loader=yaml.UnsafeLoader)

170

```

171

172

### Custom Representer Registration

173

174

```python

175

import yaml

176

from decimal import Decimal

177

178

def decimal_representer(dumper, data):

179

"""Represent Decimal as a scalar with custom tag."""

180

return dumper.represent_scalar('!decimal', str(data))

181

182

def decimal_constructor(loader, node):

183

"""Construct Decimal from scalar value."""

184

value = loader.construct_scalar(node)

185

return Decimal(value)

186

187

# Register representer and constructor

188

yaml.add_representer(Decimal, decimal_representer, Dumper=yaml.SafeDumper)

189

yaml.add_constructor('!decimal', decimal_constructor, Loader=yaml.SafeLoader)

190

191

# Test custom Decimal handling

192

data = {

193

'price': Decimal('19.99'),

194

'tax_rate': Decimal('0.0825'),

195

'total': Decimal('21.64')

196

}

197

198

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

199

print("YAML output:")

200

print(yaml_output)

201

# Output:

202

# price: !decimal '19.99'

203

# tax_rate: !decimal '0.0825'

204

# total: !decimal '21.64'

205

206

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

207

print(f"Loaded price: {loaded_data['price']} (type: {type(loaded_data['price'])})")

208

```

209

210

### Multi-Representer Registration

211

212

```python

213

import yaml

214

from pathlib import Path

215

216

def path_representer(dumper, data):

217

"""Represent Path objects as strings with custom tag."""

218

return dumper.represent_scalar('!path', str(data))

219

220

def path_constructor(loader, node):

221

"""Construct Path from string value."""

222

value = loader.construct_scalar(node)

223

return Path(value)

224

225

# Register for Path and all its subclasses

226

yaml.add_multi_representer(Path, path_representer, Dumper=yaml.SafeDumper)

227

yaml.add_constructor('!path', path_constructor, Loader=yaml.SafeLoader)

228

229

# Test with different Path types

230

from pathlib import WindowsPath, PosixPath

231

232

data = {

233

'config_file': Path('/etc/myapp/config.yaml'),

234

'data_dir': Path('/var/lib/myapp'),

235

'log_file': Path('/var/log/myapp.log')

236

}

237

238

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

239

print("YAML output:")

240

print(yaml_output)

241

242

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

243

print(f"Config file: {loaded_data['config_file']} (type: {type(loaded_data['config_file'])})")

244

```

245

246

### Implicit Resolver Registration

247

248

```python

249

import yaml

250

import re

251

from ipaddress import IPv4Address, IPv6Address

252

253

# IPv4 address pattern

254

ipv4_pattern = re.compile(r'^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$')

255

256

def ipv4_constructor(loader, node):

257

value = loader.construct_scalar(node)

258

return IPv4Address(value)

259

260

def ipv4_representer(dumper, data):

261

return dumper.represent_scalar('!ipv4', str(data))

262

263

# Register IPv4 handling

264

yaml.add_constructor('!ipv4', ipv4_constructor, Loader=yaml.SafeLoader)

265

yaml.add_representer(IPv4Address, ipv4_representer, Dumper=yaml.SafeDumper)

266

267

# Register implicit resolver - automatically detect IPv4 addresses

268

yaml.add_implicit_resolver('!ipv4', ipv4_pattern, ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'],

269

Loader=yaml.SafeLoader, Dumper=yaml.SafeDumper)

270

271

# Test automatic IPv4 detection

272

yaml_input = """

273

server: 192.168.1.1

274

database: 10.0.0.5

275

client: not.an.ip.address

276

"""

277

278

data = yaml.load(yaml_input, Loader=yaml.SafeLoader)

279

print(f"Server: {data['server']} (type: {type(data['server'])})")

280

print(f"Database: {data['database']} (type: {type(data['database'])})")

281

print(f"Client: {data['client']} (type: {type(data['client'])})")

282

283

# Dump back to YAML

284

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

285

print("\nYAML output:")

286

print(yaml_output)

287

```

288

289

### Path Resolver Registration

290

291

```python

292

import yaml

293

294

def special_string_constructor(loader, node):

295

"""Constructor for special strings in specific locations."""

296

value = loader.construct_scalar(node)

297

return f"SPECIAL: {value}"

298

299

# Register constructor

300

yaml.add_constructor('!special', special_string_constructor, Loader=yaml.SafeLoader)

301

302

# Register path resolver - apply !special tag to strings at specific paths

303

yaml.add_path_resolver('!special', ['config', 'database', 'password'], str,

304

Loader=yaml.SafeLoader)

305

yaml.add_path_resolver('!special', ['secrets', None], str, # Any key under 'secrets'

306

Loader=yaml.SafeLoader)

307

308

# Test path-based tag resolution

309

yaml_input = """

310

config:

311

database:

312

host: localhost

313

password: secret123 # Will get !special tag

314

port: 5432

315

app:

316

name: MyApp

317

debug: true

318

secrets:

319

api_key: abc123 # Will get !special tag

320

token: xyz789 # Will get !special tag

321

normal_password: plain # Won't get !special tag

322

"""

323

324

data = yaml.load(yaml_input, Loader=yaml.SafeLoader)

325

print("Loaded data:")

326

print(f"Database password: {data['config']['database']['password']}")

327

print(f"API key: {data['secrets']['api_key']}")

328

print(f"Token: {data['secrets']['token']}")

329

print(f"Normal password: {data['normal_password']}")

330

```

331

332

### Complete Custom Type System

333

334

```python

335

import yaml

336

from typing import NamedTuple

337

from enum import Enum

338

339

class Priority(Enum):

340

LOW = 1

341

MEDIUM = 2

342

HIGH = 3

343

CRITICAL = 4

344

345

class Task(NamedTuple):

346

title: str

347

priority: Priority

348

completed: bool = False

349

350

# Priority enum handling

351

def priority_representer(dumper, data):

352

return dumper.represent_scalar('!priority', data.name.lower())

353

354

def priority_constructor(loader, node):

355

value = loader.construct_scalar(node)

356

return Priority[value.upper()]

357

358

# Task handling

359

def task_representer(dumper, data):

360

return dumper.represent_mapping('!task', {

361

'title': data.title,

362

'priority': data.priority,

363

'completed': data.completed

364

})

365

366

def task_constructor(loader, node):

367

data = loader.construct_mapping(node)

368

return Task(**data)

369

370

# Register all custom types

371

yaml.add_representer(Priority, priority_representer, Dumper=yaml.SafeDumper)

372

yaml.add_constructor('!priority', priority_constructor, Loader=yaml.SafeLoader)

373

yaml.add_representer(Task, task_representer, Dumper=yaml.SafeDumper)

374

yaml.add_constructor('!task', task_constructor, Loader=yaml.SafeLoader)

375

376

# Test complete type system

377

tasks = [

378

Task("Fix critical bug", Priority.CRITICAL, False),

379

Task("Write documentation", Priority.MEDIUM, True),

380

Task("Code review", Priority.HIGH, False)

381

]

382

383

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

384

print("YAML output:")

385

print(yaml_output)

386

387

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

388

print("Loaded tasks:")

389

for task in loaded_tasks:

390

print(f" {task.title}: {task.priority.name} ({'✓' if task.completed else '✗'})")

391

```