14

First, a disclaimer: I'm not too familiar with YAML. I'm trying to parse a YAML doc into Key Value Pairs (don't worry about how I'm doing it. I've got that bit handled)

My file used to look something like:

world:
     people:
          name:Suzy
          address:chez-bob

Then, someone went and changed it.

world:
     people:
          name:!$uzy
          address:chez-bob

And I get this parse error:

yaml.constructor.ConstructorError: could not determine a constructor for the tag '!$uzy'

What does this even mean? How would I go about getting it to just interpret !$ as just two characters? I just want a dictionary of string keys and values! Also, editing the yaml files is not an option. Problem must be fixed in the code using the parser.

inspectorG4dget
  • 110,290
  • 27
  • 149
  • 241
Rokujolady
  • 939
  • 2
  • 8
  • 15
  • So yes, I realize now that the ! tells it to construct a $uzy, nevertheless, the content of the file isn't my concern. Handling it in a graceful fashion is. What I want it to do is treat !$uzy as text. – Rokujolady Nov 08 '12 at 01:24
  • wrap it in single or double quotes. '!$uzy' or "!$uzy". That will ensure the string is treated like a string. With great irony, i came to this question because I need to do the opposite - i need the constructor. So all efforts are not lost. – nelsonenzo Mar 24 '18 at 22:25

3 Answers3

16

Exclamation mark is a prefix for YAML tags. The parser has to implement a constructor for it by the tag name. There are some default tags like !!bool, !!int, etc. and even some Python specific tags like !!python/tuple.

You can define your own constructors and even constructors for multiple tags caught by a prefix. By defining the prefix to '', you can catch all the tags and ignore them. You can return the tag and its value from the constructor to just treat it all as text.

>>> import yaml
>>> def default_ctor(loader, tag_suffix, node):
...     print loader
...     print tag_suffix
...     print node
...     return tag_suffix + ' ' + node.value
...
>>> yaml.add_multi_constructor('', default_ctor)
>>> yaml.load(y)
<yaml.loader.Loader object at 0xb76ce8ec>
!$uzy
ScalarNode(tag=u'!$uzy', value=u'')
{'world': {'people': {'name': '!$uzy', 'address': 'chez-bob'}}}
>>>
Jason
  • 9,408
  • 5
  • 36
  • 36
kichik
  • 33,220
  • 7
  • 94
  • 114
  • Could you please provide a more details example, have a similar issue [here](http://stackoverflow.com/questions/43081483/write-a-constructor-to-handle-specific-tags-in-ruamel-or-pyyaml) – askb Mar 29 '17 at 00:20
  • Specifying prefix as `''` didn't work for me, instead I had to set it to `'!'`. – dmitryb Aug 28 '18 at 10:52
6

If a value starts with "!", you must enclose the value in single or double quotes; otherwise it is interpreted as a YAML tag.

world:
     people:
          name: "!$uzy"
          address: chez-bob
Kirill Simonov
  • 256
  • 1
  • 3
3

This is actually a bug in PyYAML. It interprets the : in name:!$uzy as a key/value separator, but it should only do so if : is followed by a space, or if the preceding scalar (name) is quoted. The follow up error is that the exclamation mark, which should be allowed in the middle of a scalar, gets incorrectly interpreted as being at the beginning of a scalar and hence introducing a tag.

The value for key people is the string name:!$uzy address:chez-bob and that is handled correctly in other parsers (including the Python package ruamel.yaml of which I am the author).

Anthon
  • 69,918
  • 32
  • 186
  • 246
  • It looks like a fix for this has been merged in the sources for PyYAML early 2017. But since the last release was August 2016 and the one before that March 2014, it might take a few months/years before this is available on PyPI. – Anthon Mar 14 '17 at 12:20