Прежде всего, если в вашем файле по умолчанию нет нескольких документов, вам не нужно использовать load_all
, так как вы не объединяете два документа в поток из нескольких документов.Если бы вы использовали строку формата с маркером конца документа ("{}\n...\n{}"
) или с маркером конца директивы ("{}\n---\n{}"
), ваши псевдонимы не переносились бы из одного документа в другой согласно спецификации YAML:
Для узла псевдонима является ошибкой использование привязки, которая ранее не встречалась в документе.
Якорь должен быть в документе, а не только впоток (который может состоять из нескольких документов).
Я попробовал несколько фокус-покусов, предварительно заполнив уже представленный словарь привязанных узлов:
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')
Поскольку на основе PyYAMLAPI требует класс вместо объекта, вам нужно использовать генератор классов, который на самом деле добавляет элементы данных для предварительного заполнения на лету из-за 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'
IЯ должен был сделать virtualenv, чтобы удостовериться, что смогу выполнить вышеизложенное с ruamel.yaml==0.13.14
Эта версия с тех пор, как я был еще молод (я не буду утверждать, что был невиновен).С тех пор было выпущено более 85 выпусков библиотеки.
Я могу понять, что в настоящий момент вы можете не запускать ничего, кроме Python2, и не можете скомпилировать / использовать более новую версию.Но то, что вам действительно нужно сделать, это установить virtualenv
(это можно сделать с помощью EPEL, но также без дальнейшего «загрязнения» установки вашей системы), сделать virtualenv для кода, который вы разрабатываете, и установить последнюю версию ruamel.yaml
(иваши другие библиотеки) там.Вы также можете сделать это, если вам нужно распространить свое программное обеспечение на другие системы, просто установите туда virtualenv.
У меня есть все мои утилиты в /opt/util
, и они управляются virtualenvutils
обертка вокруг virtualenv.