不使用 JSON Schema 的用户定义模式验证
我想要一个用户自定义的结构来进行模式验证。比如,我想为数据库文件创建不同的字段,在数据加载到文件之前,我想检查一下这些字段是否符合规定的格式。这通常可以通过使用 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)