Coverage for src/configuraptor/errors.py: 100%
56 statements
« prev ^ index » next coverage.py v7.8.1, created at 2025-05-22 10:38 +0200
« prev ^ index » next coverage.py v7.8.1, created at 2025-05-22 10:38 +0200
1"""
2Contains module-specific custom errors.
3"""
5import typing
6from dataclasses import dataclass
8from .abs import T_data
11class ConfigError(Exception):
12 """
13 Base exception class for this module.
14 """
17# class ConfigErrorGroup(ConfigError, ExceptionGroup):
18# """
19# Base Exception class for this module, but for exception groups (3.11+)
20# """
21# def __init__(self, _type: str, errors: list[Exception]):
22# more = len(errors) > 1
23# cnt = "Multiple" if more else "One"
24# s = "s" if more else ""
25# message = f"{cnt} {_type}{s} in config!"
26# super().__init__(message, errors)
27# if not errors:
28# raise ValueError("Error group raised without any errors?")
31@dataclass
32class ConfigErrorMissingKey(ConfigError):
33 """
34 Exception for when the config file is missing a required key.
35 """
37 key: str
38 cls: type
39 annotated_type: type
41 def __post_init__(self) -> None:
42 """
43 Automatically filles in the names of annotated type and cls for printing from __str__.
44 """
45 self._annotated_type = getattr(self.annotated_type, "__name__", str(self.annotated_type))
46 self._cls = self.cls.__name__
48 def __str__(self) -> str:
49 """
50 Custom error message based on dataclass values and calculated actual type.
51 """
52 return (
53 f"Config key '{self.key}' (type `{self._annotated_type}`) "
54 f"of class `{self._cls}` was not found in the config, "
55 f"but is required as a default value is not specified."
56 )
59@dataclass
60class ConfigErrorExtraKey(ConfigError):
61 """
62 Exception for when the config file is missing a required key.
63 """
65 key: str
66 value: str
67 cls: type
69 def __post_init__(self) -> None:
70 """
71 Automatically filles in the names of annotated type and cls for printing from __str__.
72 """
73 self._cls = self.cls.__name__
74 self._type = type(self.value)
76 def __str__(self) -> str:
77 """
78 Custom error message based on dataclass values and calculated actual type.
79 """
80 return (
81 f"Config key '{self.key}' (value: `{self.value}` type `{self._type}`) "
82 f"does not exist on class `{self._cls}`, but was attempted to be updated. "
83 f"Use strict = False to allow this behavior."
84 )
87@dataclass
88class ConfigErrorCouldNotConvert(ConfigError):
89 """
90 Raised by `convert_between` if something funky is going on (incompatible types etc.).
91 """
93 from_t: type
94 to_t: type
95 value: typing.Any
97 def __str__(self) -> str:
98 """
99 Custom error message based on dataclass values and calculated actual type.
100 """
101 return f"Could not convert `{self.value}` from `{self.from_t}` to `{self.to_t}`"
104@dataclass
105class ConfigErrorInvalidType(ConfigError):
106 """
107 Exception for when the config file contains a key with an unexpected type.
108 """
110 key: str
111 value: typing.Any
112 expected_type: type
114 def __post_init__(self) -> None:
115 """
116 Store the actual type of the config variable.
117 """
118 self.actual_type = type(self.value)
120 max_len = 50
121 self._value = str(self.value)
122 if len(self._value) > max_len:
123 self._value = f"{self._value[:max_len]}..."
125 def __str__(self) -> str:
126 """
127 Custom error message based on dataclass values and calculated actual type.
128 """
129 return (
130 f"Config key '{self.key}' had a value (`{self._value}`) with a type (`{self.actual_type}`) "
131 f"that was not expected: `{self.expected_type}` is the required type."
132 )
135@dataclass
136class ConfigErrorImmutable(ConfigError):
137 """
138 Raised when an immutable Mapping is attempted to be updated.
139 """
141 cls: type
143 def __post_init__(self) -> None:
144 """
145 Store the class name.
146 """
147 self._cls = self.cls.__name__
149 def __str__(self) -> str:
150 """
151 Custom error message.
152 """
153 return f"{self._cls} is Immutable!"
156@dataclass
157class IsPostponedError(ConfigError):
158 """
159 Error thrown when you try to access a 'postponed' property without filling its value first.
160 """
162 message: str = "This postponed property has not been filled yet!"
165class FailedToLoad(ConfigError):
166 """
167 Exception raised when a configuration fails to load.
169 E.g. when `load_data` is called with `strict=True`
170 """
172 data: T_data