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

1""" 

2Contains module-specific custom errors. 

3""" 

4import typing 

5from dataclasses import dataclass 

6 

7 

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

28class ConfigErrorMissingKey(ConfigError): 

29 """ 

30 Exception for when the config file is missing a required key. 

31 """ 

32 

33 key: str 

34 cls: type 

35 annotated_type: type 

36 

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__ 

43 

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 ) 

53 

54 

55@dataclass 

56class ConfigErrorInvalidType(ConfigError): 

57 """ 

58 Exception for when the config file contains a key with an unexpected type. 

59 """ 

60 

61 key: str 

62 value: typing.Any 

63 expected_type: type 

64 

65 def __post_init__(self) -> None: 

66 """ 

67 Store the actual type of the config variable. 

68 """ 

69 self.actual_type = type(self.value) 

70 

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]}..." 

75 

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 ) 

84 

85 

86@dataclass 

87class IsPostponedError(ConfigError): 

88 """ 

89 Error thrown when you try to access a 'postponed' property without filling its value first. 

90 """ 

91 

92 message: str = "This postponed property has not been filled yet!"