【问题标题】:Parse nested custom yaml tags解析嵌套的自定义 yaml 标签
【发布时间】:2019-01-23 18:47:54
【问题描述】:

我有一些带有应用程序特定标签的 yaml(准确地说来自 AWS Cloud Formation 模板),如下所示:

example_yaml = "Name: !Join [' ', ['EMR', !Ref 'Environment', !Ref 'Purpose']]"

我想解析它以便我可以这样做:

>>> print(result)
>>> {'Name': 'EMR {Environment} {Purpose}'}

>>> name = result['name'].format(
...    Environment='Development',
...    Purpose='ETL'
... )
>>> print(name)
>>> EMR Development ETL

目前我的代码如下所示:

import yaml
from pprint import pprint


def aws_join(loader, node):
    join_args = loader.construct_yaml_seq(node)
    delimiter = list(join_args)[0]
    joinables = list(join_args)[1]
    join_result = delimiter.join(joinables)
    return join_result

def aws_ref(loader, node):
    value = loader.construct_scalar(node)
    placeholder = '{'+value+'}'
    return placeholder

yaml.add_constructor('!Join', aws_join)
yaml.add_constructor('!Ref', aws_ref)

example_yaml = "Name: !Join [' ', ['EMR', !Ref 'Environment', !Ref 'Purpose']]"

pprint(yaml.load(example_yaml))

很遗憾,这会导致错误。

...
   joinables = list(join_args)[1]
IndexError: list index out of range

print('What I am: '+str(join_args)) 添加到aws_join 表明我得到了一个生成器:

What I am: <generator object SafeConstructor.construct_yaml_seq at 0x1082ece08>

这就是我尝试将生成器转换为列表的原因。生成器最终会正确填充,只是没有及时让我使用它。如果我将 aws_join 函数更改为这样:

def aws_join(loader, node):
    join_args = loader.construct_yaml_seq(node)
    return join_args

那么最终的结果是这样的:

{'Name': [' ', ['EMR', '{Environment}', '{Purpose}']]}

所以我的功能所需的部分就在那里,只是在我的功能中需要它们时没有。

【问题讨论】:

    标签: python yaml pyyaml


    【解决方案1】:

    你很接近,但问题是你正在使用该方法 construct_yaml_seq()。该方法实际上是一个注册的 普通 YAML 序列的构造函数(最终使 一个 Python 列表),它调用 construct_sequence() 方法来处理 被传入的节点,这也是你应该做的。

    当您返回一个字符串时,它无法处理递归数据 结构,您不需要使用两步创建过程(首先 yield-ing,然后填写)其中construct_yaml_seq()方法 跟随。但是这两个步骤的创建过程是您遇到 生成器.

    construct_sequence 返回一个简单的列表,但你想要的节点 在开始处理时可用的!Join 下方,确保 指定deep=True 参数,否则为第二个列表 element 将是一个空列表。因为construct_yaml_seq(), 没有指定deep=True,你没有及时得到碎片 你的函数(否则你可以实际使用那个方法)。

    import yaml
    from pprint import pprint
    
    
    def aws_join(loader, node):
        join_args = loader.construct_sequence(node, deep=True)
        # you can comment out next line
        assert join_args == [' ', ['EMR', '{Environment}', '{Purpose}']] 
        delimiter = join_args[0]
        joinables = join_args[1]
        return delimiter.join(joinables)
    
    def aws_ref(loader, node):
        value = loader.construct_scalar(node)
        placeholder = '{'+value+'}'
        return placeholder
    
    yaml.add_constructor('!Join', aws_join, Loader=yaml.SafeLoader)
    yaml.add_constructor('!Ref', aws_ref, Loader=yaml.SafeLoader)
    
    example_yaml = "Name: !Join [' ', ['EMR', !Ref 'Environment', !Ref 'Purpose']]"
    
    pprint(yaml.safe_load(example_yaml))
    

    给出:

    {'Name': 'EMR {Environment} {Purpose}'}
    

    你不应该使用load(),它被记录为可能 不安全,最重要的是:这里没有必要。注册 SafeLoader 并致电safe_load()

    【讨论】:

      【解决方案2】:

      你需要改变:

      def aws_join(loader, node):
          delimiter = loader.construct_scalar(node.value[0])
          value = loader.construct_sequence(node.value[1])
          return delimiter.join(value)
      

      然后你会得到输出:

      {'Name': 'EMR {Environment} {Purpose}'}
      

      【讨论】:

        猜你喜欢
        • 2018-12-21
        • 2015-02-09
        • 1970-01-01
        • 2011-09-24
        • 1970-01-01
        • 1970-01-01
        • 2018-01-03
        • 2016-08-25
        • 2011-12-15
        相关资源
        最近更新 更多