0

我希望能够在 yaml 中定义一个模式,在使用中读取它,pyyaml然后使用voluptuous(或其他模式验证器!)进行验证。但是,正如问题标题中所述,我遇到了需要为strvoluptuous 实例化内置类而不是它的字符串表示形式。

from voluptuous import Schema
import yaml


y = '''
a: str
b: int
c:
  d: float
  e: str
'''

yaml_schema = yaml.load(y,
                        Loader=yaml.CLoader)

schema1 = Schema(yaml_schema, required=True)

但是,此模式现在正在寻找字符串str作为 的唯一可接受的值a。使用直接 pyyaml(例如 'a': !!python/int)失败。相反,我想要下面的架构:

schema2 = Schema({'a': str,
                 'b': int,
                 'c': {'d': float,
                       'e': str}},
                required=True)

我很清楚这eval不是生产解决方案,但evaler下面的功能将转换schema1schema2...

def evaler(d):
    out = {}
    for k, v in d.items():
        if isinstance(v, dict):
            out[k] = evaler(v)
        else:
            out[k] = eval(v)
    return out

## Tests:

## passing
v.Schema(evaler(yaml_schema), 
         required=True)({'a': 'foo', 
                         'b': 2, 
                         'c': {'d': 2.0,
                               'e': 'bar'}})

## failling
v.Schema(evaler(yaml_schema), 
         required=True)({'a': 3, 
                         'b': 2, 
                         'c': {'d': 2.0,
                               'e': 1}})

我也知道你可以实例化一个空类:

class foo: pass
globals()['foo']

但是使用内置函数这是不可能的:

globals()['int']
# KeyError: 'int'

我探索了newandtype模块,但没有任何运气......

4

1 回答 1

1

最安全、最简单、最清晰的解决方案是明确列出您关心的类型的映射:

TYPES = {
    'str': str,
    'int': int,
    ...
}

您可以通过从类型列表中创建此 dict 来消除重复(以牺牲一些灵活性为代价):

TYPES = {cls.__name__: cls for cls in [str, int, ...]}

然后您可以递归地遍历文档(就像您在 中所做的那样evaler)并将每个字符串 s 替换为TYPES[s]. 如果您坚持按名称支持所有内置类型,而不单独列出它们,则可以使用该builtins模块(__builtin__在 Python 2 中调用)。 getattr是你的朋友吗?您可能应该检查它是否是一种类型——有很多不是的内置名称。

无论如何,您都需要遍历文档。从 PyYAML 的角度来看,用作映射值的字符串“str”与用作映射键的字符串“a”具有相同的标记,因此您无法通过为该标记指定不同的类来执行任何操作。虽然您也许可以深入了解它并引入一种以不同方式处理标量映射值的 hack,但这就是:一种 hack。还有大量的额外工作需要引导。不值得。

于 2013-06-05T21:02:41.350 回答