Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/cattrs/v.py: 13%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""Cattrs validation."""
3from typing import Callable, Union
5from .errors import (
6 ClassValidationError,
7 ForbiddenExtraKeysError,
8 IterableValidationError,
9)
11__all__ = ["format_exception", "transform_error"]
14def format_exception(exc: BaseException, type: Union[type, None]) -> str:
15 """The default exception formatter, handling the most common exceptions.
17 The following exceptions are handled specially:
19 * `KeyErrors` (`required field missing`)
20 * `ValueErrors` (`invalid value for type, expected <type>` or just `invalid value`)
21 * `TypeErrors` (`invalid value for type, expected <type>` and a couple special
22 cases for iterables)
23 * `cattrs.ForbiddenExtraKeysError`
24 * some `AttributeErrors` (special cased for structing mappings)
25 """
26 if isinstance(exc, KeyError):
27 res = "required field missing"
28 elif isinstance(exc, ValueError):
29 if type is not None:
30 tn = type.__name__ if hasattr(type, "__name__") else repr(type)
31 res = f"invalid value for type, expected {tn}"
32 else:
33 res = "invalid value"
34 elif isinstance(exc, TypeError):
35 if type is None:
36 if exc.args[0].endswith("object is not iterable"):
37 res = "invalid value for type, expected an iterable"
38 else:
39 res = f"invalid type ({exc})"
40 else:
41 tn = type.__name__ if hasattr(type, "__name__") else repr(type)
42 res = f"invalid value for type, expected {tn}"
43 elif isinstance(exc, ForbiddenExtraKeysError):
44 res = f"extra fields found ({', '.join(exc.extra_fields)})"
45 elif isinstance(exc, AttributeError) and exc.args[0].endswith(
46 "object has no attribute 'items'"
47 ):
48 # This was supposed to be a mapping (and have .items()) but it something else.
49 res = "expected a mapping"
50 else:
51 res = f"unknown error ({exc})"
53 return res
56def transform_error(
57 exc: Union[ClassValidationError, IterableValidationError, BaseException],
58 path: str = "$",
59 format_exception: Callable[
60 [BaseException, Union[type, None]], str
61 ] = format_exception,
62) -> list[str]:
63 """Transform an exception into a list of error messages.
65 To get detailed error messages, the exception should be produced by a converter
66 with `detailed_validation` set.
68 By default, the error messages are in the form of `{description} @ {path}`.
70 While traversing the exception and subexceptions, the path is formed:
72 * by appending `.{field_name}` for fields in classes
73 * by appending `[{int}]` for indices in iterables, like lists
74 * by appending `[{str}]` for keys in mappings, like dictionaries
76 :param exc: The exception to transform into error messages.
77 :param path: The root path to use.
78 :param format_exception: A callable to use to transform `Exceptions` into
79 string descriptions of errors.
81 .. versionadded:: 23.1.0
82 """
83 errors = []
84 if isinstance(exc, IterableValidationError):
85 with_notes, without = exc.group_exceptions()
86 for exc, note in with_notes:
87 p = f"{path}[{note.index!r}]"
88 if isinstance(exc, (ClassValidationError, IterableValidationError)):
89 errors.extend(transform_error(exc, p, format_exception))
90 else:
91 errors.append(f"{format_exception(exc, note.type)} @ {p}")
92 for exc in without:
93 errors.append(f"{format_exception(exc, None)} @ {path}")
94 elif isinstance(exc, ClassValidationError):
95 with_notes, without = exc.group_exceptions()
96 for exc, note in with_notes:
97 p = f"{path}.{note.name}"
98 if isinstance(exc, (ClassValidationError, IterableValidationError)):
99 errors.extend(transform_error(exc, p, format_exception))
100 else:
101 errors.append(f"{format_exception(exc, note.type)} @ {p}")
102 for exc in without:
103 errors.append(f"{format_exception(exc, None)} @ {path}")
104 else:
105 errors.append(f"{format_exception(exc, None)} @ {path}")
106 return errors