加载和转储多个yaml文件拉梅尔亚姆勒(Python)

2024-10-03 21:28:59 发布

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

使用Python2(atm)和拉梅尔亚姆勒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'

默认值存储在另一个不可编辑的文件中:

^{pr2}$

我可以同时处理这些字符串,方法是加载并合并字符串,然后通过merged_data = list(yaml.load_all("{}\n{}".format(defaults_data, user_data), Loader=yaml.RoundTripLoader))运行它们,这可以正确地解决所有问题。(当不使用RoundTripLoader时,我得到的错误是无法解析引用,这是正常的)

现在,我想通过python代码进行一些更新(例如更新时间戳),为此我只需要写回用户部分。这就是事情变得棘手的地方。我还没有找到一种方法来写yaml文档,不是两者都写。在


Tags: 文件方法字符串代码用户编辑yamldata
2条回答

首先,除非默认文件中有多个文档,否则 不必使用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需要一个类而不是一个对象,因此您需要 使用一个类生成器,它实际上添加了要预填充的数据元素 这只苍蝇来自于yaml.load()

但这不起作用,因为节点只有在锚定之后才能被写出 确定使用了锚点(即有第二个参考)。所以实际上 第一个合并键被写为锚。虽然我很熟悉 有了代码库,我无法在合理的时间内使其正常工作。

因此,我只能依赖于只有一个匹配的键 在组合更新的转储的根级别上users.yaml的第一个键 在那之前把任何东西锉掉。

^{pr2}$

它给出了:

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目前无法编译/使用更新的版本。但是什么 您真正应该做的是安装virtualenv(可以使用EPEL完成,但也可以不使用 进一步“污染”您的系统安装),为 您正在开发并安装最新版本的ruamel.yaml(和 你的其他图书馆)在那里。你也可以这样做,如果你需要 要将软件分发到其他系统,只需在那里安装virtualenv。

我把我所有的实用程序都放在/opt/util下,并进行管理 ^{}a 包装virtualenv。

为了编写用户部分,您必须手动分割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

相关问题 更多 >