【问题标题】:How to insert a comment line to YAML in Python using ruamel.yaml?如何使用 ruamel.yaml 在 Python 中向 YAML 插入注释行?
【发布时间】:2017-04-03 22:44:39
【问题描述】:

我有一个这样的结构,我想使用 ruamel.yaml 添加注释行:

xyz:
  a: 1    # comment 1
  b: 2

test1:
  test2:
    test3: 3

现在,我想插入注释行(不是 eol_cmets)使其看起来像这样:

xyz:
  a: 1    # comment 1
  b: 2

# before test1 (top level)
test1:
  # before test2
  test2:
    # after test2
    test3: 3

我知道,我可以使用 ruamel.yaml 添加 eol_cmets,但我没有找到添加整个注释行的方法。

【问题讨论】:

    标签: python ruamel.yaml


    【解决方案1】:

    ruamel.yaml<=0.12.18 中确实没有在键之前的行中插入注释的功能,但是有一个在结构开头设置注释的功能:.yaml_set_start_comment。这样您就可以设置要添加的三个 cmets 中的两个:

    import sys
    import ruamel.yaml
    
    yaml_str = """\
    xyz:
      a: 1    # comment 1
      b: 2
    
    test1:
      test2:
        test3: 3
    """
    
    data = ruamel.yaml.round_trip_load(yaml_str)
    data['test1'].yaml_set_start_comment('before test2', indent=2)
    data['test1']['test2'].yaml_set_start_comment('after test2', indent=4)
    ruamel.yaml.round_trip_dump(data, sys.stdout)
    

    给予:

    xyz:
      a: 1    # comment 1
      b: 2
    
    test1:
      # before test2
      test2:
        # after test2
        test3: 3
    

    实际上在xyztest1 的值之间有一个由空行构成的“注释”,但是如果您将注释附加到该结构,然后在test1 之前插入一个新键,则不会随心所欲地出现。因此要做的是在键test1 之前显式插入注释。您可以往返加载您的预期输出,以查看内部 Comment 的外观:

    yaml_str_out = """\
    xyz:
      a: 1    # comment 1
      b: 2
    
    # before test1 (top level)
    test1:
      # before test2
      test2:
        # before test3
        test3: 3
    """
    test = ruamel.yaml.round_trip_load(yaml_str_out)
    print(test.ca)
    

    给出(包装这个以便于查看):

    Comment(comment=None,
            items={'test1': [None, 
                            [CommentToken(value='# before test1 (top level)\n')], 
                            None, 
                            [CommentToken(value='# before test2\n')]]})
    

    如您所见,# before test2 被认为是键后的注释。 并且做test['test1'].yaml_set_start_comment('xxxxx', indent=2)会 没有任何效果,因为与 test1 关联的评论推翻了这一点,并且 # xxxxx不会显示在转储中。

    有了这些信息和一些背景知识,我改编了yaml_set_start_comment() 的一些代码(假设原始导入和yaml_str):

    def yscbak(self, key, before=None, indent=0, after=None, after_indent=None):
        """
        expects comment (before/after) to be without `#` and possible have multiple lines
        """
        from ruamel.yaml.error import Mark
        from ruamel.yaml.tokens import CommentToken
    
        def comment_token(s, mark):
            # handle empty lines as having no comment
            return CommentToken(('# ' if s else '') + s + '\n', mark, None)
    
        if after_indent is None:
            after_indent = indent + 2
        if before and before[-1] == '\n':
            before = before[:-1]  # strip final newline if there
        if after and after[-1] == '\n':
            after = after[:-1]  # strip final newline if there
        start_mark = Mark(None, None, None, indent, None, None)
        c = self.ca.items.setdefault(key, [None, [], None, None])
        if before:
            for com in before.split('\n'):
                c[1].append(comment_token(com, start_mark))
        if after:
            start_mark = Mark(None, None, None, after_indent, None, None)
            if c[3] is None:
                c[3] = []
            for com in after.split('\n'):
                c[3].append(comment_token(com, start_mark))
    
    if not hasattr(ruamel.yaml.comments.CommentedMap, 
                   'yaml_set_comment_before_after_key'):
        ruamel.yaml.comments.CommentedMap.yaml_set_comment_before_after_key = yscbak
    
    
    data = ruamel.yaml.round_trip_load(yaml_str)
    data.yaml_set_comment_before_after_key('test1', 'before test1 (top level)',
                                           after='before test2', after_indent=2)
    data['test1']['test2'].yaml_set_start_comment('after test2', indent=4)
    ruamel.yaml.round_trip_dump(data, sys.stdout)
    

    然后得到:

    xyz:
      a: 1    # comment 1
      b: 2
    
    # before test1 (top level)
    test1:
      # before test2
      test2:
        # after test2
        test3: 3
    

    hasattr 的测试是确保当它被添加到 ruamel.yaml 时你不会覆盖这样的函数

    顺便说一句:所有 cmets 都是 YAML 中的行尾 cmets,在其中一些 cmets 之前可能只有有效的 YAML。

    【讨论】:

    • 方法yaml_set_comment_before_after_key()ruamel.yaml>=0.13.0中可用
    • yaml_set_comment_before_after_key() 方法可以满足我的需要。非常感谢。但我发现:当我有一个像key: <br> - Entry1 <br> - Entry2 这样的多行列表时,后注释被插入到键之后,而不是列表之后。
    • 如果您有一个多行列表,您可以使用列表中的索引作为键(即整数 0、1 等)将其插入到列表/序列元素中。见here。再一次:只要确保yaml_add_eol_comment 在列表中被调用。
    • @Anthon 引用的示例现在似乎可以查看here。虽然我不确定第 142 行是否仍然是正确的行。
    • 嗯。 test_map_set_comment_before_and_after_non_first_key_00 明确引用了这个答案。
    猜你喜欢
    • 2021-12-24
    • 2021-11-21
    • 1970-01-01
    • 2016-11-10
    • 1970-01-01
    • 2022-01-14
    • 2017-07-03
    • 2021-06-25
    • 2015-09-08
    相关资源
    最近更新 更多