以编程方式更新YAML文件

2024-04-27 12:29:57 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个Python dict,它来自于用通常的

yaml.load(stream)

我想以编程方式更新YAML文件,并提供一个要更新的路径,如下所示:

组1,选项1,选项11,值

并将生成的dict再次保存为yaml文件。考虑到路径是动态的(假设用户可以通过我使用Cmd创建的一个简单CLI输入路径),我面临着更新一个dictional的问题。

有什么想法吗?

谢谢!

更新 让我更具体地谈谈这个问题:问题在于更新字典的一部分,我事先不知道字典的结构。我正在处理一个项目,其中所有的配置都存储在YAML文件中,我想添加一个CLI,以避免手工编辑它们。这是一个示例YAML文件,使用PyYaml加载到字典(config dict)中:

config:
 a-function: enable
 b-function: disable
 firewall:
  NET:
   A:
    uplink: enable
    downlink: enable
   B:
    uplink: enable
    downlink: enable
  subscriber-filter:
   cancellation-timer: 180
 service:
  copy:
   DS: enable
  remark:
   header-remark:
    DSC: enable
    remark-table:
 port:
  linkup-debounce: 300
  p0:
   mode: amode
  p1:
   mode: bmode
  p2:
   mode: amode
  p3:
   mode: bmode

我已经用Cmd创建了CLI,它甚至可以自动完成。用户可以提供如下行:

config port p1 mode amode

所以,我需要编辑:

配置dict['config']['port']['p1']['mode']并将其设置为'amode'。然后,使用yaml.dump()再次创建文件。另一条可能的路线是:

config a-function enable

所以config dict['config']['a-function']必须设置为'enable'。

我的问题是更新字典的时候。如果Python将值作为引用传递会很容易:只需遍历dict,直到找到正确的值并保存它。实际上,这就是我正在做的命令自动完成。但我不知道怎么更新。

希望我现在能解释得更好!

提前谢谢。


Tags: 文件路径configyamlcli字典portenable
3条回答

使用python-benedict来实现这一点非常简单,这是一个坚实的python dict子类,支持多种格式的IO操作,包括yaml

安装:pip install python-benedict

您可以直接从yaml文件初始化它:

from benedict import benedict

f = 'data.yaml'
d = benedict.from_yaml(f)
d['Pipi'] = {'score': 1000000, 'city': 'Stockholm'}

# benedict supports keypath (dot syntax by default),
# so it's possible to update nested values easily:
d['Pipi.score'] = 2000000
print(d['Pipi']) # -> {'score': 2000000, 'city': 'Stockholm'}

d.to_yaml(filepath=f)

这里是库存储库和文档: https://github.com/fabiocaccamo/python-benedict

实际上,解决方案遵循简单的模式:加载-修改-转储:

在播放之前,请确保已安装pyyaml:

$ pip install pyyaml

testyaml.py

import yaml
fname = "data.yaml"

dct = {"Jan": {"score": 3, "city": "Karvina"}, "David": {"score": 33, "city": "Brno"}}

with open(fname, "w") as f:
    yaml.dump(dct, f)

with open(fname) as f:
    newdct = yaml.load(f)

print newdct
newdct["Pipi"] = {"score": 1000000, "city": "Stockholm"}

with open(fname, "w") as f:
    yaml.dump(newdct, f)

结果data.yaml

$ cat data.yaml
David: {city: Brno, score: 33}
Jan: {city: Karvina, score: 3}
Pipi: {city: Stockholm, score: 1000000}

更新似乎是pyyaml的不足之处。对于在(a)ppend模式下打开的文件,您甚至不能毫无例外地使用yaml.load。现在,对于复杂的字典来说,这可能有点乏味,但是如果每个添加的项都代表一个单独的大小写或文档,您可以像处理其他文本文件一样处理它。

newinfo = {"Pipi": {"score": 100000, "city": "Stockholm"}}
with open(fname, "a") as f:
     sep = "\n" # For distinct documents use "\n...\n" as separator

     # Pay attention to where you put the separator. 
     # if the file exists and is in traditional format place at 
     # beginning of string. else place at the end.

     infostring = "{}".format(newinfo)
     f.write(infostring + sep)

虽然这不一定有助于值更新,但它允许文件更新。您还可以研究在文件上使用json.dump。我知道它是在YAML中,但是除非您在YAML中使用python对象存储特性,否则格式基本上是兼容的。

对于与操作系统无关的回车字符分配方法,请记住使用OS.linesep。

祝你好运。希望这有帮助。

相关问题 更多 >