以编程方式更新YAML文件

时间:2014-05-28 14:25:41

标签: python yaml

我使用通常的

读取YAML文件的Python词典
yaml.load(stream)

我希望以编程方式更新YAML文件,并给出要更新的路径,如:

组1,选项1,option11,值

并将生成的dict再次保存为yaml文件。考虑到路径是动态的,我面临着更新二项决定的问题(假设用户能够通过我使用Cmd创建的简单CLI进入路径)。

有什么想法吗?

谢谢!

更新 让我对这个问题更加具体:问题在于更新字典的一部分,我事先并不知道结构。我正在开发一个项目,其中所有配置都存储在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

所以,我需要编辑:

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

config a-function enable

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

我的问题是更新字典时。如果Python传递值作为引用将很容易:只需遍历dict直到找到正确的值并保存它。实际上,这就是我为Cmd自动完成所做的事情。但我不知道如何进行更新。

希望我现在更好地解释自己了!

提前致谢。

5 个答案:

答案 0 :(得分:16)

实际上,解决方案遵循简单模式:load - modify - dump:

在播放之前,请确保安装了pygame:

$ 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}

答案 1 :(得分:1)

使用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

答案 2 :(得分:0)

更新似乎是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-object存储功能,否则格式基本兼容。

对于与操作系统无关的方法进行字符赋值,请记住使用os.linesep。

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

答案 3 :(得分:0)

尝试这种方法,我用于更新yaml或json文件。 def update_dictionary_recursively(dictionary, key, value, key_separator="."): """Update given字典with the givenand值`。

if dictionary contains value as dict E.g. {key1:value1, key2:{key3, {key4:value4}}} and you have to
update key4 then `key` should be given as `key2.key3.key4`.

If dictionary contains value as list E.g. {key1:{key2:[{key3:valie3}, {key4:value4}]}} and you have to update
key4 then `key` should be given as `key1.key2[1].key4`.

:param dictionary: Dictionary that is to be updated.
:type dictionary: dict
:param key: Key with which the dictionary is to be updated.
:type key: str
:param value: The value which will be used to update the key in dictionary.
:type value: object
:param key_separator: Separator with which key is separated.
:type key_separator str
:return: Return updated dictionary.
:rtype: dict
"""
index = key.find(key_separator)
if index != -1:
    current_key = key[0:index]
    key = key[index + 1:]
    try:
        if '[' in current_key:
            key_index = current_key.split('[')
            current_key = key_index[0]
            list_index = int(key_index[1].strip(']'))
            dictionary[current_key][list_index] = update_dictionary_recursively(
                dictionary[current_key][list_index], key, value, key_separator)
        else:
            dictionary[current_key] = update_dictionary_recursively(dictionary[current_key],
                                                                                    key, value, key_separator)
    except (KeyError, IndexError):
        return dictionary
else:
    if '[' in key:
        key_index = key.split('[')
        list_index = int(key_index[1].strip(']'))
        if list_index > len(dictionary) - 1:
            return dictionary
        dictionary[list_index] = value
    else:
        if key not in dictionary:
            return dictionary
        dictionary[key] = value
return dictionary

`

答案 4 :(得分:0)

如果您可以使用JSON进行操作,那就容易得多。