summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulian Berman <Julian@GrayVines.com>2013-10-30 08:13:21 -0400
committerJulian Berman <Julian@GrayVines.com>2013-10-30 08:13:21 -0400
commit699528300b90a5d3de79a69838d604ef6fdd9fea (patch)
tree1e85628f2ed58b754b1fcb70cde38c38a357d155
parentbab6e745c2cafa77c9b585875d75e5634a810168 (diff)
downloadjsonschema-699528300b90a5d3de79a69838d604ef6fdd9fea.tar.gz
Move ErrorTree closer to its friends.
-rw-r--r--jsonschema/__init__.py4
-rw-r--r--jsonschema/exceptions.py76
-rw-r--r--jsonschema/tests/test_exceptions.py62
-rw-r--r--jsonschema/tests/test_validators.py65
-rw-r--r--jsonschema/validators.py78
5 files changed, 142 insertions, 143 deletions
diff --git a/jsonschema/__init__.py b/jsonschema/__init__.py
index 34ca151..bd2077d 100644
--- a/jsonschema/__init__.py
+++ b/jsonschema/__init__.py
@@ -10,13 +10,13 @@ instance under a schema, and will create a validator for you.
"""
from jsonschema.exceptions import (
- FormatError, RefResolutionError, SchemaError, ValidationError
+ ErrorTree, FormatError, RefResolutionError, SchemaError, ValidationError
)
from jsonschema._format import (
FormatChecker, draft3_format_checker, draft4_format_checker,
)
from jsonschema.validators import (
- ErrorTree, Draft3Validator, Draft4Validator, RefResolver, validate
+ Draft3Validator, Draft4Validator, RefResolver, validate
)
diff --git a/jsonschema/exceptions.py b/jsonschema/exceptions.py
index 26fafbe..6b863ec 100644
--- a/jsonschema/exceptions.py
+++ b/jsonschema/exceptions.py
@@ -136,6 +136,82 @@ class FormatError(Exception):
__str__ = __unicode__
+class ErrorTree(object):
+ """
+ ErrorTrees make it easier to check which validations failed.
+
+ """
+
+ _instance = _unset
+
+ def __init__(self, errors=()):
+ self.errors = {}
+ self._contents = collections.defaultdict(self.__class__)
+
+ for error in errors:
+ container = self
+ for element in error.path:
+ container = container[element]
+ container.errors[error.validator] = error
+
+ self._instance = error.instance
+
+ def __contains__(self, index):
+ """
+ Check whether ``instance[index]`` has any errors.
+
+ """
+
+ return index in self._contents
+
+ def __getitem__(self, index):
+ """
+ Retrieve the child tree one level down at the given ``index``.
+
+ If the index is not in the instance that this tree corresponds to and
+ is not known by this tree, whatever error would be raised by
+ ``instance.__getitem__`` will be propagated (usually this is some
+ subclass of :class:`LookupError`.
+
+ """
+
+ if self._instance is not _unset and index not in self:
+ self._instance[index]
+ return self._contents[index]
+
+ def __setitem__(self, index, value):
+ self._contents[index] = value
+
+ def __iter__(self):
+ """
+ Iterate (non-recursively) over the indices in the instance with errors.
+
+ """
+
+ return iter(self._contents)
+
+ def __len__(self):
+ """
+ Same as :attr:`total_errors`.
+
+ """
+
+ return self.total_errors
+
+ def __repr__(self):
+ return "<%s (%s total errors)>" % (self.__class__.__name__, len(self))
+
+ @property
+ def total_errors(self):
+ """
+ The total number of errors in the entire tree, including children.
+
+ """
+
+ child_errors = sum(len(tree) for _, tree in iteritems(self._contents))
+ return len(self.errors) + child_errors
+
+
def by_relevance(weak=WEAK_MATCHES, strong=STRONG_MATCHES):
def relevance(error):
validator = error.validator
diff --git a/jsonschema/tests/test_exceptions.py b/jsonschema/tests/test_exceptions.py
index 2014a64..0466596 100644
--- a/jsonschema/tests/test_exceptions.py
+++ b/jsonschema/tests/test_exceptions.py
@@ -1,3 +1,5 @@
+import mock
+
from jsonschema import Draft4Validator, exceptions
from jsonschema.tests.compat import unittest
@@ -208,3 +210,63 @@ class TestByRelevance(unittest.TestCase):
match = max([strong, normal, weak], key=best_match)
self.assertIs(match, strong)
+
+
+class TestErrorTree(unittest.TestCase):
+ def test_it_knows_how_many_total_errors_it_contains(self):
+ errors = [mock.MagicMock() for _ in range(8)]
+ tree = exceptions.ErrorTree(errors)
+ self.assertEqual(tree.total_errors, 8)
+
+ def test_it_contains_an_item_if_the_item_had_an_error(self):
+ errors = [exceptions.ValidationError("a message", path=["bar"])]
+ tree = exceptions.ErrorTree(errors)
+ self.assertIn("bar", tree)
+
+ def test_it_does_not_contain_an_item_if_the_item_had_no_error(self):
+ errors = [exceptions.ValidationError("a message", path=["bar"])]
+ tree = exceptions.ErrorTree(errors)
+ self.assertNotIn("foo", tree)
+
+ def test_validators_that_failed_appear_in_errors_dict(self):
+ error = exceptions.ValidationError("a message", validator="foo")
+ tree = exceptions.ErrorTree([error])
+ self.assertEqual(tree.errors, {"foo" : error})
+
+ def test_it_creates_a_child_tree_for_each_nested_path(self):
+ errors = [
+ exceptions.ValidationError("a bar message", path=["bar"]),
+ exceptions.ValidationError("a bar -> 0 message", path=["bar", 0]),
+ ]
+ tree = exceptions.ErrorTree(errors)
+ self.assertIn(0, tree["bar"])
+ self.assertNotIn(1, tree["bar"])
+
+ def test_children_have_their_errors_dicts_built(self):
+ e1, e2 = (
+ exceptions.ValidationError("1", validator="foo", path=["bar", 0]),
+ exceptions.ValidationError("2", validator="quux", path=["bar", 0]),
+ )
+ tree = exceptions.ErrorTree([e1, e2])
+ self.assertEqual(tree["bar"][0].errors, {"foo" : e1, "quux" : e2})
+
+ def test_it_does_not_contain_subtrees_that_are_not_in_the_instance(self):
+ error = exceptions.ValidationError("123", validator="foo", instance=[])
+ tree = exceptions.ErrorTree([error])
+
+ with self.assertRaises(IndexError):
+ tree[0]
+
+ def test_if_its_in_the_tree_anyhow_it_does_not_raise_an_error(self):
+ """
+ If a validator is dumb (like :validator:`required` in draft 3) and
+ refers to a path that isn't in the instance, the tree still properly
+ returns a subtree for that path.
+
+ """
+
+ error = exceptions.ValidationError(
+ "a message", validator="foo", instance={}, path=["foo"],
+ )
+ tree = exceptions.ErrorTree([error])
+ self.assertIsInstance(tree["foo"], exceptions.ErrorTree)
diff --git a/jsonschema/tests/test_validators.py b/jsonschema/tests/test_validators.py
index 1081ec1..bfc5871 100644
--- a/jsonschema/tests/test_validators.py
+++ b/jsonschema/tests/test_validators.py
@@ -8,7 +8,7 @@ from jsonschema import FormatChecker, ValidationError
from jsonschema.compat import PY3
from jsonschema.tests.compat import mock, unittest
from jsonschema.validators import (
- RefResolutionError, UnknownType, ErrorTree, Draft3Validator,
+ RefResolutionError, UnknownType, Draft3Validator,
Draft4Validator, RefResolver, create, extend, validator_for, validate,
)
@@ -518,69 +518,6 @@ class TestValidationErrorDetails(unittest.TestCase):
self.assertEqual(e2.validator, "minimum")
-class TestErrorTree(unittest.TestCase):
- def setUp(self):
- self.validator = Draft3Validator({})
-
- def test_it_knows_how_many_total_errors_it_contains(self):
- errors = [mock.MagicMock() for _ in range(8)]
- tree = ErrorTree(errors)
- self.assertEqual(tree.total_errors, 8)
-
- def test_it_contains_an_item_if_the_item_had_an_error(self):
- errors = [ValidationError("a message", path=["bar"])]
- tree = ErrorTree(errors)
- self.assertIn("bar", tree)
-
- def test_it_does_not_contain_an_item_if_the_item_had_no_error(self):
- errors = [ValidationError("a message", path=["bar"])]
- tree = ErrorTree(errors)
- self.assertNotIn("foo", tree)
-
- def test_validators_that_failed_appear_in_errors_dict(self):
- error = ValidationError("a message", validator="foo")
- tree = ErrorTree([error])
- self.assertEqual(tree.errors, {"foo" : error})
-
- 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=["bar", 0]),
- ]
- tree = ErrorTree(errors)
- self.assertIn(0, tree["bar"])
- self.assertNotIn(1, tree["bar"])
-
- def test_children_have_their_errors_dicts_built(self):
- e1, e2 = (
- 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})
-
- def test_it_does_not_contain_subtrees_that_are_not_in_the_instance(self):
- error = ValidationError("a message", validator="foo", instance=[])
- tree = ErrorTree([error])
-
- with self.assertRaises(IndexError):
- tree[0]
-
- def test_if_its_in_the_tree_anyhow_it_does_not_raise_an_error(self):
- """
- If a validator is dumb (like :validator:`required` in draft 3) and
- refers to a path that isn't in the instance, the tree still properly
- returns a subtree for that path.
-
- """
-
- error = ValidationError(
- "a message", validator="foo", instance={}, path=["foo"],
- )
- tree = ErrorTree([error])
- self.assertIsInstance(tree["foo"], ErrorTree)
-
-
class ValidatorTestMixin(object):
def setUp(self):
self.instance = mock.Mock()
diff --git a/jsonschema/validators.py b/jsonschema/validators.py
index 7044428..fab8201 100644
--- a/jsonschema/validators.py
+++ b/jsonschema/validators.py
@@ -1,6 +1,5 @@
from __future__ import division, unicode_literals
-import collections
import contextlib
import json
import numbers
@@ -15,6 +14,7 @@ from jsonschema.compat import (
PY3, Sequence, urljoin, urlsplit, urldefrag, unquote, urlopen,
str_types, int_types, iteritems,
)
+from jsonschema.exceptions import ErrorTree # For backwards compatibility
from jsonschema.exceptions import RefResolutionError, SchemaError, UnknownType
@@ -379,82 +379,6 @@ class RefResolver(object):
return result
-class ErrorTree(object):
- """
- ErrorTrees make it easier to check which validations failed.
-
- """
-
- _instance = _unset
-
- def __init__(self, errors=()):
- self.errors = {}
- self._contents = collections.defaultdict(self.__class__)
-
- for error in errors:
- container = self
- for element in error.path:
- container = container[element]
- container.errors[error.validator] = error
-
- self._instance = error.instance
-
- def __contains__(self, index):
- """
- Check whether ``instance[index]`` has any errors.
-
- """
-
- return index in self._contents
-
- def __getitem__(self, index):
- """
- Retrieve the child tree one level down at the given ``index``.
-
- If the index is not in the instance that this tree corresponds to and
- is not known by this tree, whatever error would be raised by
- ``instance.__getitem__`` will be propagated (usually this is some
- subclass of :class:`LookupError`.
-
- """
-
- if self._instance is not _unset and index not in self:
- self._instance[index]
- return self._contents[index]
-
- def __setitem__(self, index, value):
- self._contents[index] = value
-
- def __iter__(self):
- """
- Iterate (non-recursively) over the indices in the instance with errors.
-
- """
-
- return iter(self._contents)
-
- def __len__(self):
- """
- Same as :attr:`total_errors`.
-
- """
-
- return self.total_errors
-
- def __repr__(self):
- return "<%s (%s total errors)>" % (self.__class__.__name__, len(self))
-
- @property
- def total_errors(self):
- """
- The total number of errors in the entire tree, including children.
-
- """
-
- child_errors = sum(len(tree) for _, tree in iteritems(self._contents))
- return len(self.errors) + child_errors
-
-
def validator_for(schema, default=_unset):
if default is _unset:
default = Draft4Validator