Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/cattrs/errors.py: 38%
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
1from collections.abc import Sequence
2from typing import Any, Optional, Union
4from typing_extensions import Self
6from cattrs._compat import ExceptionGroup
9class StructureHandlerNotFoundError(Exception):
10 """
11 Error raised when structuring cannot find a handler for converting inputs into
12 :attr:`type_`.
13 """
15 def __init__(self, message: str, type_: type) -> None:
16 super().__init__(message, type_)
17 self.message = message
18 self.type_ = type_
20 def __str__(self) -> str:
21 return self.message
24class BaseValidationError(ExceptionGroup):
25 cl: type
27 def __new__(cls, message: str, excs: Sequence[Exception], cl: type) -> Self:
28 obj = super().__new__(cls, message, excs)
29 obj.cl = cl
30 return obj
32 def derive(self, excs: Sequence[Exception]) -> Self:
33 return self.__class__(self.message, excs, self.cl)
36class IterableValidationNote(str):
37 """Attached as a note to an exception when an iterable element fails structuring."""
39 index: Union[int, str] # Ints for list indices, strs for dict keys
40 type: Any
42 def __new__(cls, string: str, index: Union[int, str], type: Any) -> Self:
43 instance = str.__new__(cls, string)
44 instance.index = index
45 instance.type = type
46 return instance
48 def __getnewargs__(self) -> tuple[str, Union[int, str], Any]:
49 return (str(self), self.index, self.type)
52class IterableValidationError(BaseValidationError):
53 """Raised when structuring an iterable."""
55 def group_exceptions(
56 self,
57 ) -> tuple[list[tuple[Exception, IterableValidationNote]], list[Exception]]:
58 """Split the exceptions into two groups: with and without validation notes."""
59 excs_with_notes = []
60 other_excs = []
61 for subexc in self.exceptions:
62 if hasattr(subexc, "__notes__"):
63 for note in subexc.__notes__:
64 if note.__class__ is IterableValidationNote:
65 excs_with_notes.append((subexc, note))
66 break
67 else:
68 other_excs.append(subexc)
69 else:
70 other_excs.append(subexc)
72 return excs_with_notes, other_excs
75class AttributeValidationNote(str):
76 """Attached as a note to an exception when an attribute fails structuring."""
78 name: str
79 type: Any
81 def __new__(cls, string: str, name: str, type: Any) -> Self:
82 instance = str.__new__(cls, string)
83 instance.name = name
84 instance.type = type
85 return instance
87 def __getnewargs__(self) -> tuple[str, str, Any]:
88 return (str(self), self.name, self.type)
91class ClassValidationError(BaseValidationError):
92 """Raised when validating a class if any attributes are invalid."""
94 def group_exceptions(
95 self,
96 ) -> tuple[list[tuple[Exception, AttributeValidationNote]], list[Exception]]:
97 """Split the exceptions into two groups: with and without validation notes."""
98 excs_with_notes = []
99 other_excs = []
100 for subexc in self.exceptions:
101 if hasattr(subexc, "__notes__"):
102 for note in subexc.__notes__:
103 if note.__class__ is AttributeValidationNote:
104 excs_with_notes.append((subexc, note))
105 break
106 else:
107 other_excs.append(subexc)
108 else:
109 other_excs.append(subexc)
111 return excs_with_notes, other_excs
114class ForbiddenExtraKeysError(Exception):
115 """
116 Raised when `forbid_extra_keys` is activated and such extra keys are detected
117 during structuring.
119 The attribute `extra_fields` is a sequence of those extra keys, which were the
120 cause of this error, and `cl` is the class which was structured with those extra
121 keys.
122 """
124 def __init__(
125 self, message: Optional[str], cl: type, extra_fields: set[str]
126 ) -> None:
127 self.message = message
128 self.cl = cl
129 self.extra_fields = extra_fields
131 super().__init__(message, cl, extra_fields)
133 def __str__(self) -> str:
134 return (
135 self.message
136 or f"Extra fields in constructor for {self.cl.__name__}: "
137 f"{', '.join(sorted(self.extra_fields))}"
138 )