Coverage for configlayer/exceptions.py: 100%

61 statements  

« prev     ^ index     » next       coverage.py v7.3.1, created at 2023-09-08 11:36 +0000

1"""Config layer exceptions""" 

2from typing import Callable, Any 

3 

4 

5_UNIQUE = object() 

6 

7 

8# Common exceptions 

9 

10 

11class InternalError(Exception): 

12 """Something in code goes wrong, program works not as expected!""" 

13 

14 

15class InputError(Exception): 

16 """Input parameter(s) of func or class method is wrong 

17 All parameters are optional, can be names or exceptions (if needed to override message) 

18 Parameters must_be and received can be started from ':' sign, to join message without space""" 

19 def __init__(self, *items: Exception | str, msg='', item_name='parameter', func_name='', 

20 args=(), force_header=False, **kwargs: Any): 

21 self.items = items 

22 self.msg = msg 

23 self.item_name = item_name 

24 self.func_name = func_name 

25 self.kwargs = kwargs 

26 self.header = self.must_be = self.received = '' 

27 self.sentences: tuple = () 

28 

29 # Handle exceptions 

30 if not all(isinstance(x, str) for x in items): 

31 super().__init__('; '.join(map(repr, items)), *args) 

32 return 

33 

34 # Make header 

35 header = f'Provided wrong {item_name}' 

36 func = f' to {func_name}' if func_name else '' 

37 match len(items): 

38 case 0: header = f'{header}(s){func}' if (force_header or func or 

39 not (msg or kwargs)) else '' 

40 case 1: header = f'{header}{func}: {items[0]}' 

41 # bug mypy: not see check for str at line 30, so type ignored 

42 case _: header = f'{header}s{func}: {", ".join(items)}' # type: ignore[arg-type] 

43 self.header = header 

44 

45 # Make received, must_be and sentences 

46 must_recv, sentences = [], [] 

47 for k, v in kwargs.items(): 

48 if k == 'sentences': 

49 sentences.extend(v) 

50 continue 

51 

52 # bug mypy: isinstance can accept Callable as type, so type ignored 

53 if val := (v if isinstance(v, str) else 

54 v() if isinstance(v, Callable) else f': {v!r}'): # type: ignore[arg-type] 

55 sentence = f'{k.replace("_", " ")}{"" if val[0] in ":,=" else " "}{val}' 

56 if k == 'must_be' or k == 'received': 

57 setattr(self, k, v) 

58 must_recv.append(sentence) 

59 else: 

60 sentences.append(sentence) 

61 self.sentences = tuple(sentences) 

62 

63 # Build message and fill exception 

64 msgs = (header, msg, ', but '.join(must_recv), *sentences) 

65 super().__init__('. '.join(x[0].upper() + x[1:] for x in msgs if x), *args) 

66 

67 

68class InitError(Exception): 

69 """Object initialization failed during class.__init__() method processing""" 

70 

71 

72class CheckValueError(ValueError): 

73 """Object value check failed""" 

74 

75 

76class CheckTypeError(TypeError): 

77 """Object type check failed""" 

78 

79 

80# Config related exceptions 

81 

82 

83class ConfigError(Exception): 

84 """Any config-related error type""" 

85 

86 

87class OptionsCheckError(ConfigError): 

88 """Options check failed, planned set of values is invalid""" 

89 

90 

91def _replace_unique(value, default=''): 

92 if value != _UNIQUE: 

93 try: 

94 value = repr(value) 

95 except Exception as e: 

96 value = repr(e) 

97 else: 

98 value = default 

99 return value 

100 

101 

102class FieldError(ConfigError): 

103 """Requested wrong field operation""" 

104 def __init__(self, operation: str, config: str, field: str, to_value=_UNIQUE, 

105 from_value=_UNIQUE, by_func='', reason='', failed=True, type_name='config'): 

106 from_value = f' from {from_value!r}' if (from_value := _replace_unique(from_value)) else '' 

107 to_value = f' to {to_value!r}' if (to_value := _replace_unique(to_value)) else '' 

108 by_func = f' by {by_func}' if by_func else '' 

109 failed = f' {"failed" if failed else "completed"}' 

110 reason = f',{"" if failed else " but"} {reason}' if reason else '' 

111 super().__init__(f'{operation.capitalize()} {config!r} {type_name} field {field!r}' 

112 f'{from_value}{to_value}{by_func}{failed}{reason}') 

113 

114 

115class ProfilesError(ConfigError): 

116 """Requested wrong profiles operation""" 

117 

118 

119class IOImportError(ConfigError): 

120 """Requested wrong io import operation""" 

121 

122 

123class IOExportError(ConfigError): 

124 """Requested wrong io export operation""" 

125 

126 

127class FileError(ConfigError): 

128 """Requested wrong file operation"""