Break backwards compat: ValidationError.path is now in sorted order.
This commit is contained in:
@@ -15,5 +15,6 @@ Creating Validation Errors
|
||||
--------------------------
|
||||
|
||||
Any validating function that recurses into an instance (e.g. ``properties`` or
|
||||
``items``) must append to the :exc:`ValidationError.path` attribute of the
|
||||
error in order to properly maintain where in the instance the error occurred.
|
||||
``items``) must call ``appendleft`` on the :exc:`ValidationError.path`
|
||||
attribute of the error in order to properly maintain where in the instance the
|
||||
error occurred.
|
||||
|
||||
@@ -21,8 +21,8 @@ raised or returned, depending on which method or function is used.
|
||||
|
||||
.. attribute:: path
|
||||
|
||||
A list containing the path to the offending element (or [] if the error
|
||||
happened globally) in *reverse* order (i.e. deepest index first).
|
||||
A deque containing the path to the offending element (or an empty deque
|
||||
if the error happened globally).
|
||||
|
||||
|
||||
In case an invalid schema itself is encountered, a :exc:`SchemaError` is
|
||||
|
||||
@@ -55,7 +55,7 @@ class _Error(Exception):
|
||||
def __init__(self, message, validator=None, path=()):
|
||||
super(_Error, self).__init__(message, validator, path)
|
||||
self.message = message
|
||||
self.path = list(path)
|
||||
self.path = collections.deque(path)
|
||||
self.validator = validator
|
||||
|
||||
def __str__(self):
|
||||
@@ -202,12 +202,12 @@ class _Draft34CommonMixin(object):
|
||||
if self.is_type(items, "object"):
|
||||
for index, item in enumerate(instance):
|
||||
for error in self.iter_errors(item, items):
|
||||
error.path.append(index)
|
||||
error.path.appendleft(index)
|
||||
yield error
|
||||
else:
|
||||
for (index, item), subschema in zip(enumerate(instance), items):
|
||||
for error in self.iter_errors(item, subschema):
|
||||
error.path.append(index)
|
||||
error.path.appendleft(index)
|
||||
yield error
|
||||
|
||||
def validate_additionalItems(self, aI, instance, schema):
|
||||
@@ -366,7 +366,7 @@ class Draft3Validator(ValidatorMixin, _Draft34CommonMixin, object):
|
||||
for property, subschema in iteritems(properties):
|
||||
if property in instance:
|
||||
for error in self.iter_errors(instance[property], subschema):
|
||||
error.path.append(property)
|
||||
error.path.appendleft(property)
|
||||
yield error
|
||||
elif subschema.get("required", False):
|
||||
yield ValidationError(
|
||||
@@ -495,7 +495,7 @@ class Draft4Validator(ValidatorMixin, _Draft34CommonMixin, object):
|
||||
for property, subschema in iteritems(properties):
|
||||
if property in instance:
|
||||
for error in self.iter_errors(instance[property], subschema):
|
||||
error.path.append(property)
|
||||
error.path.appendleft(property)
|
||||
yield error
|
||||
|
||||
def validate_required(self, required, instance, schema):
|
||||
@@ -1054,7 +1054,7 @@ class ErrorTree(object):
|
||||
|
||||
for error in errors:
|
||||
container = self
|
||||
for element in reversed(error.path):
|
||||
for element in error.path:
|
||||
container = container[element]
|
||||
container.errors[error.validator] = error
|
||||
|
||||
|
||||
26
tests.py
26
tests.py
@@ -365,10 +365,10 @@ class TestValidationErrorDetails(unittest.TestCase):
|
||||
errors = self.validator.iter_errors(instance, schema)
|
||||
e1, e2, e3, e4 = sorted_errors(errors)
|
||||
|
||||
self.assertEqual(e1.path, ["bar"])
|
||||
self.assertEqual(e2.path, ["baz"])
|
||||
self.assertEqual(e3.path, ["baz"])
|
||||
self.assertEqual(e4.path, ["foo"])
|
||||
self.assertItemsEqual(e1.path, ["bar"])
|
||||
self.assertItemsEqual(e2.path, ["baz"])
|
||||
self.assertItemsEqual(e3.path, ["baz"])
|
||||
self.assertItemsEqual(e4.path, ["foo"])
|
||||
|
||||
self.assertEqual(e1.validator, "minItems")
|
||||
self.assertEqual(e2.validator, "enum")
|
||||
@@ -397,12 +397,12 @@ class TestValidationErrorDetails(unittest.TestCase):
|
||||
errors = self.validator.iter_errors(instance, schema)
|
||||
e1, e2, e3, e4, e5, e6 = sorted_errors(errors)
|
||||
|
||||
self.assertEqual(e1.path, [])
|
||||
self.assertEqual(e2.path, [0])
|
||||
self.assertEqual(e3.path, ["bar", 1])
|
||||
self.assertEqual(e4.path, ["bar", "bar", 1])
|
||||
self.assertEqual(e5.path, ["baz", "bar", 1])
|
||||
self.assertEqual(e6.path, ["foo", 1])
|
||||
self.assertItemsEqual(e1.path, [])
|
||||
self.assertItemsEqual(e2.path, [0])
|
||||
self.assertItemsEqual(e3.path, [1, "bar"])
|
||||
self.assertItemsEqual(e4.path, [1, "bar", "bar"])
|
||||
self.assertItemsEqual(e5.path, [1, "bar", "baz"])
|
||||
self.assertItemsEqual(e6.path, [1, "foo"])
|
||||
|
||||
self.assertEqual(e1.validator, "type")
|
||||
self.assertEqual(e2.validator, "type")
|
||||
@@ -439,7 +439,7 @@ class TestErrorTree(unittest.TestCase):
|
||||
def test_it_creates_a_child_tree_for_each_nested_path(self):
|
||||
errors = [
|
||||
ValidationError("a bar message", path=["bar"]),
|
||||
ValidationError("a bar -> 0 message", path=[0, "bar"]),
|
||||
ValidationError("a bar -> 0 message", path=["bar", 0]),
|
||||
]
|
||||
tree = ErrorTree(errors)
|
||||
self.assertIn(0, tree["bar"])
|
||||
@@ -447,8 +447,8 @@ class TestErrorTree(unittest.TestCase):
|
||||
|
||||
def test_children_have_their_errors_dicts_built(self):
|
||||
e1, e2 = (
|
||||
ValidationError("message 1", validator="foo", path=[0, "bar"]),
|
||||
ValidationError("message 2", validator="quux", path=[0, "bar"]),
|
||||
ValidationError("message 1", validator="foo", path=["bar", 0]),
|
||||
ValidationError("message 2", validator="quux", path=["bar", 0]),
|
||||
)
|
||||
tree = ErrorTree([e1, e2])
|
||||
self.assertEqual(tree["bar"][0].errors, {"foo" : e1, "quux" : e2})
|
||||
|
||||
Reference in New Issue
Block a user