зеркало из https://github.com/mozilla/gecko-dev.git
269 строки
8.1 KiB
Markdown
269 строки
8.1 KiB
Markdown
Error reporting should be accurate:
|
|
|
|
>>> from voluptuous import *
|
|
>>> schema = Schema(['one', {'two': 'three', 'four': ['five'],
|
|
... 'six': {'seven': 'eight'}}])
|
|
>>> schema(['one'])
|
|
['one']
|
|
>>> schema([{'two': 'three'}])
|
|
[{'two': 'three'}]
|
|
|
|
It should show the exact index and container type, in this case a list
|
|
value:
|
|
|
|
>>> try:
|
|
... schema(['one', 'two'])
|
|
... raise AssertionError('MultipleInvalid not raised')
|
|
... except MultipleInvalid as e:
|
|
... exc = e
|
|
>>> str(exc) == 'expected a dictionary @ data[1]'
|
|
True
|
|
|
|
It should also be accurate for nested values:
|
|
|
|
>>> try:
|
|
... schema([{'two': 'nine'}])
|
|
... raise AssertionError('MultipleInvalid not raised')
|
|
... except MultipleInvalid as e:
|
|
... exc = e
|
|
>>> str(exc)
|
|
"not a valid value for dictionary value @ data[0]['two']"
|
|
|
|
>>> try:
|
|
... schema([{'four': ['nine']}])
|
|
... raise AssertionError('MultipleInvalid not raised')
|
|
... except MultipleInvalid as e:
|
|
... exc = e
|
|
>>> str(exc)
|
|
"not a valid value @ data[0]['four'][0]"
|
|
|
|
>>> try:
|
|
... schema([{'six': {'seven': 'nine'}}])
|
|
... raise AssertionError('MultipleInvalid not raised')
|
|
... except MultipleInvalid as e:
|
|
... exc = e
|
|
>>> str(exc)
|
|
"not a valid value for dictionary value @ data[0]['six']['seven']"
|
|
|
|
Errors should be reported depth-first:
|
|
|
|
>>> validate = Schema({'one': {'two': 'three', 'four': 'five'}})
|
|
>>> try:
|
|
... validate({'one': {'four': 'six'}})
|
|
... except Invalid as e:
|
|
... print(e)
|
|
... print(e.path)
|
|
not a valid value for dictionary value @ data['one']['four']
|
|
['one', 'four']
|
|
|
|
Voluptuous supports validation when extra fields are present in the
|
|
data:
|
|
|
|
>>> schema = Schema({'one': 1, Extra: object})
|
|
>>> schema({'two': 'two', 'one': 1}) == {'two': 'two', 'one': 1}
|
|
True
|
|
>>> schema = Schema({'one': 1})
|
|
>>> try:
|
|
... schema({'two': 2})
|
|
... raise AssertionError('MultipleInvalid not raised')
|
|
... except MultipleInvalid as e:
|
|
... exc = e
|
|
>>> str(exc)
|
|
"extra keys not allowed @ data['two']"
|
|
|
|
dict, list, and tuple should be available as type validators:
|
|
|
|
>>> Schema(dict)({'a': 1, 'b': 2}) == {'a': 1, 'b': 2}
|
|
True
|
|
>>> Schema(list)([1,2,3])
|
|
[1, 2, 3]
|
|
>>> Schema(tuple)((1,2,3))
|
|
(1, 2, 3)
|
|
|
|
Validation should return instances of the right types when the types are
|
|
subclasses of dict or list:
|
|
|
|
>>> class Dict(dict):
|
|
... pass
|
|
>>>
|
|
>>> d = Schema(dict)(Dict(a=1, b=2))
|
|
>>> d == {'a': 1, 'b': 2}
|
|
True
|
|
>>> type(d) is Dict
|
|
True
|
|
>>> class List(list):
|
|
... pass
|
|
>>>
|
|
>>> l = Schema(list)(List([1,2,3]))
|
|
>>> l
|
|
[1, 2, 3]
|
|
>>> type(l) is List
|
|
True
|
|
|
|
Multiple errors are reported:
|
|
|
|
>>> schema = Schema({'one': 1, 'two': 2})
|
|
>>> try:
|
|
... schema({'one': 2, 'two': 3, 'three': 4})
|
|
... except MultipleInvalid as e:
|
|
... errors = sorted(e.errors, key=lambda k: str(k))
|
|
... print([str(i) for i in errors]) # doctest: +NORMALIZE_WHITESPACE
|
|
["extra keys not allowed @ data['three']",
|
|
"not a valid value for dictionary value @ data['one']",
|
|
"not a valid value for dictionary value @ data['two']"]
|
|
>>> schema = Schema([[1], [2], [3]])
|
|
>>> try:
|
|
... schema([1, 2, 3])
|
|
... except MultipleInvalid as e:
|
|
... print([str(i) for i in e.errors]) # doctest: +NORMALIZE_WHITESPACE
|
|
['expected a list @ data[0]',
|
|
'expected a list @ data[1]',
|
|
'expected a list @ data[2]']
|
|
|
|
Required fields in dictionary which are invalid should not have required :
|
|
|
|
>>> from voluptuous import *
|
|
>>> schema = Schema({'one': {'two': 3}}, required=True)
|
|
>>> try:
|
|
... schema({'one': {'two': 2}})
|
|
... except MultipleInvalid as e:
|
|
... errors = e.errors
|
|
>>> 'required' in ' '.join([x.msg for x in errors])
|
|
False
|
|
|
|
Multiple errors for nested fields in dicts and objects:
|
|
|
|
> \>\>\> from collections import namedtuple \>\>\> validate = Schema({
|
|
> ... 'anobject': Object({ ... 'strfield': str, ... 'intfield': int ...
|
|
> }) ... }) \>\>\> try: ... SomeObj = namedtuple('SomeObj', ('strfield',
|
|
> 'intfield')) ... validate({'anobject': SomeObj(strfield=123,
|
|
> intfield='one')}) ... except MultipleInvalid as e: ...
|
|
> print(sorted(str(i) for i in e.errors)) \# doctest:
|
|
> +NORMALIZE\_WHITESPACE ["expected int for object value @
|
|
> data['anobject']['intfield']", "expected str for object value @
|
|
> data['anobject']['strfield']"]
|
|
|
|
Custom classes validate as schemas:
|
|
|
|
>>> class Thing(object):
|
|
... pass
|
|
>>> schema = Schema(Thing)
|
|
>>> t = schema(Thing())
|
|
>>> type(t) is Thing
|
|
True
|
|
|
|
Classes with custom metaclasses should validate as schemas:
|
|
|
|
>>> class MyMeta(type):
|
|
... pass
|
|
>>> class Thing(object):
|
|
... __metaclass__ = MyMeta
|
|
>>> schema = Schema(Thing)
|
|
>>> t = schema(Thing())
|
|
>>> type(t) is Thing
|
|
True
|
|
|
|
Schemas built with All() should give the same error as the original
|
|
validator (Issue \#26):
|
|
|
|
>>> schema = Schema({
|
|
... Required('items'): All([{
|
|
... Required('foo'): str
|
|
... }])
|
|
... })
|
|
|
|
>>> try:
|
|
... schema({'items': [{}]})
|
|
... raise AssertionError('MultipleInvalid not raised')
|
|
... except MultipleInvalid as e:
|
|
... exc = e
|
|
>>> str(exc)
|
|
"required key not provided @ data['items'][0]['foo']"
|
|
|
|
Validator should return same instance of the same type for object:
|
|
|
|
>>> class Structure(object):
|
|
... def __init__(self, q=None):
|
|
... self.q = q
|
|
... def __repr__(self):
|
|
... return '{0.__name__}(q={1.q!r})'.format(type(self), self)
|
|
...
|
|
>>> schema = Schema(Object({'q': 'one'}, cls=Structure))
|
|
>>> type(schema(Structure(q='one'))) is Structure
|
|
True
|
|
|
|
Object validator should treat cls argument as optional. In this case it
|
|
shouldn't check object type:
|
|
|
|
>>> from collections import namedtuple
|
|
>>> NamedTuple = namedtuple('NamedTuple', ('q',))
|
|
>>> schema = Schema(Object({'q': 'one'}))
|
|
>>> named = NamedTuple(q='one')
|
|
>>> schema(named) == named
|
|
True
|
|
>>> schema(named)
|
|
NamedTuple(q='one')
|
|
|
|
If cls argument passed to object validator we should check object type:
|
|
|
|
>>> schema = Schema(Object({'q': 'one'}, cls=Structure))
|
|
>>> schema(NamedTuple(q='one')) # doctest: +IGNORE_EXCEPTION_DETAIL
|
|
Traceback (most recent call last):
|
|
...
|
|
MultipleInvalid: expected a <class 'Structure'>
|
|
>>> schema = Schema(Object({'q': 'one'}, cls=NamedTuple))
|
|
>>> schema(NamedTuple(q='one'))
|
|
NamedTuple(q='one')
|
|
|
|
Ensure that objects with \_\_slots\_\_ supported properly:
|
|
|
|
>>> class SlotsStructure(Structure):
|
|
... __slots__ = ['q']
|
|
...
|
|
>>> schema = Schema(Object({'q': 'one'}))
|
|
>>> schema(SlotsStructure(q='one'))
|
|
SlotsStructure(q='one')
|
|
>>> class DictStructure(object):
|
|
... __slots__ = ['q', '__dict__']
|
|
... def __init__(self, q=None, page=None):
|
|
... self.q = q
|
|
... self.page = page
|
|
... def __repr__(self):
|
|
... return '{0.__name__}(q={1.q!r}, page={1.page!r})'.format(type(self), self)
|
|
...
|
|
>>> structure = DictStructure(q='one')
|
|
>>> structure.page = 1
|
|
>>> try:
|
|
... schema(structure)
|
|
... raise AssertionError('MultipleInvalid not raised')
|
|
... except MultipleInvalid as e:
|
|
... exc = e
|
|
>>> str(exc)
|
|
"extra keys not allowed @ data['page']"
|
|
|
|
>>> schema = Schema(Object({'q': 'one', Extra: object}))
|
|
>>> schema(structure)
|
|
DictStructure(q='one', page=1)
|
|
|
|
Ensure that objects can be used with other validators:
|
|
|
|
>>> schema = Schema({'meta': Object({'q': 'one'})})
|
|
>>> schema({'meta': Structure(q='one')})
|
|
{'meta': Structure(q='one')}
|
|
|
|
Ensure that subclasses of Invalid of are raised as is.
|
|
|
|
>>> class SpecialInvalid(Invalid):
|
|
... pass
|
|
...
|
|
>>> def custom_validator(value):
|
|
... raise SpecialInvalid('boom')
|
|
...
|
|
>>> schema = Schema({'thing': custom_validator})
|
|
>>> try:
|
|
... schema({'thing': 'not an int'})
|
|
... except MultipleInvalid as e:
|
|
... exc = e
|
|
>>> exc.errors[0].__class__.__name__
|
|
'SpecialInvalid'
|