在ruamel.yaml中的迭代期间获取评论

时间:2018-08-27 15:58:36

标签: python yaml ruamel.yaml

在遍历YAML对象时如何获得注释

yaml = YAML()

with open(path, 'r') as f:
    yaml_data = yaml.load(f)

for obj in yaml_data:
    # how to get the comments here?

这是源数据(Ansible剧本)

---
- name: gather all complex custom facts using the custom module
  hosts: switches
  gather_facts: False
  connection: local
  tasks:
    # There is a bug in ansible 2.4.1 which prevents it loading
    # playbook/group_vars
    - name: ensure we're running a known working version
      assert:
        that:
          - 'ansible_version.major == 2'
          - 'ansible_version.minor == 4'

在Anthon评论之后,这是我发现在子节点中访问评论的方式(需要完善):

for idx, obj in enumerate(yaml_data):
    for i, item in enumerate(obj.items()):
        pprint(yaml_data[i].ca.items)

2 个答案:

答案 0 :(得分:0)

您没有指定输入内容,但是由于您的代码需要obj和 不是密钥,我假设您的YAML的根级别是序列而不是映射。 如果要在每个元素(即nr 1the last)之后获取评论,可以执行以下操作:

import ruamel.yaml

yaml_str = """\
- one  # nr 1
- two 
- three # the last
"""

yaml = ruamel.yaml.YAML()

data = yaml.load(yaml_str)

for idx, obj in enumerate(data):
    comment_token = data.ca.items.get(idx)
    if comment_token is None:
        continue
    print(repr(comment_token[0].value))

给出:

'# nr 1\n'
'# the last\n'

您可能要去除开头的八叉戟和尾随的换行符。

请注意,这适用于当前版本(0.15.61),但是 无法保证它可能不会改变。

答案 1 :(得分:0)

使用 example from Anthonan issue in ruamel.yaml on sourceforge,这里有一组方法可以让您检索(几乎 - 见下文)文档中的所有评论:

from ruamel.yaml import YAML
from ruamel.yaml.comments import CommentedMap, CommentedSeq

# set attributes
def get_comments_map(self, key):
    coms = []
    comments = self.ca.items.get(key)
    if comments is None:
        return coms
    for token in comments:
        if token is None:
            continue
        elif isinstance(token, list):
            coms.extend(token)
        else:
            coms.append(token)
    return coms

def get_comments_seq(self, idx):
    coms = []
    comments = self.ca.items.get(idx)
    if comments is None:
        return coms
    for token in comments:
        if token is None:
            continue
        elif isinstance(token, list):
            coms.extend(token)
        else:
            coms.append(token)
    return coms

setattr(CommentedMap, 'get_comments', get_comments_map)
setattr(CommentedSeq, 'get_comments', get_comments_seq)

# load string
yaml_str = """\
- name: gather all complex custom facts using the custom module
  hosts: switches
  gather_facts: False
  connection: local
  tasks:
    # There is a bug in ansible 2.4.1 which prevents it loading
    # playbook/group_vars
    - name: ensure we're running a known working version
      assert:
        that:
          - 'ansible_version.major == 2'
          - 'ansible_version.minor == 4'
"""
yml = YAML(typ='rt')
data = yml.load(yaml_str)

def walk_data(data):
    if isinstance(data, CommentedMap):
        for k, v in data.items():
            print(k, [ comment.value for comment in data.get_comments(k)])
            if isinstance(v, CommentedMap) or isinstance(v, CommentedSeq):
                walk_data(v)
    elif isinstance(data, CommentedSeq):
        for idx, item in enumerate(data):
            print(idx, [ comment.value for comment in data.get_comments(idx)])
            if isinstance(item, CommentedMap) or isinstance(item, CommentedSeq):
                walk_data(item)

walk_data(data)

输出如下:

0 []
name []
hosts []
gather_facts []
connection []
tasks ['# There is a bug in ansible 2.4.1 which prevents it loading\n', '# playbook/group_vars\n']
0 []
name []
assert []
that []
0 []
1 []

不幸的是,有两个是我遇到的一个问题s,但此方法未涵盖:

  1. 您会注意到 tasks 的注释中没有前导 \n。因此,此方法无法区分与 tasks 位于同一行或下一行的注释。由于 CommentToken.start_mark.line 仅包含注释的绝对行,因此它可能可以与 tasks 的行进行比较。但是,我还没有找到在加载的数据中检索与 tasks 关联的行的方法。
  2. 我似乎还没有找到一种方法来检索文档开头的评论。因此,任何初始评论都需要使用一种方法来发现,而不是在 yaml 阅读器之外检索它们。但是,与问题 #1 相关,这些头部注释包含在其他注释的绝对行数中。 要在文档的头部添加注释,您需要按照 {{ 3}}。