Coverage for src/configuraptor/errors.py: 100%
29 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-15 16:40 +0200
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-15 16:40 +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?")
27@dataclass
28class ConfigErrorMissingKey(ConfigError):
29 """
30 Exception for when the config file is missing a required key.
31 """
33 key: str
34 cls: type
35 annotated_type: type
37 def __post_init__(self) -> None:
38 """
39 Automatically filles in the names of annotated type and cls for printing from __str__.
40 """
41 self._annotated_type = getattr(self.annotated_type, "__name__", str(self.annotated_type))
42 self._cls = self.cls.__name__
44 def __str__(self) -> str:
45 """
46 Custom error message based on dataclass values and calculated actual type.
47 """
48 return (
49 f"Config key '{self.key}' (type `{self._annotated_type}`) "
50 f"of class `{self._cls}` was not found in the config, "
51 f"but is required as a default value is not specified."
52 )
55@dataclass
56class ConfigErrorInvalidType(ConfigError):
57 """
58 Exception for when the config file contains a key with an unexpected type.
59 """
61 key: str
62 value: typing.Any
63 expected_type: type
65 def __post_init__(self) -> None:
66 """
67 Store the actual type of the config variable.
68 """
69 self.actual_type = type(self.value)
71 max_len = 50
72 self._value = str(self.value)
73 if len(self._value) > max_len:
74 self._value = f"{self._value[:max_len]}..."
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}' had a value (`{self._value}`) with a type (`{self.actual_type}`) "
82 f"that was not expected: `{self.expected_type}` is the required type."
83 )
86@dataclass
87class IsPostponedError(ConfigError):
88 """
89 Error thrown when you try to access a 'postponed' property without filling its value first.
90 """
92 message: str = "This postponed property has not been filled yet!"