Source code for jsonschema.exceptions

from collections import defaultdict, deque
import itertools
import pprint
import textwrap

import attr

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 %s%s:
            %s

            On %s%s:
            %s
            """.rstrip()
        ) % (
            self.validator,
            self._word_for_schema_in_error_message,
            _utils.format_as_index(list(self.relative_schema_path)[:-1]),
            _utils.indent(pschema),
            self._word_for_instance_in_error_message,
            _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)


[docs]class ValidationError(_Error): _word_for_schema_in_error_message = "schema" _word_for_instance_in_error_message = "instance"
[docs]class SchemaError(_Error): _word_for_schema_in_error_message = "metaschema" _word_for_instance_in_error_message = "schema"
[docs]@attr.s(hash=True) class RefResolutionError(Exception): _cause = attr.ib() def __str__(self): return str(self._cause)
[docs]class UndefinedTypeCheck(Exception): def __init__(self, type): self.type = type def __unicode__(self): return "Type %r is unknown to this type checker" % self.type if PY3: __str__ = __unicode__ else: def __str__(self): return unicode(self).encode("utf-8")
[docs]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")
[docs]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")
[docs]class ErrorTree(object): """ ErrorTrees make it easier to check which validations failed. """ _instance = _unset
[docs] 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
[docs] def __contains__(self, index): """ Check whether ``instance[index]`` has any errors. """ return index in self._contents
[docs] 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 `exceptions.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
[docs] def __iter__(self): """ Iterate (non-recursively) over the indices in the instance with errors. """ return iter(self._contents)
[docs] def __len__(self): """ Same as `total_errors`. """ return self.total_errors
[docs] 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
[docs]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()
[docs]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