parse multiple custom tags in ruamel.yaml

Viewed 794

Background: In my computer vision program, users are modifying YAML files to supply annotation functions (lists items are functions, sequences of scalars are arguments). The workflow is open/modify YAML file > save yaml > parse and execute functions > show results, and start over or save. There can be multiple instances of the same tool in one file, differentiated through an "id" scalar. Since the annotations are saved after every iteration, they also needs to be control for overwrite behavior, which is also controlled with a scalar.

- create_annotation: 
  id: my_id1
  overwrite: false
  tool: mask
- create_annotation: 
  id: my_id2
  overwrite: true
  tool: mask

Problem I would like to start using some custom YAML tags to separate instance/overwrite statements from the function arguments (seems more intuitive). I use ruamel.yaml and I have looked at similar questions where the tags are "registered" through a class and then added to the constructor. However, I am not sure hoe to retrieve the tag information after loading (it's not in the ordereddict/CommentedMap).

Questions

  1. How do I construct and load a custom tag from a node and where do I find it?
  2. Can I have multiple tags per node?

Expected outcome

I was hoping for something like this:

- create_annotation: !id my_id1 !ow false 
  tool: mask
- create_annotation: !id my_id2 !ow true 
  tool: mask

Which would be parsed as:

{tool: mask,
 id: my_id1,
 overwrite: false}
1 Answers

To start with your second question: no, you cannot have multiple tags per node. The node properties are specified in the YAML documentation

[96]    c-ns-properties(n,c) ::=   ( c-ns-tag-property
                                     ( s-separate(n,c) c-ns-anchor-property )? )
                                 | ( c-ns-anchor-property
                                     ( s-separate(n,c) c-ns-tag-property )? ) 

and that doesn't allow for multiple tag properties. Normally that tag is used to make a specific class instance, and I am not sure how multiple such instances would need to be instantiated or combine.

So what happens if you parse your example is that the !id will be taken as a tag for the scalar string my_id1 !ow false, which just happens to have something that looks like a tag as part of the string:

import ruamel.yaml

yaml_str = """\
- create_annotation: !id my_id1 !ow false 
  tool: mask
- create_annotation: !id my_id2 !ow true 
  tool: mask
"""

yaml = ruamel.yaml.YAML()
data = yaml.load(yaml_str)
scalar = data[0]['create_annotation']
print(f'scalar: "{scalar}", id: "{scalar.tag.value}"')

which gives:

scalar: "my_id1 !ow false", id: "!id"

As you can see the tag is part of the value node that it precedes and is not stored the CommentedMap it is part of.

Related