-
Notifications
You must be signed in to change notification settings - Fork 72
Open
Description
The code merging the invalid result from the validate_partial function cannot handle complex/nested invalid errors.
https://github.com/formencode/formencode/blob/2.0.0a1/formencode/schema.py#L205-L216
for validator in self.chained_validators:
if (not hasattr(validator, 'validate_partial') or not getattr(
validator, 'validate_partial_form', False)):
continue
try:
validator.validate_partial(value_dict, state)
except Invalid as e:
sub_errors = e.unpack_errors()
if not isinstance(sub_errors, dict):
# Can't do anything here
continue
merge_dicts(errors, sub_errors)This code works for a validator like formencode.validators.FieldsMatch which only invalidates the current level of the dict. But if the validator is another Schema validator which returns a nested error_dict, the structure of the resulting error dict is inconsistent.
The following is an example unit test: test_sample.py
from formencode import (
Invalid,
validators,
)
from formencode.schema import Schema
class Name(Schema):
fname = validators.String(not_empty=True)
mi = validators.String(max=1, if_missing=None, if_empty=None)
lname = validators.String(not_empty=True)
class ChainedTest(Schema):
a = validators.String()
a_confirm = validators.String()
b = validators.String()
b_confirm = validators.String()
chained_validators = [validators.FieldsMatch('a', 'a_confirm'),
validators.FieldsMatch('b', 'b_confirm')]
class NestedSchemaValidator(Schema):
name = Name(if_missing=None)
chained_test = ChainedTest(if_missing=None)
class PartialFormValidator(validators.FormValidator):
"""
Call the NestedSchemaValidator validator as a post/chained validator to a form
"""
validate_partial_form = True
def validate_partial(self, field_dict, state):
self._validate_python(field_dict, state)
def _validate_python(self, field_dict, state):
NestedSchemaValidator().to_python(field_dict, state)
class PartialFormSchemaValidator(Schema):
allow_extra_fields = True
chained_validators = [
PartialFormValidator(),
]
def test_partial_form_with_nested_forms_validators():
def invalid_eq(self, other):
if not isinstance(other, type(self)):
return False
# state is more debug than identity information
return (
self.msg == other.msg
and self.error_dict == other.error_dict
and self.error_list == other.error_list
)
Invalid.__eq__ = invalid_eq
nested_validator = NestedSchemaValidator()
partial_validator = PartialFormSchemaValidator()
try:
partial_validator.to_python(
{'chained_test': {'a': '1', 'a_confirm': '2', 'b': '3', 'b_confirm': '4'}}
)
except Invalid as e:
partial_error = e
else:
assert False
try:
nested_validator.to_python(
{'chained_test': {'a': '1', 'a_confirm': '2', 'b': '3', 'b_confirm': '4'}}
)
except Invalid as e:
expected_error = e
else:
assert False
assert expected_error.error_dict == partial_error.error_dict
try:
partial_validator.to_python(
{'chained_test': {}}
)
except Invalid as e:
partial_error = e
else:
assert False
try:
nested_validator.to_python(
{'chained_test': {}}
)
except Invalid as e:
expected_error = e
else:
assert False
assert expected_error.error_dict == partial_error.error_dictSample run:
$ nosetests test_sample.py
F
======================================================================
FAIL: test_sample.test_partial_form_with_nested_forms_validators
----------------------------------------------------------------------
Traceback (most recent call last):
File "VirtualEnvs/formencode/lib/python3.6/site-packages/nose/case.py", line 198, in runTest
self.test(*self.arg)
File "/Repositories/git/3rdparty/formencode/test_sample.py", line 89, in test_partial_form_with_nested_forms_validators
assert expected_error.error_dict == partial_error.error_dict
AssertionError:
>> assert Invalid('chained_test: a_confirm: Fields do not match\nb_confirm: Fields do not match', {'chained_test': {'a': '1', 'a_confirm': '2', 'b': '3', 'b_confirm': '4'}}, None, None, {'chained_test': Invalid('a_confirm: Fields do not match\nb_confirm: Fields do not match', {'a': '1', 'a_confirm': '2', 'b': '3', 'b_confirm': '4'}, None, None, {'a_confirm': 'Fields do not match', 'b_confirm': 'Fields do not match'})}).error_dict == Invalid('chained_test: a_confirm: Fields do not match \nb_confirm: Fields do not match', {'chained_test': {'a': '1', 'a_confirm': '2', 'b': '3', 'b_confirm': '4'}}, None, None, {'chained_test': {'a_confirm': 'Fields do not match', 'b_confirm': 'Fields do not match'}}).error_dict
----------------------------------------------------------------------
Ran 1 test in 0.023s
FAILED (failures=1)
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels