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

68 statements  

1from collections.abc import Sequence 

2from typing import Any, Optional, Union 

3 

4from typing_extensions import Self 

5 

6from cattrs._compat import ExceptionGroup 

7 

8 

9class StructureHandlerNotFoundError(Exception): 

10 """ 

11 Error raised when structuring cannot find a handler for converting inputs into 

12 :attr:`type_`. 

13 """ 

14 

15 def __init__(self, message: str, type_: type) -> None: 

16 super().__init__(message) 

17 self.type_ = type_ 

18 

19 

20class BaseValidationError(ExceptionGroup): 

21 cl: type 

22 

23 def __new__(cls, message: str, excs: Sequence[Exception], cl: type): 

24 obj = super().__new__(cls, message, excs) 

25 obj.cl = cl 

26 return obj 

27 

28 def derive(self, excs: Sequence[Exception]) -> Self: 

29 return self.__class__(self.message, excs, self.cl) 

30 

31 

32class IterableValidationNote(str): 

33 """Attached as a note to an exception when an iterable element fails structuring.""" 

34 

35 index: Union[int, str] # Ints for list indices, strs for dict keys 

36 type: Any 

37 

38 def __new__( 

39 cls, string: str, index: Union[int, str], type: Any 

40 ) -> "IterableValidationNote": 

41 instance = str.__new__(cls, string) 

42 instance.index = index 

43 instance.type = type 

44 return instance 

45 

46 def __getnewargs__(self) -> tuple[str, Union[int, str], Any]: 

47 return (str(self), self.index, self.type) 

48 

49 

50class IterableValidationError(BaseValidationError): 

51 """Raised when structuring an iterable.""" 

52 

53 def group_exceptions( 

54 self, 

55 ) -> tuple[list[tuple[Exception, IterableValidationNote]], list[Exception]]: 

56 """Split the exceptions into two groups: with and without validation notes.""" 

57 excs_with_notes = [] 

58 other_excs = [] 

59 for subexc in self.exceptions: 

60 if hasattr(subexc, "__notes__"): 

61 for note in subexc.__notes__: 

62 if note.__class__ is IterableValidationNote: 

63 excs_with_notes.append((subexc, note)) 

64 break 

65 else: 

66 other_excs.append(subexc) 

67 else: 

68 other_excs.append(subexc) 

69 

70 return excs_with_notes, other_excs 

71 

72 

73class AttributeValidationNote(str): 

74 """Attached as a note to an exception when an attribute fails structuring.""" 

75 

76 name: str 

77 type: Any 

78 

79 def __new__(cls, string: str, name: str, type: Any) -> "AttributeValidationNote": 

80 instance = str.__new__(cls, string) 

81 instance.name = name 

82 instance.type = type 

83 return instance 

84 

85 def __getnewargs__(self) -> tuple[str, str, Any]: 

86 return (str(self), self.name, self.type) 

87 

88 

89class ClassValidationError(BaseValidationError): 

90 """Raised when validating a class if any attributes are invalid.""" 

91 

92 def group_exceptions( 

93 self, 

94 ) -> tuple[list[tuple[Exception, AttributeValidationNote]], list[Exception]]: 

95 """Split the exceptions into two groups: with and without validation notes.""" 

96 excs_with_notes = [] 

97 other_excs = [] 

98 for subexc in self.exceptions: 

99 if hasattr(subexc, "__notes__"): 

100 for note in subexc.__notes__: 

101 if note.__class__ is AttributeValidationNote: 

102 excs_with_notes.append((subexc, note)) 

103 break 

104 else: 

105 other_excs.append(subexc) 

106 else: 

107 other_excs.append(subexc) 

108 

109 return excs_with_notes, other_excs 

110 

111 

112class ForbiddenExtraKeysError(Exception): 

113 """ 

114 Raised when `forbid_extra_keys` is activated and such extra keys are detected 

115 during structuring. 

116 

117 The attribute `extra_fields` is a sequence of those extra keys, which were the 

118 cause of this error, and `cl` is the class which was structured with those extra 

119 keys. 

120 """ 

121 

122 def __init__( 

123 self, message: Optional[str], cl: type, extra_fields: set[str] 

124 ) -> None: 

125 self.cl = cl 

126 self.extra_fields = extra_fields 

127 cln = cl.__name__ 

128 

129 super().__init__( 

130 message 

131 or f"Extra fields in constructor for {cln}: {', '.join(extra_fields)}" 

132 )