Loading and dumping multiple yaml files with ruamel.yaml (python)
使用 python 2 (atm) 和 ruamel.yaml 0.13.14 (RedHat EPEL)
我目前正在编写一些代码来加载 yaml 定义,但它们被拆分为多个文件。用户可编辑部分包含例如。
1 2 3 4 5 6 7 | users: xxxx1: timestamp: '2018-10-22 11:38:28.541810' << : *userdefaults xxxx2: << : *userdefaults timestamp: '2018-10-22 11:38:28.541810' |
默认值存储在另一个不可编辑的文件中:
1 2 3 4 | userdefaults: &userdefaults # Default values for user settings fileCountQuota: 1000 diskSizeQuota:"300g" |
我可以通过加载并连接字符串来一起处理这些,然后通过
{}".format(defaults_data, user_data), Loader=yaml.RoundTripLoader))
现在,我想通过 python 代码进行一些更新(例如更新时间戳),为此我只需要写回用户部分。这就是事情变得多毛的地方。到目前为止,我还没有找到一种方法来编写该 yaml 文档,而不是两者。
首先,除非您的默认文件中有多个文档,否则您
不必使用
多文档流。如果您使用带有文档结尾的格式字符串
标记 (
...\
{}"
---\
{}"
根据
YAML 规范:
It is an error for an alias node to use an anchor that does not
previously occur in the document.
锚点必须在文档中,而不仅仅是在流中(可以由多个
文件)。
我尝试了一些恶作剧,预先填充了已经表示的字典
锚定节点数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | import sys import datetime from ruamel import yaml def load(): with open('defaults.yaml') as fp: defaults_data = fp.read() with open('user.yaml') as fp: user_data = fp.read() merged_data = yaml.load("{}\ {}".format(defaults_data, user_data), Loader=yaml.RoundTripLoader) return merged_data class MyRTDGen(object): class MyRTD(yaml.RoundTripDumper): def __init__(self, *args, **kw): pps = kw.pop('pre_populate', None) yaml.RoundTripDumper.__init__(self, *args, **kw) if pps is not None: for pp in pps: try: anchor = pp.yaml_anchor() except AttributeError: anchor = None node = yaml.nodes.MappingNode( u'tag:yaml.org,2002:map', [], flow_style=None, anchor=anchor) self.represented_objects[id(pp)] = node def __init__(self, pre_populate=None): assert isinstance(pre_populate, list) self._pre_populate = pre_populate def __call__(self, *args, **kw): kw1 = kw.copy() kw1['pre_populate'] = self._pre_populate myrtd = self.MyRTD(*args, **kw1) return myrtd def update(md, file_name): ud = md.pop('userdefaults') MyRTD = MyRTDGen([ud]) yaml.dump(md, sys.stdout, Dumper=MyRTD) with open(file_name, 'w') as fp: yaml.dump(md, fp, Dumper=MyRTD) md = load() md['users']['xxxx2']['timestamp'] = str(datetime.datetime.utcnow()) update(md, 'user.yaml') |
由于基于 PyYAML 的 API 需要类而不是对象,因此您需要
使用一个类生成器,它实际上添加了要预先填充的数据元素
飞来飞去
但这不起作用,因为一个节点只有在它被写入后才会被一个锚点写出
确定使用了锚(即有第二个参考)。所以实际上
第一个合并键被写为锚。虽然我很熟悉
使用代码库,我无法让它在合理的时间内正常工作。
因此,我只依赖于只有一个键匹配的事实
在此之前归档并剥离任何内容。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | import sys import datetime from ruamel import yaml with open('defaults.yaml') as fp: defaults_data = fp.read() with open('user.yaml') as fp: user_data = fp.read() merged_data = yaml.load("{}\ {}".format(defaults_data, user_data), Loader=yaml.RoundTripLoader) # find the key for line in user_data.splitlines(): line = line.split('# ')[0].rstrip() # end of line comment, not checking for strings if line and line[-1] == ':' and line[0] != ' ': split_key = line break merged_data['users']['xxxx2']['timestamp'] = str(datetime.datetime.utcnow()) buf = yaml.compat.StringIO() yaml.dump(merged_data, buf, Dumper=yaml.RoundTripDumper) document = split_key + buf.getvalue().split('\ ' + split_key)[1] sys.stdout.write(document) |
给出:
1 2 3 4 5 6 7 | users: xxxx1: <<: *userdefaults timestamp: '2018-10-22 11:38:28.541810' xxxx2: <<: *userdefaults timestamp: '2018-10-23 09:59:13.829978' |
我必须制作一个 virtualenv 以确保我可以使用
那个版本是从我还年轻的时候开始的(我不会声称自己是无辜的)。
从那时起,该库已经发布了超过 85 个版本。
我可以理解,您可能无法运行任何东西,但
目前 Python2 无法编译/使用更新的版本。但是什么
你真正应该做的是安装
进一步"污染"您的系统安装),为
您正在开发的代码并安装最新版本的
你的其他图书馆)在那里。如果需要,您也可以这样做
要将您的软件分发到其他系统,只需在那里安装 virtualenv。
我拥有
virtualenv 的package器。
要写入用户部分,您必须手动拆分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | import datetime import StringIO import ruamel.yaml yaml = ruamel.yaml.YAML(typ='rt') data = None with open('defaults.yaml', 'r') as defaults: with open('users.yaml', 'r') as users: raw ="{}\ {}".format(''.join(defaults.readlines()), ''.join(users.readlines())) data = list(yaml.load_all(raw)) data[0]['users']['xxxx1']['timestamp'] = datetime.datetime.now().isoformat() with open('users.yaml', 'w') as outfile: sio = StringIO.StringIO() yaml.dump(data[0], sio) out = sio.getvalue() outfile.write(out.split('\ \ ')[1]) # write the second part here as this is the contents of users.yaml |