Source code for validx.exc.formatter
from . import errors
[docs]class Formatter(object):
"""
Error Formatter
:param dict templates:
templates that will be used to format errors.
Each key of ``templates`` should be a subclass of
:class:`validx.exc.ValidationError`.
Each value of ``templates`` should be a string,
i.e. simple template,
or list of conditional templates.
Conditional template is a tuple ``(predicate, string)``.
Where ``predicate`` is a callable,
that accepts :class:`validx.exc.ValidationError`
and returns boolean value.
When the predicate evaluates to ``True``,
its corresponding string will be used as a template.
Last value of list of conditional templates can be a string,
i.e. default simple template.
See ``format_error`` object,
defined within the module,
as an example.
"""
def __init__(self, templates):
assert isinstance(templates, dict), templates
for exc_class, template in templates.items():
assert isinstance(exc_class, type), exc_class
assert issubclass(exc_class, errors.ValidationError), exc_class
assert isinstance(template, (str, list, tuple)), template
if isinstance(template, (list, tuple)):
for f in template:
assert isinstance(f, (str, tuple))
if isinstance(f, tuple):
assert len(f) == 2, f
assert callable(f[0]), f[0]
assert isinstance(f[1], str), f[1]
self._templates = templates
[docs] def __call__(self, error):
"""
Format Error
:param ValidationError error:
error to format.
:returns:
list of context/message pairs: ``[(str, str), ...]``.
"""
result = []
error.sort()
for e in error:
context = e.format_context()
template = self._templates.get(type(e))
if template is None:
result.append((context, e.format_error()))
elif isinstance(template, str):
result.append((context, template.format(e)))
elif isinstance(template, (list, tuple)):
for f in template:
if isinstance(f, tuple) and f[0](e):
result.append((context, f[1].format(e)))
break
elif isinstance(f, str):
result.append((context, f.format(e)))
break
else:
result.append((context, e.format_error()))
return result
format_error = Formatter(
{
errors.InvalidTypeError: [
(lambda error: error.actual is type(None), "Value should not be null."),
"Expected type “{0.expected.__name__}”, got “{0.actual.__name__}”.",
],
errors.CoerceError: [
"Cannot coerce “{0.actual!r}” to type “{0.expected.__name__}”.",
],
errors.OptionsError: [
(
lambda error: len(error.expected) == 1,
"Expected {0.expected[0]}, got {0.actual}.",
),
"Expected one of {0.expected}, got {0.actual}.",
],
errors.MinValueError: "Expected value ≥ {0.expected}, got {0.actual}.",
errors.MaxValueError: "Expected value ≤ {0.expected}, got {0.actual}.",
errors.NumberError: [
(
lambda error: error.expected == "finite" and error.actual < 0,
"Expected finite number, got -∞.",
),
(
lambda error: error.expected == "finite" and error.actual > 0,
"Expected finite number, got +∞.",
),
(lambda error: error.expected == "number", "Expected number, got NaN."),
],
errors.StrDecodeError: "Cannot decode value using “{0.expected}” encoding.",
errors.MinLengthError: "Expected value length ≥ {0.expected}, got {0.actual}.",
errors.MaxLengthError: "Expected value length ≤ {0.expected}, got {0.actual}.",
errors.TupleLengthError: [
(
lambda error: error.expected == 1,
"Expected exactly 1 element, got {0.actual}.",
),
"Expected exactly {0.expected} elements, got {0.actual}.",
],
errors.PatternMatchError: "Cannot match “{0.actual}” using “{0.expected}”.",
errors.DatetimeParseError: [
(
lambda error: isinstance(error.expected, str),
"Cannot parse date/time value from “{0.actual}” using “{0.expected}” format.",
),
"Cannot parse date/time value from “{0.actual}”.",
],
errors.DatetimeTypeError: [
(
lambda error: error.expected == "naive",
"Naive date/time object is expected.",
),
(
lambda error: error.expected == "tzaware",
"Timezone-aware date/time object is expected.",
),
],
errors.RecursionMaxDepthError: (
"Too many nested structures, limit is {0.expected}."
),
errors.ForbiddenKeyError: "Key is not allowed.",
errors.MissingKeyError: "Required key is not provided.",
}
)