使用 ruamel.yaml (python) 加载和转储多个 yaml 文件
Loading and dumping multiple yaml files with ruamel.yaml (python)
使用 python 2 (atm) 和 ruamel.yaml 0.13.14 (RedHat EPEL)
我目前正在编写一些代码来加载 yaml 定义,但它们被分成多个文件。用户可编辑部分包含例如
users:
xxxx1:
timestamp: '2018-10-22 11:38:28.541810'
<< : *userdefaults
xxxx2:
<< : *userdefaults
timestamp: '2018-10-22 11:38:28.541810'
默认值存储在另一个不可编辑的文件中:
userdefaults: &userdefaults
# Default values for user settings
fileCountQuota: 1000
diskSizeQuota: "300g"
我可以通过加载两个字符串并连接字符串来一起处理它们,然后 运行 它们通过 merged_data = list(yaml.load_all("{}\n{}".format(defaults_data, user_data), Loader=yaml.RoundTripLoader))
正确解析所有内容。 (当不使用 RoundTripLoader 时,我得到无法解析引用的错误,这是正常的)
现在,我想通过 python 代码进行一些更新(例如更新时间戳),为此我只需要写回用户部分。这就是事情变得多毛的地方。到目前为止,我还没有找到一种方法来只编写那个 yaml 文档,而不是两者。
为了编写用户部分,您必须手动拆分 yaml.dump()
多文件输出的输出,并将适当的部分写回用户 yaml 文件。
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 = "{}\n{}".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('\n\n')[1]) # write the second part here as this is the contents of users.yaml
首先,除非您的默认文件中有多个文档,否则您
不必使用 load_all
,因为您不需要将两个文档连接成一个
多文档流。如果您使用带有文档结尾的格式字符串
标记 ("{}\n...\n{}"
) 或带有指令结束标记 ("{}\n---\n{}"
)
根据
YAML 规范:
It is an error for an alias node to use an anchor that does not
previously occur in the document.
锚点必须在文档中,而不仅仅是在流中(可以包含多个
文档)。
我尝试了一些 hocus pocus,预填充已经表示的字典
锚定节点数:
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("{}\n{}".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 需要 class 而不是对象,因此您需要
使用 class 生成器,它实际上添加了要预填充的数据元素
来自 yaml.load()
.
的苍蝇
但是这是行不通的,因为一个节点只有在它是
确定使用了锚点(即有第二个参考)。所以实际上
第一个合并键作为锚写出。虽然我很熟悉
使用代码库,我无法在合理的时间内使其正常工作。
所以相反,我只依赖于只有一个键匹配的事实
users.yaml
的第一个键在组合更新的转储的根级别
归档并删除之前的所有内容。
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("{}\n{}".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('\n' + split_key)[1]
sys.stdout.write(document)
给出:
users:
xxxx1:
<<: *userdefaults
timestamp: '2018-10-22 11:38:28.541810'
xxxx2:
<<: *userdefaults
timestamp: '2018-10-23 09:59:13.829978'
我必须制作一个 virtualenv 以确保我可以 运行 以上 ruamel.yaml==0.13.14
。
那个版本是我小时候的(我不会声称自己是无辜的)。
从那时起,该库已经发布了超过 85 个版本。
我能理解你可能无法 运行 除了
Python2 目前无法 compile/use 更新版本。但是什么
你真正应该做的是安装 virtualenv
(可以使用 EPEL 完成,但也可以不用
进一步"polluting"你的系统安装),为
您正在开发的代码并安装最新版本的 ruamel.yaml
(以及
你的其他图书馆)在那里。如果需要,您也可以这样做
要将您的软件分发到其他系统,只需在那里安装 virtualenv。
我所有的实用程序都在 /opt/util
下,并且管理
virtualenvutils
一个
virtualenv 的包装器。
使用 python 2 (atm) 和 ruamel.yaml 0.13.14 (RedHat EPEL)
我目前正在编写一些代码来加载 yaml 定义,但它们被分成多个文件。用户可编辑部分包含例如
users:
xxxx1:
timestamp: '2018-10-22 11:38:28.541810'
<< : *userdefaults
xxxx2:
<< : *userdefaults
timestamp: '2018-10-22 11:38:28.541810'
默认值存储在另一个不可编辑的文件中:
userdefaults: &userdefaults
# Default values for user settings
fileCountQuota: 1000
diskSizeQuota: "300g"
我可以通过加载两个字符串并连接字符串来一起处理它们,然后 运行 它们通过 merged_data = list(yaml.load_all("{}\n{}".format(defaults_data, user_data), Loader=yaml.RoundTripLoader))
正确解析所有内容。 (当不使用 RoundTripLoader 时,我得到无法解析引用的错误,这是正常的)
现在,我想通过 python 代码进行一些更新(例如更新时间戳),为此我只需要写回用户部分。这就是事情变得多毛的地方。到目前为止,我还没有找到一种方法来只编写那个 yaml 文档,而不是两者。
为了编写用户部分,您必须手动拆分 yaml.dump()
多文件输出的输出,并将适当的部分写回用户 yaml 文件。
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 = "{}\n{}".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('\n\n')[1]) # write the second part here as this is the contents of users.yaml
首先,除非您的默认文件中有多个文档,否则您
不必使用 load_all
,因为您不需要将两个文档连接成一个
多文档流。如果您使用带有文档结尾的格式字符串
标记 ("{}\n...\n{}"
) 或带有指令结束标记 ("{}\n---\n{}"
)
根据
YAML 规范:
It is an error for an alias node to use an anchor that does not previously occur in the document.
锚点必须在文档中,而不仅仅是在流中(可以包含多个 文档)。
我尝试了一些 hocus pocus,预填充已经表示的字典 锚定节点数:
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("{}\n{}".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 需要 class 而不是对象,因此您需要
使用 class 生成器,它实际上添加了要预填充的数据元素
来自 yaml.load()
.
但是这是行不通的,因为一个节点只有在它是 确定使用了锚点(即有第二个参考)。所以实际上 第一个合并键作为锚写出。虽然我很熟悉 使用代码库,我无法在合理的时间内使其正常工作。
所以相反,我只依赖于只有一个键匹配的事实
users.yaml
的第一个键在组合更新的转储的根级别
归档并删除之前的所有内容。
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("{}\n{}".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('\n' + split_key)[1]
sys.stdout.write(document)
给出:
users:
xxxx1:
<<: *userdefaults
timestamp: '2018-10-22 11:38:28.541810'
xxxx2:
<<: *userdefaults
timestamp: '2018-10-23 09:59:13.829978'
我必须制作一个 virtualenv 以确保我可以 运行 以上 ruamel.yaml==0.13.14
。
那个版本是我小时候的(我不会声称自己是无辜的)。
从那时起,该库已经发布了超过 85 个版本。
我能理解你可能无法 运行 除了
Python2 目前无法 compile/use 更新版本。但是什么
你真正应该做的是安装 virtualenv
(可以使用 EPEL 完成,但也可以不用
进一步"polluting"你的系统安装),为
您正在开发的代码并安装最新版本的 ruamel.yaml
(以及
你的其他图书馆)在那里。如果需要,您也可以这样做
要将您的软件分发到其他系统,只需在那里安装 virtualenv。
我所有的实用程序都在 /opt/util
下,并且管理
virtualenvutils
一个
virtualenv 的包装器。