Viewing file: exceptions.py (6.86 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
from collections import defaultdict, deque import itertools import pprint import textwrap
from jsonschema import _utils from jsonschema.compat import PY3, iteritems
WEAK_MATCHES = frozenset(["anyOf", "oneOf"]) STRONG_MATCHES = frozenset()
_unset = _utils.Unset()
class _Error(Exception): def __init__( self, message, validator=_unset, path=(), cause=None, context=(), validator_value=_unset, instance=_unset, schema=_unset, schema_path=(), parent=None, ): super(_Error, self).__init__( message, validator, path, cause, context, validator_value, instance, schema, schema_path, parent, ) self.message = message self.path = self.relative_path = deque(path) self.schema_path = self.relative_schema_path = deque(schema_path) self.context = list(context) self.cause = self.__cause__ = cause self.validator = validator self.validator_value = validator_value self.instance = instance self.schema = schema self.parent = parent
for error in context: error.parent = self
def __repr__(self): return "<%s: %r>" % (self.__class__.__name__, self.message)
def __unicode__(self): essential_for_verbose = ( self.validator, self.validator_value, self.instance, self.schema, ) if any(m is _unset for m in essential_for_verbose): return self.message
pschema = pprint.pformat(self.schema, width=72) pinstance = pprint.pformat(self.instance, width=72) return self.message + textwrap.dedent("""
Failed validating %r in schema%s: %s
On instance%s: %s """.rstrip() ) % ( self.validator, _utils.format_as_index(list(self.relative_schema_path)[:-1]), _utils.indent(pschema), _utils.format_as_index(self.relative_path), _utils.indent(pinstance), )
if PY3: __str__ = __unicode__ else: def __str__(self): return unicode(self).encode("utf-8")
@classmethod def create_from(cls, other): return cls(**other._contents())
@property def absolute_path(self): parent = self.parent if parent is None: return self.relative_path
path = deque(self.relative_path) path.extendleft(reversed(parent.absolute_path)) return path
@property def absolute_schema_path(self): parent = self.parent if parent is None: return self.relative_schema_path
path = deque(self.relative_schema_path) path.extendleft(reversed(parent.absolute_schema_path)) return path
def _set(self, **kwargs): for k, v in iteritems(kwargs): if getattr(self, k) is _unset: setattr(self, k, v)
def _contents(self): attrs = ( "message", "cause", "context", "validator", "validator_value", "path", "schema_path", "instance", "schema", "parent", ) return dict((attr, getattr(self, attr)) for attr in attrs)
class ValidationError(_Error): pass
class SchemaError(_Error): pass
class RefResolutionError(Exception): pass
class UnknownType(Exception): def __init__(self, type, instance, schema): self.type = type self.instance = instance self.schema = schema
def __unicode__(self): pschema = pprint.pformat(self.schema, width=72) pinstance = pprint.pformat(self.instance, width=72) return textwrap.dedent(""" Unknown type %r for validator with schema: %s
While checking instance: %s """.rstrip() ) % (self.type, _utils.indent(pschema), _utils.indent(pinstance))
if PY3: __str__ = __unicode__ else: def __str__(self): return unicode(self).encode("utf-8")
class FormatError(Exception): def __init__(self, message, cause=None): super(FormatError, self).__init__(message, cause) self.message = message self.cause = self.__cause__ = cause
def __unicode__(self): return self.message
if PY3: __str__ = __unicode__ else: def __str__(self): return self.message.encode("utf-8")
class ErrorTree(object): """ ErrorTrees make it easier to check which validations failed.
"""
_instance = _unset
def __init__(self, errors=()): self.errors = {} self._contents = defaultdict(self.__class__)
for error in errors: container = self for element in error.path: container = container[element] container.errors[error.validator] = error
container._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 return -len(error.path), validator not in weak, validator in strong return relevance
relevance = by_relevance()
def best_match(errors, key=relevance): errors = iter(errors) best = next(errors, None) if best is None: return best = max(itertools.chain([best], errors), key=key)
while best.context: best = min(best.context, key=key) return best
|