在 PyYAML 中创建自定义标签 [英] Creating Custom Tag in PyYAML

查看:46
本文介绍了在 PyYAML 中创建自定义标签的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 Python 的 PyYAML 创建一个自定义标记,以便我可以使用 YAML 检索环境变量.

导入操作系统导入 yaml类 EnvTag(yaml.YAMLObject):yaml_tag = u'!Env'def __init__(self, env_var):self.env_var = env_vardef __repr__(self):返回 os.environ.get(self.env_var)settings_file = open('conf/defaults.yaml', 'r')设置 = yaml.load(settings_file)

defaults.yaml 里面很简单:

例子:!ENV foo

我不断收到的错误:

yaml.constructor.ConstructorError:无法确定标签 '!ENV' 的构造函数defaults.yaml",第 1 行,第 10 列

我还计划拥有多个自定义标签(假设我可以让这个标签正常工作)

解决方案

你的 PyYAML 类有一些问题:

  1. yaml_tag 区分大小写,所以 !Env!ENV 是不同的标签.
  2. 因此,根据文档,yaml.YAMLObject 使用元类来定义自身,并具有默认的 to_yamlfrom_yaml 函数用于那些情况.但是,默认情况下,这些函数要求自定义标记的参数(在本例中为 !ENV)是 映射.因此,要使用默认函数,您的 defaults.yaml 文件必须如下所示(仅作为示例):

<块引用>

例子:!ENV {env_var: "PWD", test: "test"}

然后您的代码将保持不变,在我的情况下 print(settings) 现在导致 {'example':/home/Fred} 但是你'重新使用 load 而不是 safe_load - 在他们下面的回答中,Anthon 指出这是危险的,因为解析的 YAML 可以覆盖/读取磁盘上任何位置的数据.em>

您仍然可以轻松使用您的 YAML 文件格式,示例:!ENV foo——您只需要定义一个合适的 to_yamlfrom_yamlEnvTag 类中,可以解析和发出 scalar 变量,如字符串foo".

所以:

导入操作系统导入 yaml类 EnvTag(yaml.YAMLObject):yaml_tag = u'!ENV'def __init__(self, env_var):self.env_var = env_vardef __repr__(self):v = os.environ.get(self.env_var) 或 ''返回 '​​EnvTag({}, contains={})'.format(self.env_var, v)@类方法def from_yaml(cls​​, loader, node):返回 EnvTag(node.value)@类方法def to_yaml(cls​​, dumper, data):返回 dumper.represent_scalar(cls.yaml_tag, data.env_var)# safe_load 需要yaml.SafeLoader.add_constructor('!ENV', EnvTag.from_yaml)# safe_dump 需要yaml.SafeDumper.add_multi_representer(EnvTag, EnvTag.to_yaml)settings_file = open('defaults.yaml', 'r')设置 = yaml.safe_load(settings_file)打印(设置)s = yaml.safe_dump(设置)印刷)

当这个程序运行时,它输出:

{'example': EnvTag(foo, contains=)}{示例:!ENV 'foo'}

此代码的好处是(1)使用原始 pyyaml,因此无需额外安装和(2)添加代表.:)

I'm trying to use Python's PyYAML to create a custom tag that will allow me to retrieve environment variables with my YAML.

import os
import yaml

class EnvTag(yaml.YAMLObject):
    yaml_tag = u'!Env'

    def __init__(self, env_var):
       self.env_var = env_var

    def __repr__(self):
       return os.environ.get(self.env_var)

settings_file = open('conf/defaults.yaml', 'r')
settings = yaml.load(settings_file)

And inside of defaults.yaml is simply:

example: !ENV foo

The error I keep getting:

yaml.constructor.ConstructorError: 
could not determine a constructor for the tag '!ENV' in 
"defaults.yaml", line 1, column 10

I plan to have more than one custom tag as well (assuming I can get this one working)

解决方案

Your PyYAML class had a few problems:

  1. yaml_tag is case sensitive, so !Env and !ENV are different tags.
  2. So, as per the documentation, yaml.YAMLObject uses meta-classes to define itself, and has default to_yaml and from_yaml functions for those cases. By default, however, those functions require that your argument to your custom tag (in this case !ENV) be a mapping. So, to work with the default functions, your defaults.yaml file must look like this (just for example) instead:

example: !ENV {env_var: "PWD", test: "test"}

Your code will then work unchanged, in my case print(settings) now results in {'example': /home/Fred} But you're using load instead of safe_load -- in their answer below, Anthon pointed out that this is dangerous because the parsed YAML can overwrite/read data anywhere on the disk.

You can still easily use your YAML file format, example: !ENV foo—you just have to define an appropriate to_yaml and from_yaml in class EnvTag, ones that can parse and emit scalar variables like the string "foo".

So:

import os
import yaml

class EnvTag(yaml.YAMLObject):
    yaml_tag = u'!ENV'

    def __init__(self, env_var):
        self.env_var = env_var

    def __repr__(self):
        v = os.environ.get(self.env_var) or ''
        return 'EnvTag({}, contains={})'.format(self.env_var, v)

    @classmethod
    def from_yaml(cls, loader, node):
        return EnvTag(node.value)

    @classmethod
    def to_yaml(cls, dumper, data):
        return dumper.represent_scalar(cls.yaml_tag, data.env_var)

# Required for safe_load
yaml.SafeLoader.add_constructor('!ENV', EnvTag.from_yaml)
# Required for safe_dump
yaml.SafeDumper.add_multi_representer(EnvTag, EnvTag.to_yaml)

settings_file = open('defaults.yaml', 'r')

settings = yaml.safe_load(settings_file)
print(settings)

s = yaml.safe_dump(settings)
print(s)

When this program is run, it outputs:

{'example': EnvTag(foo, contains=)}
{example: !ENV 'foo'}

This code has the benefit of (1) using the original pyyaml, so nothing extra to install and (2) adding a representer. :)

这篇关于在 PyYAML 中创建自定义标签的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