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 Any validating function that recurses into an instance (e.g. ``properties`` or
``items``) must append to the :exc:`ValidationError.path` attribute of the ``items``) must call ``appendleft`` on the :exc:`ValidationError.path`
error in order to properly maintain where in the instance the error occurred. 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 .. attribute:: path
A list containing the path to the offending element (or [] if the error A deque containing the path to the offending element (or an empty deque
happened globally) in *reverse* order (i.e. deepest index first). if the error happened globally).
In case an invalid schema itself is encountered, a :exc:`SchemaError` is 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=()): def __init__(self, message, validator=None, path=()):
super(_Error, self).__init__(message, validator, path) super(_Error, self).__init__(message, validator, path)
self.message = message self.message = message
self.path = list(path) self.path = collections.deque(path)
self.validator = validator self.validator = validator
def __str__(self): def __str__(self):
@@ -202,12 +202,12 @@ class _Draft34CommonMixin(object):
if self.is_type(items, "object"): if self.is_type(items, "object"):
for index, item in enumerate(instance): for index, item in enumerate(instance):
for error in self.iter_errors(item, items): for error in self.iter_errors(item, items):
error.path.append(index) error.path.appendleft(index)
yield error yield error
else: else:
for (index, item), subschema in zip(enumerate(instance), items): for (index, item), subschema in zip(enumerate(instance), items):
for error in self.iter_errors(item, subschema): for error in self.iter_errors(item, subschema):
error.path.append(index) error.path.appendleft(index)
yield error yield error
def validate_additionalItems(self, aI, instance, schema): def validate_additionalItems(self, aI, instance, schema):
@@ -366,7 +366,7 @@ class Draft3Validator(ValidatorMixin, _Draft34CommonMixin, object):
for property, subschema in iteritems(properties): for property, subschema in iteritems(properties):
if property in instance: if property in instance:
for error in self.iter_errors(instance[property], subschema): for error in self.iter_errors(instance[property], subschema):
error.path.append(property) error.path.appendleft(property)
yield error yield error
elif subschema.get("required", False): elif subschema.get("required", False):
yield ValidationError( yield ValidationError(
@@ -495,7 +495,7 @@ class Draft4Validator(ValidatorMixin, _Draft34CommonMixin, object):
for property, subschema in iteritems(properties): for property, subschema in iteritems(properties):
if property in instance: if property in instance:
for error in self.iter_errors(instance[property], subschema): for error in self.iter_errors(instance[property], subschema):
error.path.append(property) error.path.appendleft(property)
yield error yield error
def validate_required(self, required, instance, schema): def validate_required(self, required, instance, schema):
@@ -1054,7 +1054,7 @@ class ErrorTree(object):
for error in errors: for error in errors:
container = self container = self
for element in reversed(error.path): for element in error.path:
container = container[element] container = container[element]
container.errors[error.validator] = error container.errors[error.validator] = error

View File

@@ -365,10 +365,10 @@ class TestValidationErrorDetails(unittest.TestCase):
errors = self.validator.iter_errors(instance, schema) errors = self.validator.iter_errors(instance, schema)
e1, e2, e3, e4 = sorted_errors(errors) e1, e2, e3, e4 = sorted_errors(errors)
self.assertEqual(e1.path, ["bar"]) self.assertItemsEqual(e1.path, ["bar"])
self.assertEqual(e2.path, ["baz"]) self.assertItemsEqual(e2.path, ["baz"])
self.assertEqual(e3.path, ["baz"]) self.assertItemsEqual(e3.path, ["baz"])
self.assertEqual(e4.path, ["foo"]) self.assertItemsEqual(e4.path, ["foo"])
self.assertEqual(e1.validator, "minItems") self.assertEqual(e1.validator, "minItems")
self.assertEqual(e2.validator, "enum") self.assertEqual(e2.validator, "enum")
@@ -397,12 +397,12 @@ class TestValidationErrorDetails(unittest.TestCase):
errors = self.validator.iter_errors(instance, schema) errors = self.validator.iter_errors(instance, schema)
e1, e2, e3, e4, e5, e6 = sorted_errors(errors) e1, e2, e3, e4, e5, e6 = sorted_errors(errors)
self.assertEqual(e1.path, []) self.assertItemsEqual(e1.path, [])
self.assertEqual(e2.path, [0]) self.assertItemsEqual(e2.path, [0])
self.assertEqual(e3.path, ["bar", 1]) self.assertItemsEqual(e3.path, [1, "bar"])
self.assertEqual(e4.path, ["bar", "bar", 1]) self.assertItemsEqual(e4.path, [1, "bar", "bar"])
self.assertEqual(e5.path, ["baz", "bar", 1]) self.assertItemsEqual(e5.path, [1, "bar", "baz"])
self.assertEqual(e6.path, ["foo", 1]) self.assertItemsEqual(e6.path, [1, "foo"])
self.assertEqual(e1.validator, "type") self.assertEqual(e1.validator, "type")
self.assertEqual(e2.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): def test_it_creates_a_child_tree_for_each_nested_path(self):
errors = [ errors = [
ValidationError("a bar message", path=["bar"]), 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) tree = ErrorTree(errors)
self.assertIn(0, tree["bar"]) self.assertIn(0, tree["bar"])
@@ -447,8 +447,8 @@ class TestErrorTree(unittest.TestCase):
def test_children_have_their_errors_dicts_built(self): def test_children_have_their_errors_dicts_built(self):
e1, e2 = ( e1, e2 = (
ValidationError("message 1", validator="foo", path=[0, "bar"]), ValidationError("message 1", validator="foo", path=["bar", 0]),
ValidationError("message 2", validator="quux", path=[0, "bar"]), ValidationError("message 2", validator="quux", path=["bar", 0]),
) )
tree = ErrorTree([e1, e2]) tree = ErrorTree([e1, e2])
self.assertEqual(tree["bar"][0].errors, {"foo" : e1, "quux" : e2}) self.assertEqual(tree["bar"][0].errors, {"foo" : e1, "quux" : e2})