Coverage for src/configuraptor/errors.py: 100%
39 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-19 17:39 +0200
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-19 17:39 +0200
1"""
2Contains module-specific custom errors.
3"""
4import typing
5from dataclasses import dataclass
8class ConfigError(Exception):
9 """
10 Base exception class for this module.
11 """
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?")
28@dataclass
29class ConfigErrorMissingKey(ConfigError):
30 """
31 Exception for when the config file is missing a required key.
32 """
34 key: str
35 cls: type
36 annotated_type: type
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__
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 )
56@dataclass
57class ConfigErrorExtraKey(ConfigError):
58 """
59 Exception for when the config file is missing a required key.
60 """
62 key: str
63 value: str
64 cls: type
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)
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 )
84@dataclass
85class ConfigErrorInvalidType(ConfigError):
86 """
87 Exception for when the config file contains a key with an unexpected type.
88 """
90 key: str
91 value: typing.Any
92 expected_type: type
94 def __post_init__(self) -> None:
95 """
96 Store the actual type of the config variable.
97 """
98 self.actual_type = type(self.value)
100 max_len = 50
101 self._value = str(self.value)
102 if len(self._value) > max_len:
103 self._value = f"{self._value[:max_len]}..."
105 def __str__(self) -> str:
106 """
107 Custom error message based on dataclass values and calculated actual type.
108 """
109 return (
110 f"Config key '{self.key}' had a value (`{self._value}`) with a type (`{self.actual_type}`) "
111 f"that was not expected: `{self.expected_type}` is the required type."
112 )
115@dataclass
116class IsPostponedError(ConfigError):
117 """
118 Error thrown when you try to access a 'postponed' property without filling its value first.
119 """
121 message: str = "This postponed property has not been filled yet!"