Break backwards compat: ValidationError.path is now in sorted order.

This commit is contained in:
Julian Berman
2013-02-24 13:23:35 -05:00
parent 2341a25b0e
commit 6feff85365
4 changed files with 24 additions and 23 deletions

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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})