Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/cattrs/errors.py: 39%
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 CattrsError(Exception):
10 """Base ``cattrs`` exception."""
13class StructureHandlerNotFoundError(CattrsError):
14 """
15 Error raised when structuring cannot find a handler for converting inputs into
16 :attr:`type_`.
17 """
19 def __init__(self, message: str, type_: type) -> None:
20 super().__init__(message, type_)
21 self.message = message
22 self.type_ = type_
24 def __str__(self) -> str:
25 return self.message
28class BaseValidationError(ExceptionGroup, CattrsError):
29 cl: type
31 def __new__(cls, message: str, excs: Sequence[Exception], cl: type) -> Self:
32 obj = super().__new__(cls, message, excs)
33 obj.cl = cl
34 return obj
36 def derive(self, excs: Sequence[Exception]) -> Self:
37 return self.__class__(self.message, excs, self.cl)
40class IterableValidationNote(str):
41 """Attached as a note to an exception when an iterable element fails structuring."""
43 index: Union[int, str] # Ints for list indices, strs for dict keys
44 type: Any
46 def __new__(cls, string: str, index: Union[int, str], type: Any) -> Self:
47 instance = str.__new__(cls, string)
48 instance.index = index
49 instance.type = type
50 return instance
52 def __getnewargs__(self) -> tuple[str, Union[int, str], Any]:
53 return (str(self), self.index, self.type)
56class IterableValidationError(BaseValidationError):
57 """Raised when structuring an iterable."""
59 def group_exceptions(
60 self,
61 ) -> tuple[list[tuple[Exception, IterableValidationNote]], list[Exception]]:
62 """Split the exceptions into two groups: with and without validation notes."""
63 excs_with_notes = []
64 other_excs = []
65 for subexc in self.exceptions:
66 if hasattr(subexc, "__notes__"):
67 for note in subexc.__notes__:
68 if note.__class__ is IterableValidationNote:
69 excs_with_notes.append((subexc, note))
70 break
71 else:
72 other_excs.append(subexc)
73 else:
74 other_excs.append(subexc)
76 return excs_with_notes, other_excs
79class AttributeValidationNote(str):
80 """Attached as a note to an exception when an attribute fails structuring."""
82 name: str
83 type: Any
85 def __new__(cls, string: str, name: str, type: Any) -> Self:
86 instance = str.__new__(cls, string)
87 instance.name = name
88 instance.type = type
89 return instance
91 def __getnewargs__(self) -> tuple[str, str, Any]:
92 return (str(self), self.name, self.type)
95class ClassValidationError(BaseValidationError):
96 """Raised when validating a class if any attributes are invalid."""
98 def group_exceptions(
99 self,
100 ) -> tuple[list[tuple[Exception, AttributeValidationNote]], list[Exception]]:
101 """Split the exceptions into two groups: with and without validation notes."""
102 excs_with_notes = []
103 other_excs = []
104 for subexc in self.exceptions:
105 if hasattr(subexc, "__notes__"):
106 for note in subexc.__notes__:
107 if note.__class__ is AttributeValidationNote:
108 excs_with_notes.append((subexc, note))
109 break
110 else:
111 other_excs.append(subexc)
112 else:
113 other_excs.append(subexc)
115 return excs_with_notes, other_excs
118class ForbiddenExtraKeysError(CattrsError):
119 """
120 Raised when `forbid_extra_keys` is activated and such extra keys are detected
121 during structuring.
123 The attribute `extra_fields` is a sequence of those extra keys, which were the
124 cause of this error, and `cl` is the class which was structured with those extra
125 keys.
126 """
128 def __init__(
129 self, message: Optional[str], cl: type, extra_fields: set[str]
130 ) -> None:
131 self.message = message
132 self.cl = cl
133 self.extra_fields = extra_fields
135 super().__init__(message, cl, extra_fields)
137 def __str__(self) -> str:
138 return (
139 self.message
140 or f"Extra fields in constructor for {self.cl.__name__}: "
141 f"{', '.join(sorted(self.extra_fields))}"
142 )