如何使用 python 更新 yaml 文件

How to update yaml file using python

我有一个包含以下内容的 some.yaml 文件。

    init_config: {}
    instances:
        - host: <IP>
          username: <username>
          password: <password>

yaml 文件应按如下方式解析和更新。

    init_config: {}
    instances:
        - host: 1.2.3.4
          username: Username
          password: Password

如何解析值并适当地更新它们?

这里是使用 PyYaml 的示例。据我了解,您有类似 yaml 格式的模板,您必须用实际值替换尖括号中的位置。

import yaml

s = """
    init_config: {}
    instances:
        - host: <IP>
          username: <username>
          password: <password>
"""

dict_obj = yaml.load(s) # loads string in internal data structure - dict
dict_obj['instances'][0]['host'] = 'localhost' # change values
print yaml.dump(dict_obj) # dumps dict to yaml format back

这就是我如何从我提到的上述文件中读取、解析和根据需要更新的方式。

import yaml

fname = "some.yaml"

stream = open(fname, 'r')
data = yaml.load(stream)

data['instances'][0]['host'] = '1.2.3.4'
data['instances'][0]['username'] = 'Username'
data['instances'][0]['password'] = 'Password'

with open(fname, 'w') as yaml_file:
    yaml_file.write( yaml.dump(data, default_flow_style=False))

我不知道你是否需要 YAML。除了使用 YAML 标签外,您似乎对 YAML 文档没有兴趣。那么为什么不使用 Jinja2 或一些模板语言呢?

from jinja2 import Template

tmpl = Template(u'''\
    init_config: {}
    instances:
         - host: {{ IP }}
           username: {{ username }}
           password: {{ password }}
''')

print tmpl.render(
     IP=u"1.2.3.4",
     username=u"Username",
     password=u"Password"
)

我不知道这是否是个好主意,但如果您只需要获取更改了某些字段的文件,则不需要实际解析 YAML 文档,可以直接受益于模板语言.


奖励: 用例

我处理过非常复杂的 YAML 文档,其中有未知的标签

...
  propertiesIDs: { 1, 2, 3, 4 }
  globalID: !myapplication.InterfaceID &primitiveID

replication: !myapplication.replication
  beginDate: 2012-09-10T20:00:03
  endDate: 2020-09-10T20:00:04
  replicant_uuid:
    ? 17169504-B6AB-11E4-8437-36E258BB2172
    ? 206B5842-B6AB-11E4-AAC3-36E258BB2172
...

对此文档执行有效解析既困难又耗时。我只需要填充一些值,YAML 就会发送到第三方应用程序。因此,与其解析 YAML 或尝试直接使用 pyyaml 生成有效文档,不如直接通过模板生成它更简单(更省时,更不容易出错)。此外,模板语言可以很容易地与循环一起使用来填充动态大小的字段。

ruamel.yaml 包经过专门增强(由我从 PyYAML 开始)以进行这种往返、编程、更新。

如果您以(请注意我删除了多余的初始空格)开头:

init_config: {}
instances:
    - host: <IP>              # update with IP
      username: <username>    # update with user name
      password: <password>    # update with password

和运行:

import ruamel.yaml

file_name = 'input.yaml'
config, ind, bsi = ruamel.yaml.util.load_yaml_guess_indent(open(file_name))

instances = config['instances']
instances[0]['host'] = '1.2.3.4'
instances[0]['username'] = 'Username'
instances[0]['password'] = 'Password'

yaml = ruamel.yaml.YAML()
yaml.indent(mapping=ind, sequence=ind, offset=bsi) 
with open('output.yaml', 'w') as fp:
    yaml.dump(config, fp)

输出将是:

init_config: {}
instances:
    - host: 1.2.3.4           # update with IP
      username: Username      # update with user name
      password: Password      # update with password

映射键的顺序(hostusernamepassword)、样式和评论 将保留,无需任何进一步具体操作。

不用猜测缩进和块序列缩进,您可以进行手动传统加载,并自己设置缩进值:

yaml = ruamel.yaml.YAML()
yaml.indent(mapping=6, sequence=4)
with open(file_name) as fp:
    config = yaml.load(fp)

如果你看看这个答案的历史,你可以看到如何使用更有限的 PyYAML 来做到这一点,比如 API。

以下是我如何为开发、生产、阶段等生成 docker-crane 模板...

  1. mkdir crane_templates
  2. touch crane_templates/init.py
  3. 用nano添加模板内容crane_templates/some.yaml
  4. 纳米crane_gen.py

--- crane_gen.py ---

#!/usr/bin/env python
from jinja2 import Environment, PackageLoader

env = Environment(loader=PackageLoader('crane_templates', './'))
tmpl = env.get_template('crane.yaml.tmpl')

result = tmpl.render(
     IP=u"1.2.3.4",
     username=u"Username",
     password=u"Password"
)

5。 python crane_gen.py > result.yaml

答案灵感来自@MariusSiuram