Coverage for src/configuraptor/errors.py: 100%
53 statements
« prev ^ index » next coverage.py v7.2.7, created at 2025-01-09 20:20 +0100
« prev ^ index » next coverage.py v7.2.7, created at 2025-01-09 20:20 +0100
1"""
2Contains module-specific custom errors.
3"""
5import typing
6from dataclasses import dataclass
9class ConfigError(Exception):
10 """
11 Base exception class for this module.
12 """
15# class ConfigErrorGroup(ConfigError, ExceptionGroup):
16# """
17# Base Exception class for this module, but for exception groups (3.11+)
18# """
19# def __init__(self, _type: str, errors: list[Exception]):
20# more = len(errors) > 1
21# cnt = "Multiple" if more else "One"
22# s = "s" if more else ""
23# message = f"{cnt} {_type}{s} in config!"
24# super().__init__(message, errors)
25# if not errors:
26# raise ValueError("Error group raised without any errors?")
29@dataclass
30class ConfigErrorMissingKey(ConfigError):
31 """
32 Exception for when the config file is missing a required key.
33 """
35 key: str
36 cls: type
37 annotated_type: type
39 def __post_init__(self) -> None:
40 """
41 Automatically filles in the names of annotated type and cls for printing from __str__.
42 """
43 self._annotated_type = getattr(self.annotated_type, "__name__", str(self.annotated_type))
44 self._cls = self.cls.__name__
46 def __str__(self) -> str:
47 """
48 Custom error message based on dataclass values and calculated actual type.
49 """
50 return (
51 f"Config key '{self.key}' (type `{self._annotated_type}`) "
52 f"of class `{self._cls}` was not found in the config, "
53 f"but is required as a default value is not specified."
54 )
57@dataclass
58class ConfigErrorExtraKey(ConfigError):
59 """
60 Exception for when the config file is missing a required key.
61 """
63 key: str
64 value: str
65 cls: type
67 def __post_init__(self) -> None:
68 """
69 Automatically filles in the names of annotated type and cls for printing from __str__.
70 """
71 self._cls = self.cls.__name__
72 self._type = type(self.value)
74 def __str__(self) -> str:
75 """
76 Custom error message based on dataclass values and calculated actual type.
77 """
78 return (
79 f"Config key '{self.key}' (value: `{self.value}` type `{self._type}`) "
80 f"does not exist on class `{self._cls}`, but was attempted to be updated. "
81 f"Use strict = False to allow this behavior."
82 )
85@dataclass
86class ConfigErrorCouldNotConvert(ConfigError):
87 """
88 Raised by `convert_between` if something funky is going on (incompatible types etc.).
89 """
91 from_t: type
92 to_t: type
93 value: typing.Any
95 def __str__(self) -> str:
96 """
97 Custom error message based on dataclass values and calculated actual type.
98 """
99 return f"Could not convert `{self.value}` from `{self.from_t}` to `{self.to_t}`"
102@dataclass
103class ConfigErrorInvalidType(ConfigError):
104 """
105 Exception for when the config file contains a key with an unexpected type.
106 """
108 key: str
109 value: typing.Any
110 expected_type: type
112 def __post_init__(self) -> None:
113 """
114 Store the actual type of the config variable.
115 """
116 self.actual_type = type(self.value)
118 max_len = 50
119 self._value = str(self.value)
120 if len(self._value) > max_len:
121 self._value = f"{self._value[:max_len]}..."
123 def __str__(self) -> str:
124 """
125 Custom error message based on dataclass values and calculated actual type.
126 """
127 return (
128 f"Config key '{self.key}' had a value (`{self._value}`) with a type (`{self.actual_type}`) "
129 f"that was not expected: `{self.expected_type}` is the required type."
130 )
133@dataclass
134class ConfigErrorImmutable(ConfigError):
135 """
136 Raised when an immutable Mapping is attempted to be updated.
137 """
139 cls: type
141 def __post_init__(self) -> None:
142 """
143 Store the class name.
144 """
145 self._cls = self.cls.__name__
147 def __str__(self) -> str:
148 """
149 Custom error message.
150 """
151 return f"{self._cls} is Immutable!"
154@dataclass
155class IsPostponedError(ConfigError):
156 """
157 Error thrown when you try to access a 'postponed' property without filling its value first.
158 """
160 message: str = "This postponed property has not been filled yet!"