exception.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
  2. #
  3. # Permission to use, copy, modify, and distribute this software and its
  4. # documentation for any purpose with or without fee is hereby granted,
  5. # provided that the above copyright notice and this permission notice
  6. # appear in all copies.
  7. #
  8. # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
  9. # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  10. # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
  11. # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  12. # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  13. # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
  14. # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. """Common DNS Exceptions."""
  16. class DNSException(Exception):
  17. """Abstract base class shared by all dnspython exceptions.
  18. It supports two basic modes of operation:
  19. a) Old/compatible mode is used if __init__ was called with
  20. empty **kwargs.
  21. In compatible mode all *args are passed to standard Python Exception class
  22. as before and all *args are printed by standard __str__ implementation.
  23. Class variable msg (or doc string if msg is None) is returned from str()
  24. if *args is empty.
  25. b) New/parametrized mode is used if __init__ was called with
  26. non-empty **kwargs.
  27. In the new mode *args has to be empty and all kwargs has to exactly match
  28. set in class variable self.supp_kwargs. All kwargs are stored inside
  29. self.kwargs and used in new __str__ implementation to construct
  30. formatted message based on self.fmt string.
  31. In the simplest case it is enough to override supp_kwargs and fmt
  32. class variables to get nice parametrized messages.
  33. """
  34. msg = None # non-parametrized message
  35. supp_kwargs = set() # accepted parameters for _fmt_kwargs (sanity check)
  36. fmt = None # message parametrized with results from _fmt_kwargs
  37. def __init__(self, *args, **kwargs):
  38. self._check_params(*args, **kwargs)
  39. if kwargs:
  40. self.kwargs = self._check_kwargs(**kwargs)
  41. self.msg = str(self)
  42. else:
  43. self.kwargs = dict() # defined but empty for old mode exceptions
  44. if self.msg is None:
  45. # doc string is better implicit message than empty string
  46. self.msg = self.__doc__
  47. if args:
  48. super(DNSException, self).__init__(*args)
  49. else:
  50. super(DNSException, self).__init__(self.msg)
  51. def _check_params(self, *args, **kwargs):
  52. """Old exceptions supported only args and not kwargs.
  53. For sanity we do not allow to mix old and new behavior."""
  54. if args or kwargs:
  55. assert bool(args) != bool(kwargs), \
  56. 'keyword arguments are mutually exclusive with positional args'
  57. def _check_kwargs(self, **kwargs):
  58. if kwargs:
  59. assert set(kwargs.keys()) == self.supp_kwargs, \
  60. 'following set of keyword args is required: %s' % (
  61. self.supp_kwargs)
  62. return kwargs
  63. def _fmt_kwargs(self, **kwargs):
  64. """Format kwargs before printing them.
  65. Resulting dictionary has to have keys necessary for str.format call
  66. on fmt class variable.
  67. """
  68. fmtargs = {}
  69. for kw, data in kwargs.items():
  70. if isinstance(data, (list, set)):
  71. # convert list of <someobj> to list of str(<someobj>)
  72. fmtargs[kw] = list(map(str, data))
  73. if len(fmtargs[kw]) == 1:
  74. # remove list brackets [] from single-item lists
  75. fmtargs[kw] = fmtargs[kw].pop()
  76. else:
  77. fmtargs[kw] = data
  78. return fmtargs
  79. def __str__(self):
  80. if self.kwargs and self.fmt:
  81. # provide custom message constructed from keyword arguments
  82. fmtargs = self._fmt_kwargs(**self.kwargs)
  83. return self.fmt.format(**fmtargs)
  84. else:
  85. # print *args directly in the same way as old DNSException
  86. return super(DNSException, self).__str__()
  87. class FormError(DNSException):
  88. """DNS message is malformed."""
  89. class SyntaxError(DNSException):
  90. """Text input is malformed."""
  91. class UnexpectedEnd(SyntaxError):
  92. """Text input ended unexpectedly."""
  93. class TooBig(DNSException):
  94. """The DNS message is too big."""
  95. class Timeout(DNSException):
  96. """The DNS operation timed out."""
  97. supp_kwargs = set(['timeout'])
  98. fmt = "The DNS operation timed out after {timeout} seconds"