程序员最近都爱上了这个网站  程序员们快来瞅瞅吧!  it98k网:it98k.com

本站消息

站长简介/公众号

  出租广告位,需要合作请联系站长

+关注
已关注

分类  

暂无分类

标签  

暂无标签

日期归档  

暂无数据

如何使用Python更新.yml文件,忽略已有的Jinja语法?

发布于2019-09-19 10:57     阅读(1229)     评论(0)     点赞(22)     收藏(3)


我有一些预处理与一些现有的.yml文件 - 但是,其中一些嵌入了Jinja模板语法:

A:
 B:
 - ip: 1.2.3.4
 - myArray:
   - {{ jinja.variable }}
   - val1
   - val2

我想在这个文件中读取,并添加val3myArray这样:

A:
 B:
 - ip: 1.2.3.4
 - myArray:
   - {{ jinja.variable }}
   - val1
   - val2
   - val 3

我试着手动写出jinja模板,但是他们用单引号写了: '{{ jinja.variable }}'

对于我来说,读取这样的.yml文件并修改它们的推荐方法是什么,尽管有预先存在的Jinja语法?我想向这些文件添加信息,保持其他所有相同。

我在Python 2.7+上使用PyYAML尝试了上述内容


解决方案


此答案中的解决方案已使用插件机制合并到ruamel.yaml中。在这篇文章的底部有关于如何使用它的快速和脏的说明。

更新包含jinja2“code”的YAML文件有三个方面:

  • 使jinja2代码可以被YAML解析器接受
  • 确保可接受的可以逆转(即变化应该是唯一的,所以只有它们才能被逆转)
  • 保留YAML文件的布局,以便jinja2处理的更新文件仍然生成一个有效的YAML文件,该文件也可以加载。

让我们首先通过添加jinja2变量定义和for循环以及添加一些注释(input.yaml来使您的示例更加真实

# trying to update
{% set xyz = "123" }

A:
  B:
  - ip: 1.2.3.4
  - myArray:
    - {{ jinja.variable }}
    - val1
    - val2         # add a value after this one
    {% for d in data %}
    - phone: {{ d.phone }}
      name: {{ d.name }}
    {% endfor %}
    - {{ xyz }}
# #% or ##% should not be in the file and neither <{ or <<{

{%#YLL 开头的行不包含YAML,因此我们将这些作为注释(假设注释在往返时保留,见下文)。由于YAML标量不能在{没有引用的情况下开始,我们将更{{改为<{这可以通过调用以下代码完成sanitize()(它也存储使用的模式,反之在sanitize.reverse(使用存储的模式)完成。

保存您的YAML代码(块样式等)最好使用ruamel.yaml(免责声明:我是该软件包的作者),这样您就不必担心输入中的流式元素被破坏为块与default_flow_style=False其他答案使用的相当粗糙的风格一样ruamel.yaml还保留了注释,包括最初在文件中的注释,以及那些暂时插入以“注释掉”jinja2结构的注释%{

结果代码:

import sys
from ruamel.yaml import YAML

yaml = YAML()

class Sanitize:
    """analyse, change and revert YAML/jinja2 mixture to/from valid YAML"""
    def __init__(self):
        self.accacc = None
        self.accper = None

    def __call__(self, s):
        len = 1
        for len in range(1, 10):
            pat = '<' * len + '{'
            if pat not in s:
                self.accacc = pat
                break
        else:
            raise NotImplementedError('could not find substitute pattern '+pat)
        len = 1
        for len in range(1, 10):
            pat = '#' * len + '%'
            if pat not in s:
                self.accper = pat
                break
        else:
            raise NotImplementedError('could not find substitute pattern '+pat)
        return s.replace('{{', self.accacc).replace('{%', self.accper)

    def revert(self, s):
        return s.replace(self.accacc, '{{').replace(self.accper, '{%')


def update_one(file_name, out_file_name=None):

    sanitize = Sanitize()

    with open(file_name) as fp:
        data = yaml.load(sanitize(fp.read()))
    myArray = data['A']['B'][1]['myArray']
    pos = myArray.index('val2')
    myArray.insert(pos+1, 'val 3')
    if out_file_name is None:
        yaml.dump(data, sys.stdout, transform=sanitize.revert)
    else:
        with open(out_file_name, 'w') as fp:
            yaml.dump(data, out, transform=sanitize.revert)

update_one('input.yaml')

update_one()使用Python 2.7 打印(指定要写入文件的第二个参数):

# trying to update
{% set xyz = "123" }

A:
  B:
  - ip: 1.2.3.4
  - myArray:
    - {{ jinja.variable }}
    - val1
    - val2         # add a value after this one
    - val 3
    {% for d in data %}
    - phone: {{ d.phone }}
      name: {{ d.name }}
    {% endfor %}
    - {{ xyz }}
# #% or ##% should not be in the file and neither <{ or <<{

如果既不是#{也不<{在任何原始输入中,则可以使用简单的单行函数完成清理和恢复(请参阅此文章的此版本),然后您不需要该类Sanitize

您的示例缩进了一个位置(键B)以及两个位置(序列元素),ruamel.yaml没有对输出缩进的精确控制(我不知道任何YAML解析器)。缩进(默认为2)应用于两个YAML映射关于序列元素(测量到元素的开头,而不是短划线)。这对重新阅读YAML没有影响,也发生在其他两个回答者的输出上(没有他们指出这个变化)。

另请注意,这YAML().load()是安全的(即不加载任意潜在的恶意对象),而yaml.load()其他答案中使用的肯定是不安全的,它在文档中也是如此,甚至在YAML上WikiPedia文章中也有提及如果使用yaml.load(),则必须检查每个输入文件,以确保没有标记的对象可能导致光盘被擦除(或更糟)。

如果您需要反复更新你的文件,并有过的Jinja2模板控制,它可能是更好的改变Jinja2的模式一次,不能回复他们,然后指定相应的block_start_stringvariable_start_string(可能block_end_stringvariable_end_string)的jinja2.FileSystemLoader添加加载到jinja2.Environment


If the above seems to complicated then in a a virtualenv do:

pip install ruamel.yaml ruamel.yaml.jinja2

assuming you have the input.yaml from before you can run:

import os
from ruamel.yaml import YAML


yaml = YAML(typ='jinja2')

with open('input.yaml') as fp:
    data = yaml.load(fp)

myArray = data['A']['B'][1]['myArray']
pos = myArray.index('val2')
myArray.insert(pos+1, 'val 3')

with open('output.yaml', 'w') as fp:
    yaml.dump(data, fp)

os.system('diff -u input.yaml output.yaml')

to get the diff output:

--- input.yaml  2017-06-14 23:10:46.144710495 +0200
+++ output.yaml 2017-06-14 23:11:21.627742055 +0200
@@ -8,6 +8,7 @@
     - {{ jinja.variable }}
     - val1
     - val2         # add a value after this one
+    - val 3
     {% for d in data %}
     - phone: {{ d.phone }}
       name: {{ d.name }}

ruamel.yaml 0.15.7 implements a new plug-in mechanism and ruamel.yaml.jinja2 is a plug-in that rewraps the code in this answer transparently for the user. Currently the information for reversion is attached to the YAML() instance, so make sure you do yaml = YAML(typ='jinja2') for each file you process (that information could be attached to the top-level data instance, just like the YAML comments are).



所属网站分类: 技术文章 > 问答

作者:黑洞官方问答小能手

链接:https://www.pythonheidong.com/blog/article/114877/1c428d15113185cd20ba/

来源:python黑洞网

任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任

22 0
收藏该文
已收藏

评论内容:(最多支持255个字符)