|
Packit |
534379 |
import collections
|
|
Packit |
534379 |
import itertools
|
|
Packit |
534379 |
import pprint
|
|
Packit |
534379 |
import textwrap
|
|
Packit |
534379 |
|
|
Packit |
534379 |
from jsonschema import _utils
|
|
Packit |
534379 |
from jsonschema.compat import PY3, iteritems
|
|
Packit |
534379 |
|
|
Packit |
534379 |
|
|
Packit |
534379 |
WEAK_MATCHES = frozenset(["anyOf", "oneOf"])
|
|
Packit |
534379 |
STRONG_MATCHES = frozenset()
|
|
Packit |
534379 |
|
|
Packit |
534379 |
_unset = _utils.Unset()
|
|
Packit |
534379 |
|
|
Packit |
534379 |
|
|
Packit |
534379 |
class _Error(Exception):
|
|
Packit |
534379 |
def __init__(
|
|
Packit |
534379 |
self, message, validator=_unset, path=(), cause=None, context=(),
|
|
Packit |
534379 |
validator_value=_unset, instance=_unset, schema=_unset, schema_path=(),
|
|
Packit |
534379 |
):
|
|
Packit |
534379 |
self.message = message
|
|
Packit |
534379 |
self.path = collections.deque(path)
|
|
Packit |
534379 |
self.schema_path = collections.deque(schema_path)
|
|
Packit |
534379 |
self.context = list(context)
|
|
Packit |
534379 |
self.cause = self.__cause__ = cause
|
|
Packit |
534379 |
self.validator = validator
|
|
Packit |
534379 |
self.validator_value = validator_value
|
|
Packit |
534379 |
self.instance = instance
|
|
Packit |
534379 |
self.schema = schema
|
|
Packit |
534379 |
|
|
Packit |
534379 |
def __repr__(self):
|
|
Packit |
534379 |
return "<%s: %r>" % (self.__class__.__name__, self.message)
|
|
Packit |
534379 |
|
|
Packit |
534379 |
def __str__(self):
|
|
Packit |
534379 |
return unicode(self).encode("utf-8")
|
|
Packit |
534379 |
|
|
Packit |
534379 |
def __unicode__(self):
|
|
Packit |
534379 |
if _unset in (
|
|
Packit |
534379 |
self.validator, self.validator_value, self.instance, self.schema,
|
|
Packit |
534379 |
):
|
|
Packit |
534379 |
return self.message
|
|
Packit |
534379 |
|
|
Packit |
534379 |
path = _utils.format_as_index(self.path)
|
|
Packit |
534379 |
schema_path = _utils.format_as_index(list(self.schema_path)[:-1])
|
|
Packit |
534379 |
|
|
Packit |
534379 |
pschema = pprint.pformat(self.schema, width=72)
|
|
Packit |
534379 |
pinstance = pprint.pformat(self.instance, width=72)
|
|
Packit |
534379 |
return self.message + textwrap.dedent("""
|
|
Packit |
534379 |
|
|
Packit |
534379 |
Failed validating %r in schema%s:
|
|
Packit |
534379 |
%s
|
|
Packit |
534379 |
|
|
Packit |
534379 |
On instance%s:
|
|
Packit |
534379 |
%s
|
|
Packit |
534379 |
""".rstrip()
|
|
Packit |
534379 |
) % (
|
|
Packit |
534379 |
self.validator,
|
|
Packit |
534379 |
schema_path,
|
|
Packit |
534379 |
_utils.indent(pschema),
|
|
Packit |
534379 |
path,
|
|
Packit |
534379 |
_utils.indent(pinstance),
|
|
Packit |
534379 |
)
|
|
Packit |
534379 |
|
|
Packit |
534379 |
if PY3:
|
|
Packit |
534379 |
__str__ = __unicode__
|
|
Packit |
534379 |
|
|
Packit |
534379 |
@classmethod
|
|
Packit |
534379 |
def create_from(cls, other):
|
|
Packit |
534379 |
return cls(**other._contents())
|
|
Packit |
534379 |
|
|
Packit |
534379 |
def _set(self, **kwargs):
|
|
Packit |
534379 |
for k, v in iteritems(kwargs):
|
|
Packit |
534379 |
if getattr(self, k) is _unset:
|
|
Packit |
534379 |
setattr(self, k, v)
|
|
Packit |
534379 |
|
|
Packit |
534379 |
def _contents(self):
|
|
Packit |
534379 |
return dict(
|
|
Packit |
534379 |
(attr, getattr(self, attr)) for attr in (
|
|
Packit |
534379 |
"message", "cause", "context", "path", "schema_path",
|
|
Packit |
534379 |
"validator", "validator_value", "instance", "schema"
|
|
Packit |
534379 |
)
|
|
Packit |
534379 |
)
|
|
Packit |
534379 |
|
|
Packit |
534379 |
|
|
Packit |
534379 |
class ValidationError(_Error):
|
|
Packit |
534379 |
pass
|
|
Packit |
534379 |
|
|
Packit |
534379 |
|
|
Packit |
534379 |
class SchemaError(_Error):
|
|
Packit |
534379 |
pass
|
|
Packit |
534379 |
|
|
Packit |
534379 |
|
|
Packit |
534379 |
class RefResolutionError(Exception):
|
|
Packit |
534379 |
pass
|
|
Packit |
534379 |
|
|
Packit |
534379 |
|
|
Packit |
534379 |
class UnknownType(Exception):
|
|
Packit |
534379 |
def __init__(self, type, instance, schema):
|
|
Packit |
534379 |
self.type = type
|
|
Packit |
534379 |
self.instance = instance
|
|
Packit |
534379 |
self.schema = schema
|
|
Packit |
534379 |
|
|
Packit |
534379 |
def __str__(self):
|
|
Packit |
534379 |
return unicode(self).encode("utf-8")
|
|
Packit |
534379 |
|
|
Packit |
534379 |
def __unicode__(self):
|
|
Packit |
534379 |
pschema = pprint.pformat(self.schema, width=72)
|
|
Packit |
534379 |
pinstance = pprint.pformat(self.instance, width=72)
|
|
Packit |
534379 |
return textwrap.dedent("""
|
|
Packit |
534379 |
Unknown type %r for validator with schema:
|
|
Packit |
534379 |
%s
|
|
Packit |
534379 |
|
|
Packit |
534379 |
While checking instance:
|
|
Packit |
534379 |
%s
|
|
Packit |
534379 |
""".rstrip()
|
|
Packit |
534379 |
) % (self.type, _utils.indent(pschema), _utils.indent(pinstance))
|
|
Packit |
534379 |
|
|
Packit |
534379 |
if PY3:
|
|
Packit |
534379 |
__str__ = __unicode__
|
|
Packit |
534379 |
|
|
Packit |
534379 |
|
|
Packit |
534379 |
class FormatError(Exception):
|
|
Packit |
534379 |
def __init__(self, message, cause=None):
|
|
Packit |
534379 |
super(FormatError, self).__init__(message, cause)
|
|
Packit |
534379 |
self.message = message
|
|
Packit |
534379 |
self.cause = self.__cause__ = cause
|
|
Packit |
534379 |
|
|
Packit |
534379 |
def __str__(self):
|
|
Packit |
534379 |
return self.message.encode("utf-8")
|
|
Packit |
534379 |
|
|
Packit |
534379 |
def __unicode__(self):
|
|
Packit |
534379 |
return self.message
|
|
Packit |
534379 |
|
|
Packit |
534379 |
if PY3:
|
|
Packit |
534379 |
__str__ = __unicode__
|
|
Packit |
534379 |
|
|
Packit |
534379 |
|
|
Packit |
534379 |
class ErrorTree(object):
|
|
Packit |
534379 |
"""
|
|
Packit |
534379 |
ErrorTrees make it easier to check which validations failed.
|
|
Packit |
534379 |
|
|
Packit |
534379 |
"""
|
|
Packit |
534379 |
|
|
Packit |
534379 |
_instance = _unset
|
|
Packit |
534379 |
|
|
Packit |
534379 |
def __init__(self, errors=()):
|
|
Packit |
534379 |
self.errors = {}
|
|
Packit |
534379 |
self._contents = collections.defaultdict(self.__class__)
|
|
Packit |
534379 |
|
|
Packit |
534379 |
for error in errors:
|
|
Packit |
534379 |
container = self
|
|
Packit |
534379 |
for element in error.path:
|
|
Packit |
534379 |
container = container[element]
|
|
Packit |
534379 |
container.errors[error.validator] = error
|
|
Packit |
534379 |
|
|
Packit |
534379 |
self._instance = error.instance
|
|
Packit |
534379 |
|
|
Packit |
534379 |
def __contains__(self, index):
|
|
Packit |
534379 |
"""
|
|
Packit |
534379 |
Check whether ``instance[index]`` has any errors.
|
|
Packit |
534379 |
|
|
Packit |
534379 |
"""
|
|
Packit |
534379 |
|
|
Packit |
534379 |
return index in self._contents
|
|
Packit |
534379 |
|
|
Packit |
534379 |
def __getitem__(self, index):
|
|
Packit |
534379 |
"""
|
|
Packit |
534379 |
Retrieve the child tree one level down at the given ``index``.
|
|
Packit |
534379 |
|
|
Packit |
534379 |
If the index is not in the instance that this tree corresponds to and
|
|
Packit |
534379 |
is not known by this tree, whatever error would be raised by
|
|
Packit |
534379 |
``instance.__getitem__`` will be propagated (usually this is some
|
|
Packit |
534379 |
subclass of :class:`LookupError`.
|
|
Packit |
534379 |
|
|
Packit |
534379 |
"""
|
|
Packit |
534379 |
|
|
Packit |
534379 |
if self._instance is not _unset and index not in self:
|
|
Packit |
534379 |
self._instance[index]
|
|
Packit |
534379 |
return self._contents[index]
|
|
Packit |
534379 |
|
|
Packit |
534379 |
def __setitem__(self, index, value):
|
|
Packit |
534379 |
self._contents[index] = value
|
|
Packit |
534379 |
|
|
Packit |
534379 |
def __iter__(self):
|
|
Packit |
534379 |
"""
|
|
Packit |
534379 |
Iterate (non-recursively) over the indices in the instance with errors.
|
|
Packit |
534379 |
|
|
Packit |
534379 |
"""
|
|
Packit |
534379 |
|
|
Packit |
534379 |
return iter(self._contents)
|
|
Packit |
534379 |
|
|
Packit |
534379 |
def __len__(self):
|
|
Packit |
534379 |
"""
|
|
Packit |
534379 |
Same as :attr:`total_errors`.
|
|
Packit |
534379 |
|
|
Packit |
534379 |
"""
|
|
Packit |
534379 |
|
|
Packit |
534379 |
return self.total_errors
|
|
Packit |
534379 |
|
|
Packit |
534379 |
def __repr__(self):
|
|
Packit |
534379 |
return "<%s (%s total errors)>" % (self.__class__.__name__, len(self))
|
|
Packit |
534379 |
|
|
Packit |
534379 |
@property
|
|
Packit |
534379 |
def total_errors(self):
|
|
Packit |
534379 |
"""
|
|
Packit |
534379 |
The total number of errors in the entire tree, including children.
|
|
Packit |
534379 |
|
|
Packit |
534379 |
"""
|
|
Packit |
534379 |
|
|
Packit |
534379 |
child_errors = sum(len(tree) for _, tree in iteritems(self._contents))
|
|
Packit |
534379 |
return len(self.errors) + child_errors
|
|
Packit |
534379 |
|
|
Packit |
534379 |
|
|
Packit |
534379 |
def by_relevance(weak=WEAK_MATCHES, strong=STRONG_MATCHES):
|
|
Packit |
534379 |
def relevance(error):
|
|
Packit |
534379 |
validator = error.validator
|
|
Packit |
534379 |
return -len(error.path), validator not in weak, validator in strong
|
|
Packit |
534379 |
return relevance
|
|
Packit |
534379 |
|
|
Packit |
534379 |
|
|
Packit |
534379 |
def best_match(errors, key=by_relevance()):
|
|
Packit |
534379 |
errors = iter(errors)
|
|
Packit |
534379 |
best = next(errors, None)
|
|
Packit |
534379 |
if best is None:
|
|
Packit |
534379 |
return
|
|
Packit |
534379 |
best = max(itertools.chain([best], errors), key=key)
|
|
Packit |
534379 |
|
|
Packit |
534379 |
while best.context:
|
|
Packit |
534379 |
best = min(best.context, key=key)
|
|
Packit |
534379 |
return best
|