Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding comments to YAML produced with PyYaml

Tags:

I'm creating Yaml documents from my own python objects using PyYaml. for example my object:

class MyObj(object):
    name = "boby"
    age = 34

becomes:

boby:
   age: 34

So far so good.

But I have not found a way to programmatically add comments to the produced yaml so it will look like:

boby:       # this is the name
   age: 34  # in years

Looking at PyYaml documentation and also at the code, I found no way of doing so.

Any suggestions?

like image 520
Periodic Maintenance Avatar asked Nov 22 '12 17:11

Periodic Maintenance


People also ask

How do I keep comments in YAML file?

The shortcut key combination for commenting YAML blocks is Ctrl+Q. Select the block. Use “CTRL + /” on Linux and Windows and “CMD+/” for Mac operating system.

Is PyYAML same as YAML?

YAML is a data serialization format designed for human readability and interaction with scripting languages. PyYAML is a YAML parser and emitter for Python. PyYAML features a complete YAML 1.1 parser, Unicode support, pickle support, capable extension API, and sensible error messages.


1 Answers

You probably have some representer for the MyObj class, as by default dumping ( print(yaml.dump(MyObj())) ) with PyYAML will give you:

!!python/object:__main__.MyObj {}

PyYAML can only do one thing with the comments in your desired output: discard them. If you would read that desired output back in, you end up with a dict containing a dict ({'boby': {'age': 34}}, you would not get a MyObj() instance because there is no tag information)

The enhanced version for PyYAML that I developed (ruamel.yaml) can read in YAML with comments, preserve the comments and write comments when dumping. If you read your desired output, the resulting data will look (and act) like a dict containing a dict, but in reality there is more complex data structure that can handle the comments. You can however create that structure when ruamel.yaml asks you to dump an instance of MyObj and if you add the comments at that time, you will get your desired output.

from __future__ import print_function

import sys
import ruamel.yaml
from ruamel.yaml.comments import CommentedMap


class MyObj():
    name = "boby"
    age = 34

    def convert_to_yaml_struct(self):
        x = CommentedMap()
        a = CommentedMap()
        x[data.name] = a
        x.yaml_add_eol_comment('this is the name', 'boby', 11)
        a['age'] = data.age
        a.yaml_add_eol_comment('in years', 'age', 11)
        return x

    @staticmethod
    def yaml_representer(dumper, data, flow_style=False):
        assert isinstance(dumper, ruamel.yaml.RoundTripDumper)
        return dumper.represent_dict(data.convert_to_yaml_struct())


ruamel.yaml.RoundTripDumper.add_representer(MyObj, MyObj.yaml_representer)

ruamel.yaml.round_trip_dump(MyObj(), sys.stdout)

Which prints:

boby:      # this is the name
  age: 34  # in years

There is no need to wait with creating the CommentedMap instances until you want to represent the MyObj instance. I would e.g. make name and age into properties that get/set values from/on the approprate CommentedMap. That way you could more easily add the comments before the yaml_representer static method is called to represent the MyObj instance.

like image 156
Anthon Avatar answered Oct 23 '22 15:10

Anthon