# fmt: off import typing # fmt: on class Error(Exception): """Base validation exception.""" class SchemaError(Error): """An error was encountered in the schema.""" class Invalid(Error): """The data was invalid. :attr msg: The error message. :attr path: The path to the error, as a list of keys in the source data. :attr error_message: The actual error message that was raised, as a string. """ def __init__( self, message: str, path: typing.Optional[typing.List[typing.Hashable]] = None, error_message: typing.Optional[str] = None, error_type: typing.Optional[str] = None, ) -> None: Error.__init__(self, message) self._path = path or [] self._error_message = error_message or message self.error_type = error_type @property def msg(self) -> str: return self.args[0] @property def path(self) -> typing.List[typing.Hashable]: return self._path @property def error_message(self) -> str: return self._error_message def __str__(self) -> str: path = ' @ data[%s]' % ']['.join(map(repr, self.path)) if self.path else '' output = Exception.__str__(self) if self.error_type: output += ' for ' + self.error_type return output + path def prepend(self, path: typing.List[typing.Hashable]) -> None: self._path = path + self.path class MultipleInvalid(Invalid): def __init__(self, errors: typing.Optional[typing.List[Invalid]] = None) -> None: self.errors = errors[:] if errors else [] def __repr__(self) -> str: return 'MultipleInvalid(%r)' % self.errors @property def msg(self) -> str: return self.errors[0].msg @property def path(self) -> typing.List[typing.Hashable]: return self.errors[0].path @property def error_message(self) -> str: return self.errors[0].error_message def add(self, error: Invalid) -> None: self.errors.append(error) def __str__(self) -> str: return str(self.errors[0]) def prepend(self, path: typing.List[typing.Hashable]) -> None: for error in self.errors: error.prepend(path) class RequiredFieldInvalid(Invalid): """Required field was missing.""" class ObjectInvalid(Invalid): """The value we found was not an object.""" class DictInvalid(Invalid): """The value found was not a dict.""" class ExclusiveInvalid(Invalid): """More than one value found in exclusion group.""" class InclusiveInvalid(Invalid): """Not all values found in inclusion group.""" class SequenceTypeInvalid(Invalid): """The type found is not a sequence type.""" class TypeInvalid(Invalid): """The value was not of required type.""" class ValueInvalid(Invalid): """The value was found invalid by evaluation function.""" class ContainsInvalid(Invalid): """List does not contain item""" class ScalarInvalid(Invalid): """Scalars did not match.""" class CoerceInvalid(Invalid): """Impossible to coerce value to type.""" class AnyInvalid(Invalid): """The value did not pass any validator.""" class AllInvalid(Invalid): """The value did not pass all validators.""" class MatchInvalid(Invalid): """The value does not match the given regular expression.""" class RangeInvalid(Invalid): """The value is not in given range.""" class TrueInvalid(Invalid): """The value is not True.""" class FalseInvalid(Invalid): """The value is not False.""" class BooleanInvalid(Invalid): """The value is not a boolean.""" class UrlInvalid(Invalid): """The value is not a URL.""" class EmailInvalid(Invalid): """The value is not an email address.""" class FileInvalid(Invalid): """The value is not a file.""" class DirInvalid(Invalid): """The value is not a directory.""" class PathInvalid(Invalid): """The value is not a path.""" class LiteralInvalid(Invalid): """The literal values do not match.""" class LengthInvalid(Invalid): pass class DatetimeInvalid(Invalid): """The value is not a formatted datetime string.""" class DateInvalid(Invalid): """The value is not a formatted date string.""" class InInvalid(Invalid): pass class NotInInvalid(Invalid): pass class ExactSequenceInvalid(Invalid): pass class NotEnoughValid(Invalid): """The value did not pass enough validations.""" pass class TooManyValid(Invalid): """The value passed more than expected validations.""" pass