不使用 JSON Schema 的用户定义模式验证

0 投票
2 回答
999 浏览
提问于 2025-04-18 18:10

我想要一个用户自定义的结构来进行模式验证。比如,我想为数据库文件创建不同的字段,在数据加载到文件之前,我想检查一下这些字段是否符合规定的格式。这通常可以通过使用 JSON 模式和验证方法来轻松实现;不过,我不想使用除了 Python 自带的标准库以外的任何包。比如我有

"price" : {"type" : "number" and number >45},
"name" : {"type" : "string"}
"age" : {"type" : "number" and number >0}
.................

那么我该如何在不使用 JSON 模式的情况下,利用这样的结构来验证我的输入呢?

谢谢!

2 个回答

0

虽然它不符合你“除了标准内置包之外不使用其他包”的要求,但你可以考虑一下Voluptuous。这个库简单明了,使用起来也很方便,我们在项目中用得很顺利。

0

在这种情况下,我认为你能做的最好的事情就是在确保安全的情况下,使用 ast 来处理验证代码,然后再用 eval 来执行它。

# schema_validator.py
import ast


class SchemaSecurityValidationError(Exception):
    pass


class ValidationError(Exception):
    pass


def secure_schema(schema,
                  builitins=__builtins__.__dict__.values(),
                  forbidden_builtins=(file, open, __import__),
                  forbidden_nodes=(ast.Import, ast.ImportFrom)):
    forbidden_builtins_names = tuple(b.__name__ for b in forbidden_builtins)
    for field, field_descriptor in schema.iteritems():
        type_validator = field_descriptor.get('type', None)
        if type_validator is not None:
            if not type_validator in builitins:
                raise SchemaSecurityValidationError(
                    "%s:'type' can only be basic type or builtin" % field)
            if type_validator in forbidden_builtins:
                raise SchemaSecurityValidationError(
                    "%s:'type' cant be of %s type" % (field, type_validator))
        value_validator = field_descriptor.get('validator', None)
        if value_validator is not None:
            tree = ast.parse(value_validator)
            for node in ast.walk(tree):
                if type(node) in forbidden_nodes:
                    raise SchemaSecurityValidationError(
                        "%s:%s is forbidden in validator" % (field, ast.dump(node)))
                if  isinstance(node, ast.Call) \
                        and isinstance(node.func, ast.Name)\
                        and node.func.id in forbidden_builtins_names:
                    raise SchemaSecurityValidationError(
                        "%s:%s is forbidden in validator" % (field, ast.dump(node)))


def validate(record, schema):
    secure_schema(schema)
    for field, field_descriptor in schema.iteritems():
        value = record[field]
        type_validator = field_descriptor.get('type', None)
        if type_validator is not None:
            if not isinstance(value, type_validator):
                raise ValidationError('wrong type blabla')
        value_validator = field_descriptor.get('validator', None)
        if value_validator is not None:
            if not eval(value_validator, {'value': value}):
                raise ValidationError('wrong value blabla')
    return record


#validating with different schemas

schema0 = {'price': {'type': int, 'validator': 'value and value>45'}}
schema1 = {'price': {
    'type': int, 'validator': '__import__("socket") or value and value>45'}}
schema2 = {'price': {
    'type': int, 'validator': 'int("test.py",x=file("test.py")).read() or value and value>45'}}
schema3 = {'price': {
    'type': int, 'validator': 'open("test.py").read() or value and value>45'}}

print validate({'price': 45}, schema0)
print validate({'price': 46}, schema1)
print validate({'price': 46}, schema2)
print validate({'price': 46}, schema3)

撰写回答