如何对齐 ruamel.yaml 中的 eol 注释,使它们都在同一列中

How do I align the eol comments in ruamel.yaml so that they are all in the same column

我目前正在使用一些配置值自动生成 YAML 文件。 有时这些值附有注释,注释会添加到 YAML 文件中。

简单示例

import sys
from ruamel.yaml import CommentedMap, YAML

top = CommentedMap()
top['sub_key1'] = data = CommentedMap()

data['a'] = 1
data['b'] = 'asdf'
data['c'] = 3.3333
data.yaml_add_eol_comment('comment 1', 'a')
data.yaml_add_eol_comment('comment 2', 'b')
data.yaml_add_eol_comment('comment 3', 'c')

top['sub_key2'] = data = CommentedMap()

data['a'] = 'long text'
data['b'] = 'an even longer text'
data.yaml_add_eol_comment('comment 4', 'a')
data.yaml_add_eol_comment('comment 5', 'b')

YAML().dump(top, sys.stdout)

这有效并按预期输出

sub_key1:
  a: 1  # comment 1
  b: asdf # comment 2
  c: 3.3333 # comment 3
sub_key2:
  a: long text  # comment 4
  b: an even longer text # comment 5

但是我真的很想像这样对齐注释(或者在值后有两个空格更好)。

sub_key1:
  a: 1      # comment 1
  b: asdf   # comment 2
  c: 3.3333 # comment 3
sub_key2:
  a: long text           # comment 4
  b: an even longer text # comment 5

添加eol注释时无法使用column参数,因为column从一开始就是绝对的,我不知道

有什么方法可以在创建后对齐评论吗?

您可以在不设置列的情况下进行转储,读回生成的 YAML 并进行分析 评论结构 ( data['sub_key1']ca.items ) 以获得最大的列值,然后 使用该值再转储一次。

或者当您的数据加载了评论时,只需将评论中的所有列值设置为 最大值或最大值加一得到最长值后两个空格:

import sys
import io
from ruamel.yaml import CommentedMap, YAML

yaml = YAML()
top = CommentedMap()
top['sub_key1'] = data = CommentedMap()

data['a'] = 1
data['b'] = 'asdf'
data['c'] = 3.3333
data.yaml_add_eol_comment('comment 1', 'a')
data.yaml_add_eol_comment('comment 2', 'b')
data.yaml_add_eol_comment('comment 3', 'c')

top['sub_key2'] = data = CommentedMap()

data['a'] = 'long text'
data['b'] = 'an even longer text'
data.yaml_add_eol_comment('comment 4', 'a')
data.yaml_add_eol_comment('comment 5', 'b')

buf = io.BytesIO()
yaml.dump(top, buf)

top = yaml.load(buf.getvalue())

def align_comments(d, extra=0):
    def align_one(d, extra=0):
        comments = d.ca.items.values()
        if not comments:
            return
        max = -1
        for comment in comments:
            if comment[2].column > max:
                max = comment[2].column
        for comment in comments:
            comment[2].column = max + extra

    if isinstance(d, dict):
        align_one(d, extra=extra)
        for val in d.values():
            align_comments(val, extra=extra)
    elif isinstance(d, list):
        align_one(d, extra=extra)
        for elem in d:
            align_comments(elem, extra=extra)


align_comments(top, extra=1)

yaml.dump(top, sys.stdout)

给出:

sub_key1:
  a: 1       # comment 1
  b: asdf    # comment 2
  c: 3.3333  # comment 3
sub_key2:
  a: long text            # comment 4
  b: an even longer text  # comment 5

确保固定您正在使用的 ruamel.yaml 版本。由于这些内部结构 会改变。

@Anthon: 为了更好的可读性,我稍微修改了你的例子。 感谢您的帮助

def align_comments(d, extra_indent=0):

    is_dict = isinstance(d, dict)
    if not is_dict and not isinstance(d, list):
        return None

    comments = d.ca.items.values()
    if comments:
        max_col = max(map(lambda x: x[2].column, comments), default=0)
        for comment in comments:
            comment[2].column = max_col + extra_indent

    for element in (d.values() if is_dict else d):
        align_comments(element, extra_indent=extra_indent)
    return None

用法:

buf = io.BytesIO()
yaml.dump(top, buf)

top = yaml.load(buf.getvalue())
align_comments(top, extra=1)

yaml.dump(top, sys.stdout)