Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove ValidationError.fields #845

Merged
merged 1 commit into from
Jun 23, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ Other changes:
Returning ``None`` does not imply data were mutated (:issue:`347`). Thanks
:user:`tdevelioglu` for reporting.

Deprecations/Removals:

- ``ValidationError.fields`` is removed (:issue:`840`). Access field
instances from ``Schema.fields``.

3.0.0b11 (2018-05-20)
+++++++++++++++++++++
Expand Down
28 changes: 28 additions & 0 deletions docs/upgrading.rst
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,34 @@ will raise a :exc:`TypeError`.
# marshmallow.exceptions.ValidationError: {'_schema': ['Invalid input type.']}
schema.load(None)


``ValidationError.fields`` is removed
*************************************

:exc:`ValidationError <marshmallow.exceptions.ValidationError>` no
longer stores a list of `Field <marshmallow.fields.Field>` instances
associated with the validation errors.

If you need field instances associated with an error, you can access
them from ``schema.fields``.

.. code-block:: python


from marshmallow import Schema, fields, ValidationError

class MySchema(Schema):
foo = fields.Int()


schema = MySchema()

try:
schema.load({'foo': 'invalid'})
except ValidationError as error:
field = schema.fields['foo']
# ...

Overriding ``get_attribute``
****************************

Expand Down
4 changes: 1 addition & 3 deletions marshmallow/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class ValidationError(MarshmallowError):
:param list fields: `Field` objects to which the error applies.
"""

def __init__(self, message, field_names=None, fields=None,
def __init__(self, message, field_names=None,
data=None, valid_data=None, **kwargs):
if not isinstance(message, dict) and not isinstance(message, list):
messages = [message]
Expand All @@ -29,8 +29,6 @@ def __init__(self, message, field_names=None, fields=None,
#: If a `dict`, the keys will be field names and the values will be lists of
#: messages.
self.messages = messages
#: List of field objects which failed validation.
self.fields = fields
if isinstance(field_names, basestring):
#: List of field_names which failed validation.
self.field_names = [field_names]
Expand Down
23 changes: 3 additions & 20 deletions marshmallow/marshalling.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ class ErrorStore(object):
def __init__(self):
#: Dictionary of errors stored during serialization
self.errors = {}
#: List of `Field` objects which have validation errors
self.error_fields = []
#: List of field_names which have validation errors
self.error_field_names = []
#: True while (de)serializing a collection
Expand All @@ -48,10 +46,8 @@ def get_errors(self, index=None):
errors = self.errors
return errors

def store_error(self, field_name, error, field_obj=None, index=None):
def store_error(self, field_name, error, index=None):
self.error_kwargs.update(error.kwargs)
if field_obj is not None:
self.error_fields.append(field_obj)
self.error_field_names.append(field_name)
errors = self.get_errors(index=index)
# Warning: Mutation!
Expand All @@ -65,22 +61,20 @@ def store_error(self, field_name, error, field_obj=None, index=None):
# on the ValidationError's valid_data attribute
return error.valid_data or missing

def call_and_store(self, getter_func, data, field_name, field_obj, index=None):
def call_and_store(self, getter_func, data, field_name, index=None):
"""Call ``getter_func`` with ``data`` as its argument, and store any `ValidationErrors`.

