使 yaml/ruamel.yaml 始终内联转储列表

Vit*_*tov 4 python yaml

如何使 PyYAML 或 ruamel.yaml始终内联转储列表?无论是从现有文件加载的 YAML 元素列表还是从我的代码添加的 YAML 元素列表。

当我从文件加载 YAML 然后转储它时,它会转储内联列表(请参见下面的代码)。但是,如果我向现有父对象添加一个带有列表的新 YAML 元素,然后转储它,它会转储非内联列表。

我尝试使用 Python 3.7.3、PyYAML 5.1.1 和 ruamel.yaml 0.15.97。

>>> import ruamel.yaml
>>> ruamel.yaml.__version__
'0.15.97'
>>> raw_yaml = """
... users:
...   user1:
...     comment: comment1
...     keys: ["user1 key1", "user1 key2"]
...     groups: ["user1 group1", "user1 group2"]
... """
>>> yaml = ruamel.yaml.round_trip_load(raw_yaml, preserve_quotes=True)
>>> dump = ruamel.yaml.round_trip_dump(yaml, default_flow_style=None)
>>> print(dump)
users:
  user1:
    comment: comment1
    keys: ["user1 key1", "user1 key2"]
    groups: ["user1 group1", "user1 group2"]
# So far so good, 'keys' and 'groups' are dumped inline
>>> yaml['users']['user2'] = {}
>>> yaml['users']['user2']['comment'] = 'comment2'
>>> yaml['users']['user2']['keys'] = []
>>> yaml['users']['user2']['keys'].append('user2 key1')
>>> yaml['users']['user2']['keys'].append('user2 key2')
>>> yaml['users']['user2']['groups'] = []
>>> yaml['users']['user2']['groups'].append('user2 group1')
>>> yaml['users']['user2']['groups'].append('user2 group2')
>>> dump = ruamel.yaml.round_trip_dump(
...     yaml, default_flow_style=False, default_style="'",
...     indent=2, block_seq_indent=2)
# desired result:
# users:
#   user1:
#     comment: comment1
#     keys: ["user1 key1", "user1 key2"]
#     groups: ["user1 group1", "user1 group2"]
#   user2:
#     comment: comment2
#     keys: ["user2 key1", "user2 key2"]
#     groups: ["user2 group1", "user2 group2"]
>>> print(dump)
'users':
    'user1':
        'comment': 'comment1'
        'keys': ["user1 key1", "user1 key2"]
        'groups': ["user1 group1", "user1 group2"]
    'user2':
        'comment': 'comment2'
        'keys':
            - 'user2 key1'
            - 'user2 key2'
        'groups':
            - 'user2 group1'
            - 'user2 group2'
Run Code Online (Sandbox Code Playgroud)

参见上文,当我转储刚刚加载的 YAML (users['user1']) 时,列表是内联的:

keys: ["user1 key1", "user1 key2"]
groups: ["user1 group1", "user1 group2"]
Run Code Online (Sandbox Code Playgroud)

但是当我添加 users['user2'] 然后转储整个 YAML 对象时,列表不是内联的:

'keys':
    - 'user2 key1'
    - 'user2 key2'
'groups':
    - 'user2 group1'
    - 'user2 group2'
Run Code Online (Sandbox Code Playgroud)

如果我设置 'default_flow_style=True',它会内联转储整个元素:

'user2': {'comment': 'comment2', 'keys': ['user2 key1', 'user2 key2'], 'groups': [ 'user2 group1', 'user2 group2']}
Run Code Online (Sandbox Code Playgroud)

这不是我想要的。我希望将“评论”、“键”和“组”转储到单独的行上,并内联列表:

user2:
  comment: comment2
  keys: ["user2 key1", "user2 key2"]
  groups: ["user2 group1", "user2 group2"]
Run Code Online (Sandbox Code Playgroud)

对于 PyYaml 来说,情况几乎是一样的。

我希望列表始终被内联转储(对于用户['user1'])。我怎么做?

Ant*_*hon 5

您所说的内联在 YAML 文档中称为流式。ruamel.yaml 中都有一个选项 ( default_flow_style) 可以全局使用所有流样式、所有块样式或叶节点流样式(其余块样式)。这是 PyYAML 的旧行为。

然而,这不是您想要的,因为它会影响序列和映射,而您只需要映射。

ruamel.yaml,在往返模式下,可以保留文件中出现的单个流样式/块样式,因此您可以让例如离开节点及其父节点为流样式,或所有序列(Python列表)流样式和所有映射(Python 字典)块样式后者当然只有在映射不在序列“下方”时才有效,因为您不能在流样式中使用块样式。

如果您从头开始或使用具有正确格式的加载 YAML,只需确保任何添加的列表实际上是特殊的内部列表子类,用于ruamel.yaml保留注释、样式等并在添加的列表上设置流样式

import sys
import ruamel.yaml
from ruamel.yaml.scalarstring import DoubleQuotedScalarString as dq

def L(*l):
   ret = ruamel.yaml.comments.CommentedSeq(l)
   ret.fa.set_flow_style()
   return ret   


raw_yaml = """\
users:
   user1:
    comment: comment1
    keys: ["user1 key1", "user1 key2"]
    groups: ["user1 group1", "user1 group2"]
"""

yaml = ruamel.yaml.YAML()
# yaml.indent(mapping=4, sequence=4, offset=2)
yaml.preserve_quotes = True
data = yaml.load(raw_yaml)
data['users']['user2'] = {}
data['users']['user2']['comment'] = 'comment2'
data['users']['user2']['keys'] = L()
data['users']['user2']['keys'].append('user2 key1')
data['users']['user2']['keys'].append('user2 key2')
data['users']['user2']['groups'] = L('abc', L('user2 group1', dq('user2 group2')))
# print(data)
yaml.dump(data, sys.stdout)
Run Code Online (Sandbox Code Playgroud)

这使:

users:
  user1:
    comment: comment1
    keys: ["user1 key1", "user1 key2"]
    groups: ["user1 group1", "user1 group2"]
  user2:
    comment: comment2
    keys: [user2 key1, user2 key2]
    groups: [abc, [user2 group1, "user2 group2"]]
Run Code Online (Sandbox Code Playgroud)

由于您希望每个列表都表示为流样式序列,因此还可以通过子类化表示器来更改所有列表的表示器,但上面的内容为您提供了更好的控制,并允许您准确地在流中流动那些您想要的列表-风格。


ruamel.yaml(和 PyYAML)使用流接口,而 print(dump(data))不是dump(data, sys.stdout)使转储完成到内存中的缓冲区,然后流式传输该缓冲区。这在时间和空间上都是低效率的,不要这样做

  • 如果此答案解决了您的问题,请考虑通过单击答案旁边的 ✔(复选标记)来*接受*它。这就是其他人无需阅读评论就知道您的问题已解决的方式。它还会更改列表中问题和答案的外观。如果出现更好的答案,您可以随时更改已接受的答案。 (2认同)