Search code examples
python-3.xyamlpyyamlruamel.yaml

Add a comment in list element in ruamel.yaml


I am dynamically adding elements in a list in a YAML file using Python and I would like to add a comment alongside each of the elements I am adding. The following are all desired formats:

flow_style_example:
  - [a, b, c] # first list
  - [d, e] # second list

block_style_example:
  - - a  # first list side comment
    - b
    - c
  # second list top comment
  - - d
    - e

list_of_elements_side_comment:
  - a # foo
  - b # bar

list_of_elements_top_comment:
  # comment 1
  - a
  # comment 2
  - b

For any of the above I have yet to figure out how to properly create the respective CommentToken entries, especially when it comes to marks (how to determine the line and col of what was just added?)

How can I achieve any of the above functionalities?


Solution

  • Instead of writing in your question what you would appreciate, it would have been more useful to see your program, to determine what you were doing wrong.

    Because you mix and match indentation styles, you cannot get the exact indentation you want in one dump.

    import sys
    import ruamel.yaml
    CS = ruamel.yaml.comments.CommentedSeq  # defaults to block style
    CM = ruamel.yaml.comments.CommentedMap  # defaults to block style
    
    def FS(x):  # flow style list
       res = CS(x)
       res.fa.set_flow_style()
       return res
    
    
    yaml = ruamel.yaml.YAML()
    yaml.indent(sequence=4, offset=2)
    
    lst = CS()
    lst.append(FS(['a', 'b', 'c']))
    lst.append(FS(['d', 'e']))
    lst.yaml_add_eol_comment("first list", 0, 0)
    lst.yaml_add_eol_comment("second list\n\n", 1)
    data = CM(flow_style_example=lst)
    
    lst = CS()
    data['block_style_example'] = lst
    lst.append(CS(['a', 'b', 'c']))
    lst[0].yaml_add_eol_comment("first list side comment", 0, 0)
    lst.append(CS(['d', 'e']))
    lst.yaml_set_comment_before_after_key(1, "second list top comment", 2)
    
    lst = CS(['a', 'b'])
    lst.yaml_add_eol_comment("foo", 0, 0)
    lst.yaml_add_eol_comment("bar\n\n", 1)
    data["list_of_elements_side_comment"] = lst
    data.yaml_set_comment_before_after_key("list_of_elements_side_comment", "\n")
    
    lst = CS(['a', 'b'])
    lst.yaml_set_comment_before_after_key(0, "comment 1", 2)
    lst.yaml_set_comment_before_after_key(1, "comment 2", 2)
    data["list_of_elements_top_comment"] = lst
    
    
    yaml.dump(data, sys.stdout)
    

    which gives:

    flow_style_example:
      - [a, b, c] # first list
      - [d, e] # second list
    
    block_style_example:
      -   - a # first list side comment
          - b
          - c
      # second list top comment
      -   - d
          - e
    
    list_of_elements_side_comment:
      - a # foo
      - b # bar
    
    list_of_elements_top_comment:
      # comment 1
      - a
      # comment 2
      - b
    

    The comment handling for CommentedSeq is very similar to that of CommentedMap: comments are currently stored as a dict where the sequence index fulfills the same function as the mapping key, hence the use of yaml_set_comment_before_after_key on a sequence/list.

    The above uses internals of ruamel.yaml, which might change without notice cq. with notice, but without you noticing. Therefore (be prepared to) fix the version number of ruamel.yaml you install.