:param callable getter_func: Function for getting the serialized/deserialized
value from ``data``.
:param data: The data passed to ``getter_func``.
:param str field_name: Field name.
:param FieldABC field_obj: Field object that performs the
serialization/deserialization behavior.
:param int index: Index of the item being validated, if validating a collection,
otherwise `None`.
"""
try:
value = getter_func(data)
except ValidationError as error:
return self.store_error(field_name, error, field_obj, index)
return self.store_error(field_name, error, index)
return value


Expand Down Expand Up @@ -126,7 +120,6 @@ def serialize(self, obj, fields_dict, many=False,
raise ValidationError(
self.errors,
field_names=self.error_field_names,
fields=self.error_fields,
data=ret,
)
return ret
Expand All @@ -142,7 +135,6 @@ def serialize(self, obj, fields_dict, many=False,
getter_func=getter,
data=obj,
field_name=key,
field_obj=field_obj,
index=(index if index_errors else None)
)
if value is missing:
Expand All @@ -153,7 +145,6 @@ def serialize(self, obj, fields_dict, many=False,
raise ValidationError(
self.errors,
field_names=self.error_field_names,
fields=self.error_fields,
data=ret
)
return ret
Expand Down Expand Up @@ -190,13 +181,9 @@ def run_validator(self, validator_func, output,
# Store or reraise errors
if err.field_names:
field_names = err.field_names
field_objs = [fields_dict[each] if each in fields_dict else None
for each in field_names]
else:
field_names = [SCHEMA]
field_objs = []
self.error_field_names = field_names
self.error_fields = field_objs
for field_name in field_names:
if isinstance(err.messages, (list, tuple)):
# self.errors[field_name] may be a dict if schemas are nested
Expand Down Expand Up @@ -244,7 +231,6 @@ def deserialize(self, data, fields_dict, many=False, partial=False,
raise ValidationError(
self.errors,
field_names=self.error_field_names,
fields=self.error_fields,
data=ret,
)
return ret
Expand All @@ -264,7 +250,6 @@ def deserialize(self, data, fields_dict, many=False, partial=False,
input=data, input_type=data.__class__.__name__
)
self.error_field_names = [SCHEMA]
self.error_fields = []
errors = self.get_errors()
errors.setdefault(SCHEMA, []).append(msg)
# Input data type is incorrect, so we can bail out early
Expand All @@ -281,7 +266,6 @@ def deserialize(self, data, fields_dict, many=False, partial=False,
getter_func=getter,
data=raw_value,
field_name=field_name,
field_obj=field_obj,
index=(index if index_errors else None)
)
if value is not missing:
Expand All @@ -306,7 +290,6 @@ def deserialize(self, data, fields_dict, many=False, partial=False,
raise ValidationError(
self.errors,
field_names=self.error_field_names,
fields=self.error_fields,
data=ret,
)
return ret
Expand Down
6 changes: 1 addition & 5 deletions marshmallow/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,6 @@ def dump(self, obj, many=None, update_fields=True):
exc = ValidationError(
errors,
field_names=marshal.error_field_names,
fields=marshal.error_fields,
data=obj,
valid_data=result,
**marshal.error_kwargs
Expand Down Expand Up @@ -659,7 +658,6 @@ def _do_load(self, data, many=None, partial=None, unknown=None,
exc = ValidationError(
errors,
field_names=unmarshal.error_field_names,
fields=unmarshal.error_fields,
data=data,
valid_data=result,
**unmarshal.error_kwargs
Expand Down Expand Up @@ -859,7 +857,6 @@ def _invoke_field_validators(self, unmarshal, data, many):
getter_func=validator,
data=value,
field_name=field_name,
field_obj=field_obj,
index=(idx if self.opts.index_errors else None)
)
if validated_value is missing:
Expand All @@ -873,8 +870,7 @@ def _invoke_field_validators(self, unmarshal, data, many):
validated_value = unmarshal.call_and_store(
getter_func=validator,
data=value,
field_name=field_name,
field_obj=field_obj
field_name=field_name
)
if validated_value is missing:
data.pop(field_name, None)
Expand Down
1 change: 0 additions & 1 deletion tests/test_deserialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -1558,4 +1558,3 @@ class MySchema(Schema):
assert 'Invalid input type.' in str(excinfo)
exc = excinfo.value
assert exc.field_names == ['_schema']
assert exc.fields == []
9 changes: 0 additions & 9 deletions tests/test_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ def test_dump_mode_raises_error(SchemaClass):
with pytest.raises(ValidationError) as excinfo:
s.dump(bad_user)
exc = excinfo.value
assert type(exc.fields[0]) == fields.Decimal
assert exc.field_names[0] == 'balance'

assert type(exc.messages) == dict
Expand Down Expand Up @@ -164,13 +163,11 @@ class MySchema(Schema):
with pytest.raises(ValidationError) as excinfo:
schema.dump(User('Joe', age='dummy'))
exc = excinfo.value
assert len(exc.fields) == 1
assert len(exc.field_names) == 1

with pytest.raises(ValidationError) as excinfo:
schema.dump(User('Joe', age='__dummy'))

assert len(exc.fields) == 1
assert len(exc.field_names) == 1

def test_load_resets_error_fields():
Expand All @@ -181,13 +178,11 @@ class MySchema(Schema):
with pytest.raises(ValidationError) as excinfo:
schema.load({'name': 'Joe', 'email': 'not-valid'})
exc = excinfo.value
assert len(exc.fields) == 1
assert len(exc.field_names) == 1

with pytest.raises(ValidationError) as excinfo:
schema.load({'name': 'Joe', 'email': '__invalid'})

assert len(exc.fields) == 1
assert len(exc.field_names) == 1

def test_load_resets_error_kwargs():
Expand Down Expand Up @@ -1476,7 +1471,6 @@ def handle_error(self, error, data):
assert type(error) is ValidationError
assert 'email' in error.messages
assert error.field_names == ['email']
assert error.fields == [self.fields['email']]
assert data == in_data
raise CustomError('Something bad happened')

Expand All @@ -1494,7 +1488,6 @@ def handle_error(self, error, data):
assert type(error) is ValidationError
assert 'email' in error.messages
assert error.field_names == ['email']
assert error.fields == [self.fields['email']]
assert data == in_data
raise CustomError('Something bad happened')

Expand All @@ -1516,7 +1509,6 @@ def handle_error(self, error, data):
assert type(error) is ValidationError
assert 'num' in error.messages
assert error.field_names == ['num']
assert error.fields == [self.fields['num']]
assert data == in_data
raise CustomError('Something bad happened')

Expand All @@ -1537,7 +1529,6 @@ def handle_error(self, error, data):
assert type(error) is ValidationError
assert '_schema' in error.messages
assert error.field_names == ['_schema']
assert error.fields == []
assert data == in_data
raise CustomError('Something bad happened')

Expand Down