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

1"""

2 Contains module-specific custom errors.

3"""

4 import typing

5 from dataclasses import dataclass

6

7

8 class 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

29 class 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

57 class 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

85 class 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

100 class 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

131 class 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

152 class 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!"