Coverage for src/configuraptor/errors.py: 100%

53 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-28 15:07 +0200

1""" 

2Contains module-specific custom errors. 

3""" 

4import typing 

5from dataclasses import dataclass 

6 

7 

8class ConfigError(Exception): 

9 """ 

10 Base exception class for this module. 

11 """ 

12 

13 

14# class ConfigErrorGroup(ConfigError, ExceptionGroup): 

15# """ 

16# Base Exception class for this module, but for exception groups (3.11+) 

17# """ 

18# def __init__(self, _type: str, errors: list[Exception]): 

19# more = len(errors) > 1 

20# cnt = "Multiple" if more else "One" 

21# s = "s" if more else "" 

22# message = f"{cnt} {_type}{s} in config!" 

23# super().__init__(message, errors) 

24# if not errors: 

25# raise ValueError("Error group raised without any errors?") 

26 

27 

28@dataclass 

29class ConfigErrorMissingKey(ConfigError): 

30 """ 

31 Exception for when the config file is missing a required key. 

32 """ 

33 

34 key: str 

35 cls: type 

36 annotated_type: type 

37 

38 def __post_init__(self) -> None: 

39 """ 

40 Automatically filles in the names of annotated type and cls for printing from __str__. 

41 """ 

42 self._annotated_type = getattr(self.annotated_type, "__name__", str(self.annotated_type)) 

43 self._cls = self.cls.__name__ 

44 

45 def __str__(self) -> str: 

46 """ 

47 Custom error message based on dataclass values and calculated actual type. 

48 """ 

49 return ( 

50 f"Config key '{self.key}' (type `{self._annotated_type}`) " 

51 f"of class `{self._cls}` was not found in the config, " 

52 f"but is required as a default value is not specified." 

53 ) 

54 

55 

56@dataclass 

57class ConfigErrorExtraKey(ConfigError): 

58 """ 

59 Exception for when the config file is missing a required key. 

60 """ 

61 

62 key: str 

63 value: str 

64 cls: type 

65 

66 def __post_init__(self) -> None: 

67 """ 

68 Automatically filles in the names of annotated type and cls for printing from __str__. 

69 """ 

70 self._cls = self.cls.__name__ 

71 self._type = type(self.value) 

72 

73 def __str__(self) -> str: 

74 """ 

75 Custom error message based on dataclass values and calculated actual type. 

76 """ 

77 return ( 

78 f"Config key '{self.key}' (value: `{self.value}` type `{self._type}`) " 

79 f"does not exist on class `{self._cls}`, but was attempted to be updated. " 

80 f"Use strict = False to allow this behavior." 

81 ) 

82 

83 

84@dataclass 

85class ConfigErrorCouldNotConvert(ConfigError): 

86 from_t: type 

87 to_t: type 

88 value: str 

89 

90 def __str__(self) -> str: 

91 """ 

92 Custom error message based on dataclass values and calculated actual type. 

93 """ 

94 return ( 

95 f"Could not convert `{self.value}` from `{self.from_t}` to `{self.to_t}`" 

96 ) 

97 

98 

99@dataclass 

100class ConfigErrorInvalidType(ConfigError): 

101 """ 

102 Exception for when the config file contains a key with an unexpected type. 

103 """ 

104 

105 key: str 

106 value: typing.Any 

107 expected_type: type 

108 

109 def __post_init__(self) -> None: 

110 """ 

111 Store the actual type of the config variable. 

112 """ 

113 self.actual_type = type(self.value) 

114 

115 max_len = 50 

116 self._value = str(self.value) 

117 if len(self._value) > max_len: 

118 self._value = f"{self._value[:max_len]}..." 

119 

120 def __str__(self) -> str: 

121 """ 

122 Custom error message based on dataclass values and calculated actual type. 

123 """ 

124 return ( 

125 f"Config key '{self.key}' had a value (`{self._value}`) with a type (`{self.actual_type}`) " 

126 f"that was not expected: `{self.expected_type}` is the required type." 

127 ) 

128 

129 

130@dataclass 

131class ConfigErrorImmutable(ConfigError): 

132 """ 

133 Raised when an immutable Mapping is attempted to be updated. 

134 """ 

135 

136 cls: type 

137 

138 def __post_init__(self) -> None: 

139 """ 

140 Store the class name. 

141 """ 

142 self._cls = self.cls.__name__ 

143 

144 def __str__(self) -> str: 

145 """ 

146 Custom error message. 

147 """ 

148 return f"{self._cls} is Immutable!" 

149 

150 

151@dataclass 

152class IsPostponedError(ConfigError): 

153 """ 

154 Error thrown when you try to access a 'postponed' property without filling its value first. 

155 """ 

156 

157 message: str = "This postponed property has not been filled yet!"