codekingpro/portable-devtools
114k
1# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license2 3# Copyright (C) 2003-2017 Nominum, Inc.4#5# Permission to use, copy, modify, and distribute this software and its6# documentation for any purpose with or without fee is hereby granted,7# provided that the above copyright notice and this permission notice8# appear in all copies.9#10# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES11# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF12# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR13# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES14# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN15# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT16# 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 will21always be subclasses of ``DNSException``.22"""23 24 25from typing import Optional, Set26 27 28class DNSException(Exception):29 """Abstract base class shared by all dnspython exceptions.30 31 It supports two basic modes of operation:32 33 a) Old/compatible mode is used if ``__init__`` was called with34 empty *kwargs*. In compatible mode all *args* are passed35 to the standard Python Exception class as before and all *args* are36 printed by the standard ``__str__`` implementation. Class variable37 ``msg`` (or doc string if ``msg`` is ``None``) is returned from ``str()``38 if *args* is empty.39 40 b) New/parametrized mode is used if ``__init__`` was called with41 non-empty *kwargs*.42 In the new mode *args* must be empty and all kwargs must match43 those set in class variable ``supp_kwargs``. All kwargs are stored inside44 ``self.kwargs`` and used in a new ``__str__`` implementation to construct45 a formatted message based on the ``fmt`` class variable, a ``string``.46 47 In the simplest case it is enough to override the ``supp_kwargs``48 and ``fmt`` class variables to get nice parametrized messages.49 """50 51 msg: Optional[str] = None # non-parametrized message52 supp_kwargs: Set[str] = set() # accepted parameters for _fmt_kwargs (sanity check)53 fmt: Optional[str] = None # message parametrized with results from _fmt_kwargs54 55 def __init__(self, *args, **kwargs):56 self._check_params(*args, **kwargs)57 if kwargs:58 # This call to a virtual method from __init__ is ok in our usage59 self.kwargs = self._check_kwargs(**kwargs) # lgtm[py/init-calls-subclass]60 self.msg = str(self)61 else:62 self.kwargs = dict() # defined but empty for old mode exceptions63 if self.msg is None:64 # doc string is better implicit message than empty string65 self.msg = self.__doc__66 if args:67 super().__init__(*args)68 else:69 super().__init__(self.msg)70 71 def _check_params(self, *args, **kwargs):72 """Old exceptions supported only args and not kwargs.73 74 For sanity we do not allow to mix old and new behavior."""75 if args or kwargs:76 assert bool(args) != bool(77 kwargs78 ), "keyword arguments are mutually exclusive with positional args"79 80 def _check_kwargs(self, **kwargs):81 if kwargs:82 assert (83 set(kwargs.keys()) == self.supp_kwargs84 ), "following set of keyword args is required: %s" % (self.supp_kwargs)85 return kwargs86 87 def _fmt_kwargs(self, **kwargs):88 """Format kwargs before printing them.89 90 Resulting dictionary has to have keys necessary for str.format call91 on fmt class variable.92 """93 fmtargs = {}94 for kw, data in kwargs.items():95 if isinstance(data, (list, set)):96 # convert list of <someobj> to list of str(<someobj>)97 fmtargs[kw] = list(map(str, data))98 if len(fmtargs[kw]) == 1:99 # remove list brackets [] from single-item lists100 fmtargs[kw] = fmtargs[kw].pop()101 else:102 fmtargs[kw] = data103 return fmtargs104 105 def __str__(self):106 if self.kwargs and self.fmt:107 # provide custom message constructed from keyword arguments108 fmtargs = self._fmt_kwargs(**self.kwargs)109 return self.fmt.format(**fmtargs)110 else:111 # print *args directly in the same way as old DNSException112 return super().__str__()113 114 115class FormError(DNSException):116 """DNS message is malformed."""117 118 119class SyntaxError(DNSException):120 """Text input is malformed."""121 122 123class UnexpectedEnd(SyntaxError):124 """Text input ended unexpectedly."""125 126 127class TooBig(DNSException):128 """The DNS message is too big."""129 130 131class Timeout(DNSException):132 """The DNS operation timed out."""133 134 supp_kwargs = {"timeout"}135 fmt = "The DNS operation timed out after {timeout:.3f} seconds"136 137 # We do this as otherwise mypy complains about unexpected keyword argument138 # idna_exception139 def __init__(self, *args, **kwargs):140 super().__init__(*args, **kwargs)141 142 143class UnsupportedAlgorithm(DNSException):144 """The DNSSEC algorithm is not supported."""145 146 147class AlgorithmKeyMismatch(UnsupportedAlgorithm):148 """The DNSSEC algorithm is not supported for the given key type."""149 150 151class ValidationFailure(DNSException):152 """The DNSSEC signature is invalid."""153 154 155class DeniedByPolicy(DNSException):156 """Denied by DNSSEC policy."""157 158 159class ExceptionWrapper:160 def __init__(self, exception_class):161 self.exception_class = exception_class162 163 def __enter__(self):164 return self165 166 def __exit__(self, exc_type, exc_val, exc_tb):167 if exc_type is not None and not isinstance(exc_val, self.exception_class):168 raise self.exception_class(str(exc_val)) from exc_val169 return False170 