| | 238 | }}} |
| | 239 | |
| | 240 | |
| | 241 | === Constructors, representers, resolvers === |
| | 242 | |
| | 243 | You may define your own application-specific tags. The easiest way to do it is |
| | 244 | to define a subclass of '''`yaml.YAMLObject`''': |
| | 245 | {{{{ |
| | 246 | #!python |
| | 247 | >>> yaml.load(""" |
| | 248 | ... --- !Monster |
| | 249 | ... name: Cave spider |
| | 250 | ... hp: [2,6] # 2d6 |
| | 251 | ... ac: 16 |
| | 252 | ... attacks: [BITE, HURT] |
| | 253 | ... """) |
| | 254 | |
| | 255 | Monster(name='Cave spider', hp=[2, 6], ac=16, attacks=['BITE', 'HURT']) |
| | 256 | |
| | 257 | >>> print yaml.dump(Monster( |
| | 258 | ... name='Cave lizard', hp=[3,6], ac=16, attacks=['BITE','HURT'])) |
| | 259 | |
| | 260 | !Monster |
| | 261 | ac: 16 |
| | 262 | attacks: [BITE, HURT] |
| | 263 | hp: [3, 6] |
| | 264 | name: Cave lizard |
| | 265 | }}}} |
| | 266 | |
| | 267 | '''`yaml.YAMLObject`''' uses metaclass magic to register a constructor, which |
| | 268 | transforms a YAML node to a class instance, and a representer, which serializes |
| | 269 | a class instance to a YAML node. |
| | 270 | |
| | 271 | If you don't want to use metaclasses, you may register your constructors |
| | 272 | and representers using the functions '''`yaml.add_constructor`''' and |
| | 273 | '''`yaml.add_representer`'''. For instance, you may want to add a constructor |
| | 274 | and a representer for the following '''`Dice`''' class: |
| | 275 | {{{ |
| | 276 | #!python |
| | 277 | >>> class Dice(tuple): |
| | 278 | ... def __new__(cls, a, b): |
| | 279 | ... return tuple.__new__(cls, [a, b]) |
| | 280 | ... def __repr__(self): |
| | 281 | ... return "%sd%s" % self |
| | 282 | |
| | 283 | >>> print Dice(3,6) |
| | 284 | 3d6 |
| | 285 | }}} |
| | 286 | |
| | 287 | First we define a representer, that convert a dice object to scalar node |
| | 288 | with the tag `!dice` and register it. |
| | 289 | {{{ |
| | 290 | #!python |
| | 291 | >>> def dice_representer(dumper, data): |
| | 292 | ... return dumper.represent_scalar(u'!dice', unicode(data)) |
| | 293 | |
| | 294 | >>> yaml.add_representer(Dice, dice_representer) |
| | 295 | }}} |
| | 296 | |
| | 297 | Now you may dump an instance of the `Dice` object: |
| | 298 | {{{ |
| | 299 | #!python |
| | 300 | >>> print yaml.dump({'gold': Dice(10,6)}) |
| | 301 | {gold: !dice '10d6'} |
| | 302 | }}} |
| | 303 | |
| | 304 | Let us add the code to construct a Dice object: |
| | 305 | {{{ |
| | 306 | #!python |
| | 307 | >>> def dice_constructor(loader, node): |
| | 308 | ... value = loader.construct_scalar(node) |
| | 309 | ... a, b = map(int, value.split('d')) |
| | 310 | ... return Dice(a, b) |
| | 311 | |
| | 312 | >>> yaml.add_constructor(u'!dice', dice_constructor) |
| | 313 | }}} |
| | 314 | |
| | 315 | Then you may load a `Dice` object as well: |
| | 316 | {{{ |
| | 317 | #!python |
| | 318 | >>> print yaml.load(""" |
| | 319 | ... initial hit points: !dice 8d4 |
| | 320 | ... """) |
| | 321 | |
| | 322 | {'initial hit points': 8d4 |
| | 323 | }}} |
| | 324 | |
| | 325 | You might want to not specify the tag `!dice` everywhere. There is a way |
| | 326 | to teach PyYAML that any untagged plain scalar that looks like XdY has |
| | 327 | the implicit tag `!dice`. Use '''`add_implicit_resolver`''': |
| | 328 | {{{ |
| | 329 | #!python |
| | 330 | >>> import re |
| | 331 | >>> pattern = re.compile(r'^\d+d\d+$') |
| | 332 | >>> yaml.add_implicit_resolver(u'!dice', pattern) |
| | 333 | }}} |
| | 334 | |
| | 335 | Now you don't have to specify the tag to define a `Dice` object: |
| | 336 | {{{ |
| | 337 | #!python |
| | 338 | >>> print yaml.dump({'treasure': Dice(10,20)}) |
| | 339 | |
| | 340 | {treasure: 10d20} |
| | 341 | |
| | 342 | >>> print yaml.load(""" |
| | 343 | ... damage: 5d10 |
| | 344 | ... """) |
| | 345 | |
| | 346 | {'damage': 5d10} |