如何使用 ruamel.yaml rtsc 模式?

How can I use the ruamel.yaml rtsc mode?

我一直致力于创建一个基于 ruamel.yaml 的 YAML 重新格式化程序(您可以看到 here)。 我目前使用的是 0.17.20 版本。

清理评论和白色space 很困难。我想:

为了更接近于实现这一目标,我有一个自定义发射器 class,我在其中扩展 write_comment 以在使用 super().write_comment(...) 写入之前调整注释。但是,发射器不知道接下来是哪个键或项目,因为注释通常作为 post 注释附加。

当我研究 ruamel.yaml 代码以弄清楚如何做到这一点时,我发现 rtsc 模式(往返拆分评论)看起来很棒,因为它将 EOLCommentBlankLineCommentFullLineComment 而不是将它们混为一谈。

据我所知,ParserScanner 已经过调整以捕获评论。所以,加载是(主要是?)用这个“NEWCMNT”实现来实现的。但是 Emitter.write_comment 期望 CommentToken 而不是注释行号,因此转储还不起作用。

如果我更新 Emitter.write_comment 方法,是否足以完成转储?或者还有什么可能是必要的?在我的一次尝试中,我 运行 变成 ScannedComments.assign_eol() 中的 sys.exit - 还需要什么来完成它?

PS:我通常不会问如何在 Whosebug 上进行协作,但这不是错误报告或功能请求,我 trying/failing 使用新的(未记录的) ) 功能,所以我在这里而不是 sourceforge 归档。

rtsc 正在进行中 cq 工作已开始但未完成。它的内部几乎肯定会改变。

您指出的三点中有两点相对容易实现:

  • 如果列在值的末尾位置之前,则将每个注释的列设置为 0(通过递归遍历类似于 的已加载数据结构)行,您将在值和列

    之间得到一个 space
  • 同时做上一点的递归。获取每个评论值并执行以下操作:

       value = '\n'.join(line.strip() for line in value.splitlines()
       while '\n\n\n' in value:
            value = value.replace('\n\n\n', '\n\n')
    

以下元素的缩进比较困难,取决于 数据结构等鉴于这些是整行注释,我建议 您对生成的 YAML 文档进行一些后处理:

  1. 找到整行评论,收集整行评论直到下一行 不是全行注释(即一些“真实的”YAML)。由于整行评论 如果应用了以前的内容,则在列 [0] 中,您不必 跟踪您是否在 (multi-line) 文字或折叠标量字符串中 其中一行恰好以 #

    开头
  2. 确定space个数 在真正的 YAML 之前并将这些应用于整行注释。

import sys
import ruamel.yaml

yaml_str = """\
   # the following is a example YAML doc
a:
- b: 42


       # collapse multiple empty lines 
  c: |
    # this is not a comment
    it is the first line of a block style literal scalar
    processing this gobbles a newline which doesn't go into a comment
 # that is unless you have a (dedented) comment directly following


  d: 42       # and some non-full line comment
 

  e:   # another one
 # and some more comments to align
     f: glitter in the dark near the Tannhäuser gate
"""
 

def redo_comments(d):
    def do_one(comment):
        if not comment:
            return
        comment.column = 0
        value = '\n'.join(line.strip() for line in comment.value.splitlines()) + '\n'
        while '\n\n\n' in value:
            value = value.replace('\n\n\n', '\n\n')
        comment.value = value

    def do_values(v):
        for x in v:
            for comment in x:
                do_one(comment)

    def do_loc(v):
        if v is None:
            return
        do_one(v[0])
        if not v[1]:
            return
        for comment in v[1]:
            do_one(comment)

    if isinstance(d, dict):
        do_loc(d.ca.comment)
        do_values(d.ca.items.values())
        for val in d.values():
            redo_comments(val)
    elif isinstance(d, list):
        do_values(d.ca.items.values())
        for elem in d:
            redo_comments(elem)

def realign_full_line_comments(s):
    res = []
    buf = []
    for line in s.splitlines(True):
        if not buf:
            if line and line[0] == '#':
                buf.append(line)
            else:
                res.append(line)
        else:
            if line[0] in '#\n':
                buf.append(line)
            else:
                # YAML line, determine indent
                count = 0
                while line[count] == ' ':
                    count += 1
                    if count > len(line):
                        break  # superfluous?
                indent = ' ' * count
                for cline in buf:
                    if cline[0] == '\n':  # empty
                        res.append(cline)
                    else:
                        res.append(indent + cline)
                buf = []
                res.append(line)
    return ''.join(res)


 
yaml = ruamel.yaml.YAML()
# yaml.indent(mapping=4, sequence=4, offset=2)
# yaml.preserve_quotes = True
data = yaml.load(yaml_str)
redo_comments(data)
yaml.dump(data, sys.stdout, transform=realign_full_line_comments)

给出:

# the following is a example YAML doc
a:
- b: 42

  # collapse multiple empty lines
  c: |
    # this is not a comment
    it is the first line of a block style literal scalar
    processing this gobbles a newline which doesn't go into a comment
  # that is unless you have a (dedented) comment directly following

  d: 42 # and some non-full line comment

  e: # another one
    # and some more comments to align
    f: glitter in the dark near the Tannhäuser gate