Fix const validations (#794)

This fixes #620 and #793
This commit is contained in:
Hmvp
2019-09-17 19:30:58 +02:00
committed by Samuel Colvin
parent 1b467da11f
commit dd32a43814
4 changed files with 117 additions and 8 deletions
+1
View File
@@ -0,0 +1 @@
Fix const validations for lists
+1 -1
View File
@@ -105,7 +105,7 @@ def flatten_errors(
else:
yield error.dict(config, loc_prefix=loc)
elif isinstance(error, list):
yield from flatten_errors(error, config)
yield from flatten_errors(error, config, loc=loc)
else:
raise RuntimeError(f'Unknown error object: {error}')
+9 -7
View File
@@ -106,8 +106,8 @@ class Field:
self.sub_fields: Optional[List[Field]] = None
self.key_field: Optional[Field] = None
self.validators: 'ValidatorsList' = []
self.whole_pre_validators: Optional['ValidatorsList'] = None
self.whole_post_validators: Optional['ValidatorsList'] = None
self.whole_pre_validators: 'ValidatorsList' = []
self.whole_post_validators: 'ValidatorsList' = []
self.parse_json: bool = False
self.shape: int = SHAPE_SINGLETON
self.prepare()
@@ -253,9 +253,8 @@ class Field:
else:
raise TypeError(f'Fields of type "{origin}" are not supported.')
if getattr(self.type_, '__origin__', None):
# type_ has been refined eg. as the type of a List and sub_fields needs to be populated
self.sub_fields = [self._create_sub_type(self.type_, '_' + self.name)]
# type_ has been refined eg. as the type of a List and sub_fields needs to be populated
self.sub_fields = [self._create_sub_type(self.type_, '_' + self.name)]
def _create_sub_type(self, type_: AnyType, name: str, *, for_keys: bool = False) -> 'Field':
return self.__class__(
@@ -272,13 +271,16 @@ class Field:
v_funcs = (
*[v.func for v in class_validators_ if not v.whole and v.pre],
*(get_validators() if get_validators else list(find_validators(self.type_, self.model_config))),
self.schema is not None and self.schema.const and constant_validator,
*[v.func for v in class_validators_ if not v.whole and not v.pre],
)
self.validators = self._prep_vals(v_funcs)
# Add const validator
if self.schema is not None and self.schema.const:
self.whole_pre_validators = self._prep_vals([constant_validator])
if class_validators_:
self.whole_pre_validators = self._prep_vals(v.func for v in class_validators_ if v.whole and v.pre)
self.whole_pre_validators.extend(self._prep_vals(v.func for v in class_validators_ if v.whole and v.pre))
self.whole_post_validators = self._prep_vals(v.func for v in class_validators_ if v.whole and not v.pre)
@staticmethod
+106
View File
@@ -397,6 +397,112 @@ def test_const_with_wrong_value():
]
def test_const_list():
class SubModel(BaseModel):
b: int
class Model(BaseModel):
a: List[SubModel] = Schema([SubModel(b=1), SubModel(b=2), SubModel(b=3)], const=True)
b: List[SubModel] = Schema([{'b': 4}, {'b': 5}, {'b': 6}], const=True)
m = Model()
assert m.a == [SubModel(b=1), SubModel(b=2), SubModel(b=3)]
assert m.b == [SubModel(b=4), SubModel(b=5), SubModel(b=6)]
assert m.schema() == {
'definitions': {
'SubModel': {
'properties': {'b': {'title': 'B', 'type': 'integer'}},
'required': ['b'],
'title': 'SubModel',
'type': 'object',
}
},
'properties': {
'a': {
'const': [SubModel(b=1), SubModel(b=2), SubModel(b=3)],
'items': {'$ref': '#/definitions/SubModel'},
'title': 'A',
'type': 'array',
},
'b': {
'const': [{'b': 4}, {'b': 5}, {'b': 6}],
'items': {'$ref': '#/definitions/SubModel'},
'title': 'B',
'type': 'array',
},
},
'title': 'Model',
'type': 'object',
}
def test_const_list_with_wrong_value():
class SubModel(BaseModel):
b: int
class Model(BaseModel):
a: List[SubModel] = Schema([SubModel(b=1), SubModel(b=2), SubModel(b=3)], const=True)
b: List[SubModel] = Schema([{'b': 4}, {'b': 5}, {'b': 6}], const=True)
with pytest.raises(ValidationError) as exc_info:
Model(a=[{'b': 3}, {'b': 1}, {'b': 2}], b=[{'b': 6}, {'b': 5}])
assert exc_info.value.errors() == [
{
'ctx': {
'given': [{'b': 3}, {'b': 1}, {'b': 2}],
'permitted': [[SubModel(b=1), SubModel(b=2), SubModel(b=3)]],
},
'loc': ('a',),
'msg': 'unexpected value; permitted: [<SubModel b=1>, <SubModel b=2>, <SubModel b=3>]',
'type': 'value_error.const',
},
{
'ctx': {'given': [{'b': 6}, {'b': 5}], 'permitted': [[{'b': 4}, {'b': 5}, {'b': 6}]]},
'loc': ('b',),
'msg': "unexpected value; permitted: [{'b': 4}, {'b': 5}, {'b': 6}]",
'type': 'value_error.const',
},
]
with pytest.raises(ValidationError) as exc_info:
Model(a=[SubModel(b=3), SubModel(b=1), SubModel(b=2)], b=[SubModel(b=3), SubModel(b=1)])
assert exc_info.value.errors() == [
{
'ctx': {
'given': [SubModel(b=3), SubModel(b=1), SubModel(b=2)],
'permitted': [[SubModel(b=1), SubModel(b=2), SubModel(b=3)]],
},
'loc': ('a',),
'msg': 'unexpected value; permitted: [<SubModel b=1>, <SubModel b=2>, <SubModel b=3>]',
'type': 'value_error.const',
},
{
'ctx': {'given': [SubModel(b=3), SubModel(b=1)], 'permitted': [[{'b': 4}, {'b': 5}, {'b': 6}]]},
'loc': ('b',),
'msg': "unexpected value; permitted: [{'b': 4}, {'b': 5}, {'b': 6}]",
'type': 'value_error.const',
},
]
def test_const_validation_json_serializable():
class SubForm(BaseModel):
field: int
class Form(BaseModel):
field1: SubForm = Schema({'field': 2}, const=True)
field2: List[SubForm] = Schema([{'field': 2}], const=True)
with pytest.raises(ValidationError) as exc_info:
# Fails
Form(field1={'field': 1}, field2=[{'field': 1}])
# This should not raise an Json error
exc_info.value.json()
class ValidateAssignmentModel(BaseModel):
a: int = 2
b: constr(min_length=1)