Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/dns/exception.py: 60%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

60 statements  

1# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license 

2 

3# Copyright (C) 2003-2017 Nominum, Inc. 

4# 

5# Permission to use, copy, modify, and distribute this software and its 

6# documentation for any purpose with or without fee is hereby granted, 

7# provided that the above copyright notice and this permission notice 

8# appear in all copies. 

9# 

10# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES 

11# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 

12# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR 

13# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 

14# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 

15# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 

16# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 

17 

18"""Common DNS Exceptions. 

19 

20Dnspython modules may also define their own exceptions, which will 

21always be subclasses of ``DNSException``. 

22""" 

23 

24 

25class DNSException(Exception): 

26 """Abstract base class shared by all dnspython exceptions. 

27 

28 It supports two basic modes of operation: 

29 

30 a) Old/compatible mode is used if ``__init__`` was called with 

31 empty *kwargs*. In compatible mode all *args* are passed 

32 to the standard Python Exception class as before and all *args* are 

33 printed by the standard ``__str__`` implementation. Class variable 

34 ``msg`` (or doc string if ``msg`` is ``None``) is returned from ``str()`` 

35 if *args* is empty. 

36 

37 b) New/parametrized mode is used if ``__init__`` was called with 

38 non-empty *kwargs*. 

39 In the new mode *args* must be empty and all kwargs must match 

40 those set in class variable ``supp_kwargs``. All kwargs are stored inside 

41 ``self.kwargs`` and used in a new ``__str__`` implementation to construct 

42 a formatted message based on the ``fmt`` class variable, a ``string``. 

43 

44 In the simplest case it is enough to override the ``supp_kwargs`` 

45 and ``fmt`` class variables to get nice parametrized messages. 

46 """ 

47 

48 msg: str | None = None # non-parametrized message 

49 supp_kwargs: set[str] = set() # accepted parameters for _fmt_kwargs (sanity check) 

50 fmt: str | None = None # message parametrized with results from _fmt_kwargs 

51 

52 def __init__(self, *args, **kwargs): 

53 self._check_params(*args, **kwargs) 

54 if kwargs: 

55 # This call to a virtual method from __init__ is ok in our usage 

56 self.kwargs = self._check_kwargs(**kwargs) # lgtm[py/init-calls-subclass] 

57 self.msg = str(self) 

58 else: 

59 self.kwargs = dict() # defined but empty for old mode exceptions 

60 if self.msg is None: 

61 # doc string is better implicit message than empty string 

62 self.msg = self.__doc__ 

63 if args: 

64 super().__init__(*args) 

65 else: 

66 super().__init__(self.msg) 

67 

68 def _check_params(self, *args, **kwargs): 

69 """Old exceptions supported only args and not kwargs. 

70 

71 For sanity we do not allow to mix old and new behavior.""" 

72 if args or kwargs: 

73 assert bool(args) != bool( 

74 kwargs 

75 ), "keyword arguments are mutually exclusive with positional args" 

76 

77 def _check_kwargs(self, **kwargs): 

78 if kwargs: 

79 assert ( 

80 set(kwargs.keys()) == self.supp_kwargs 

81 ), f"following set of keyword args is required: {self.supp_kwargs}" 

82 return kwargs 

83 

84 def _fmt_kwargs(self, **kwargs): 

85 """Format kwargs before printing them. 

86 

87 Resulting dictionary has to have keys necessary for str.format call 

88 on fmt class variable. 

89 """ 

90 fmtargs = {} 

91 for kw, data in kwargs.items(): 

92 if isinstance(data, list | set): 

93 # convert list of <someobj> to list of str(<someobj>) 

94 fmtargs[kw] = list(map(str, data)) 

95 if len(fmtargs[kw]) == 1: 

96 # remove list brackets [] from single-item lists 

97 fmtargs[kw] = fmtargs[kw].pop() 

98 else: 

99 fmtargs[kw] = data 

100 return fmtargs 

101 

102 def __str__(self): 

103 if self.kwargs and self.fmt: 

104 # provide custom message constructed from keyword arguments 

105 fmtargs = self._fmt_kwargs(**self.kwargs) 

106 return self.fmt.format(**fmtargs) 

107 else: 

108 # print *args directly in the same way as old DNSException 

109 return super().__str__() 

110 

111 

112class FormError(DNSException): 

113 """DNS message is malformed.""" 

114 

115 

116class SyntaxError(DNSException): 

117 """Text input is malformed.""" 

118 

119 

120class UnexpectedEnd(SyntaxError): 

121 """Text input ended unexpectedly.""" 

122 

123 

124class TooBig(DNSException): 

125 """The DNS message is too big.""" 

126 

127 

128class Timeout(DNSException): 

129 """The DNS operation timed out.""" 

130 

131 supp_kwargs = {"timeout"} 

132 fmt = "The DNS operation timed out after {timeout:.3f} seconds" 

133 

134 # We do this as otherwise mypy complains about unexpected keyword argument 

135 # idna_exception 

136 def __init__(self, *args, **kwargs): 

137 super().__init__(*args, **kwargs) 

138 

139 

140class UnsupportedAlgorithm(DNSException): 

141 """The DNSSEC algorithm is not supported.""" 

142 

143 

144class AlgorithmKeyMismatch(UnsupportedAlgorithm): 

145 """The DNSSEC algorithm is not supported for the given key type.""" 

146 

147 

148class ValidationFailure(DNSException): 

149 """The DNSSEC signature is invalid.""" 

150 

151 

152class DeniedByPolicy(DNSException): 

153 """Denied by DNSSEC policy.""" 

154 

155 

156class ExceptionWrapper: 

157 def __init__(self, exception_class): 

158 self.exception_class = exception_class 

159 

160 def __enter__(self): 

161 return self 

162 

163 def __exit__(self, exc_type, exc_val, exc_tb): 

164 if exc_type is not None and not isinstance(exc_val, self.exception_class): 

165 raise self.exception_class(str(exc_val)) from exc_val 

166 return False