【问题标题】:Insert a comment before a key in Ruamel.yaml在 Ruamel.yaml 中的键前插入注释
【发布时间】:2016-10-01 04:21:34
【问题描述】:

我正在使用 Ruamel Python 库以编程方式编辑人工编辑的 YAML 文件。

我很难理解如何将 cmets 插入结构化数据。

我有一些数据:

a:
  b: banana
  c: apple
  d: orange

我想添加评论和新密钥:

a:
  b: banana
  c: apple
  d: orange
  # This is my comment
  e: pear

是否可以使用ruamel.yaml 做到这一点,如果可以,怎么做?

【问题讨论】:

    标签: ruamel.yaml


    【解决方案1】:

    是的,这是可能的,因为您可以通过往返来检查:

    import sys
    import ruamel.yaml
    
    with open('your_input.yaml') as fp:
        data = ruamel.yaml.round_trip_load(yaml_str)
    ruamel.yaml.round_trip_dump(data, sys.stdout)
    

    打印的输出将与您的输入相匹配,因此以某种方式将注释插入到结构的data 层次结构中,保存并在转储时写出。

    ruamel.yaml 中,cmets 附加到listsdicts 的包装类,您可以使用print(type(data['a']) 检查:它是CommentedMap(来自ruamel.yaml.comment.py)。 a 值的注释信息挂起属性_yaml_comment,您可以通过属性ca 访问该属性:

    cm = data['a']
    print(cm.ca)
    

    给予:

    items={'e': [None, [CommentToken(value='# This is my comment\n')], None, None]})
    

    这表明评论与键 e 相关联,即在评论之后。不幸的是,CommentToken 不能像它所代表的那样通过调用它来创建(即CommentToken(value='# This is my comment\n')),它需要更多的工作,因为它至少需要一个开始Mark

    没有创建此类评论的“帮助”例程,但通过查看CommentedMap 及其基类CommentedBase,您可以得出以下¹:

    import sys
    import ruamel.yaml
    
    if not hasattr(ruamel.yaml.comments.CommentedMap, "yaml_set_comment_before_key"):
        def my_yaml_set_comment_before_key(self, key, comment, column=None,
                                           clear=False):
            """
            append comment to list of comment lines before key, '# ' is inserted
                before the comment
            column: determines indentation, if not specified take indentation from
                    previous comment, otherwise defaults to 0
            clear: if True removes any existing comments instead of appending
            """
            key_comment = self.ca.items.setdefault(key, [None, [], None, None])
            if clear:
                key_comment[1] = []
            comment_list = key_comment[1]
            if comment:
                comment_start = '# '
                if comment[-1] == '\n':
                    comment = comment[:-1]  # strip final newline if there
            else:
                comment_start = '#'
            if column is None:
                if comment_list:
                     # if there already are other comments get the column from them
                    column = comment_list[-1].start_mark.column
                else:
                    column = 0
            start_mark = ruamel.yaml.error.Mark(None, None, None, column, None, None)
            comment_list.append(ruamel.yaml.tokens.CommentToken(
                comment_start + comment + '\n', start_mark, None))
            return self
    
        ruamel.yaml.comments.CommentedMap.yaml_set_comment_before_key = \
            my_yaml_set_comment_before_key
    

    使用此方法扩展CommentedMap,您可以这样做:

    yaml_str = """\
    a:
      b: banana
      c: apple
      d: orange
      e: pear
    """
    
    data = ruamel.yaml.round_trip_load(yaml_str)
    cm = data['a']
    
    cm.yaml_set_comment_before_key('e', "This is Alex' comment", column=2)
    cm.yaml_set_comment_before_key('e', 'and this mine')
    ruamel.yaml.round_trip_dump(data, sys.stdout)
    

    得到:

    a:
      b: banana
      c: apple
      d: orange
      # This is Alex' comment
      # and this mine one
      e: pear
    

    除非你在评论中阅读,否则无法查询cm是哪一列 注释应该在,以将其与键 e 对齐(该列在写出数据结构时确定)。您可能想存储一个特殊值 (-1?) 并尝试在输出期间确定这一点,但在流出时您几乎没有上下文。您当然可以将列确定/设置为嵌套级别(1)并将其乘以缩进(您给round_trip_dump的缩进,默认为2

    cmets 工具是为了在往返过程中保存,而不是最初用于修改或插入新的,因此不能保证接口是稳定的。考虑到这一点,请确保您围绕yaml_set_comment_before_key() 创建一个或一组例程来进行更改,因此如果界面发生更改,您只需更新一个模块(能够附加评论的能力将不会消失,但是这样做的方法可能会改变)


    ¹ 也许不是你,但由于我是ruamel.yaml 的作者,我应该能够在文档不足的代码中找到自己的方式。

    【讨论】:

    • 再次非常感谢@Anthon。但是,您在 key_comment 开头的行中留下了一个调试行,其中“'e'”应该是“key”。此外,如果您想要一个空白的注释行,该方法会强制您在行尾添加一个空格。所以我更喜欢更改它,以便用户需要在 # 之后立即指定空格。
    • 与其说是调试,不如先得到功能,然后再转化为方法,从而忘记了on value->parameter的转换。谢谢你抓住那个。我还更新了以测试注释不是空字符串(这会导致异常),但现在换行符之前没有空格(即它直接跟在# 之后)。感谢您了解这一点。
    • 未来访问者,对于版本 ruamel.yaml==0.15.94 使用 ruamel.yaml.yaml_set_comment_before_after_key('e', before="This is Alex' comment", after="This is another comment after the key")
    • @Anthon 您如何看待在您的答案中编辑一个简短的注释以反映 Drake 刚刚在评论中发布的内容?
    • @DavidZ 我一直在推迟更新我的 ruamel.yaml 答案以进行评论处理,因为必须更改代码(在未来的版本中)。这将意味着不兼容的更改(人们应该注意,因为 ruamel.yaml 是 1.0 之前的版本,并且上述例程不是公共 API)。而且我不想多次更新答案,如果我有时间的话,我首先会把它花在 ruamel.yaml 上的(无偿)工作上。所以你的建议不错,但我现在不会这样做
    猜你喜欢
    • 2017-04-03
    • 2016-07-13
    • 2019-10-21
    • 1970-01-01
    • 2021-12-24
    • 2021-06-25
    • 2013-02-06
    • 2015-05-02
    • 2013-05-06
    相关资源
    最近更新 更多