exceptions.py 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. # Copyright (c) 2003-2007 LOGILAB S.A. (Paris, FRANCE).
  2. # http://www.logilab.fr/ -- mailto:contact@logilab.fr
  3. # This program is free software; you can redistribute it and/or modify it under
  4. # the terms of the GNU General Public License as published by the Free Software
  5. # Foundation; either version 2 of the License, or (at your option) any later
  6. # version.
  7. #
  8. # This program is distributed in the hope that it will be useful, but WITHOUT
  9. # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  10. # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  11. #
  12. # You should have received a copy of the GNU General Public License along with
  13. # this program; if not, write to the Free Software Foundation, Inc.,
  14. # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  15. """exceptions handling (raising, catching, exceptions classes) checker
  16. """
  17. import sys
  18. from logilab.common.compat import builtins
  19. BUILTINS_NAME = builtins.__name__
  20. from logilab import astng
  21. from logilab.astng import YES, Instance, unpack_infer
  22. from pylint.checkers import BaseChecker
  23. from pylint.checkers.utils import is_empty, is_raising
  24. from pylint.interfaces import IASTNGChecker
  25. OVERGENERAL_EXCEPTIONS = ('Exception',)
  26. MSGS = {
  27. 'E0701': (
  28. 'Bad except clauses order (%s)',
  29. 'Used when except clauses are not in the correct order (from the \
  30. more specific to the more generic). If you don\'t fix the order, \
  31. some exceptions may not be catched by the most specific handler.'),
  32. 'E0702': ('Raising %s while only classes, instances or string are allowed',
  33. 'Used when something which is neither a class, an instance or a \
  34. string is raised (i.e. a `TypeError` will be raised).'),
  35. 'E0710': ('Raising a new style class which doesn\'t inherit from BaseException',
  36. 'Used when a new style class which doesn\'t inherit from \
  37. BaseException is raised.'),
  38. 'E0711': ('NotImplemented raised - should raise NotImplementedError',
  39. 'Used when NotImplemented is raised instead of \
  40. NotImplementedError'),
  41. 'W0701': ('Raising a string exception',
  42. 'Used when a string exception is raised.'),
  43. 'W0702': ('No exception type(s) specified',
  44. 'Used when an except clause doesn\'t specify exceptions type to \
  45. catch.'),
  46. 'W0703': ('Catching too general exception %s',
  47. 'Used when an except catches a too general exception, \
  48. possibly burying unrelated errors.'),
  49. 'W0704': ('Except doesn\'t do anything',
  50. 'Used when an except clause does nothing but "pass" and there is\
  51. no "else" clause.'),
  52. 'W0710': ('Exception doesn\'t inherit from standard "Exception" class',
  53. 'Used when a custom exception class is raised but doesn\'t \
  54. inherit from the builtin "Exception" class.'),
  55. }
  56. if sys.version_info < (3, 0):
  57. EXCEPTIONS_MODULE = "exceptions"
  58. else:
  59. EXCEPTIONS_MODULE = "builtins"
  60. class ExceptionsChecker(BaseChecker):
  61. """checks for
  62. * excepts without exception filter
  63. * type of raise argument : string, Exceptions, other values
  64. """
  65. __implements__ = IASTNGChecker
  66. name = 'exceptions'
  67. msgs = MSGS
  68. priority = -4
  69. options = (('overgeneral-exceptions',
  70. {'default' : OVERGENERAL_EXCEPTIONS,
  71. 'type' :'csv', 'metavar' : '<comma-separated class names>',
  72. 'help' : 'Exceptions that will emit a warning '
  73. 'when being caught. Defaults to "%s"' % (
  74. ', '.join(OVERGENERAL_EXCEPTIONS),)}
  75. ),
  76. )
  77. def visit_raise(self, node):
  78. """visit raise possibly inferring value"""
  79. # ignore empty raise
  80. if node.exc is None:
  81. return
  82. expr = node.exc
  83. if self._check_raise_value(node, expr):
  84. return
  85. else:
  86. try:
  87. value = unpack_infer(expr).next()
  88. except astng.InferenceError:
  89. return
  90. self._check_raise_value(node, value)
  91. def _check_raise_value(self, node, expr):
  92. """check for bad values, string exception and class inheritance
  93. """
  94. value_found = True
  95. if isinstance(expr, astng.Const):
  96. value = expr.value
  97. if isinstance(value, str):
  98. self.add_message('W0701', node=node)
  99. else:
  100. self.add_message('E0702', node=node,
  101. args=value.__class__.__name__)
  102. elif (isinstance(expr, astng.Name) and \
  103. expr.name in ('None', 'True', 'False')) or \
  104. isinstance(expr, (astng.List, astng.Dict, astng.Tuple,
  105. astng.Module, astng.Function)):
  106. self.add_message('E0702', node=node, args=expr.name)
  107. elif ( (isinstance(expr, astng.Name) and expr.name == 'NotImplemented')
  108. or (isinstance(expr, astng.CallFunc) and
  109. isinstance(expr.func, astng.Name) and
  110. expr.func.name == 'NotImplemented') ):
  111. self.add_message('E0711', node=node)
  112. elif isinstance(expr, astng.BinOp) and expr.op == '%':
  113. self.add_message('W0701', node=node)
  114. elif isinstance(expr, (Instance, astng.Class)):
  115. if isinstance(expr, Instance):
  116. expr = expr._proxied
  117. if (isinstance(expr, astng.Class) and
  118. not inherit_from_std_ex(expr) and
  119. expr.root().name != BUILTINS_NAME):
  120. if expr.newstyle:
  121. self.add_message('E0710', node=node)
  122. else:
  123. self.add_message('W0710', node=node)
  124. else:
  125. value_found = False
  126. else:
  127. value_found = False
  128. return value_found
  129. def visit_tryexcept(self, node):
  130. """check for empty except"""
  131. exceptions_classes = []
  132. nb_handlers = len(node.handlers)
  133. for index, handler in enumerate(node.handlers):
  134. # single except doing nothing but "pass" without else clause
  135. if nb_handlers == 1 and is_empty(handler.body) and not node.orelse:
  136. self.add_message('W0704', node=handler.type or handler.body[0])
  137. if handler.type is None:
  138. if nb_handlers == 1 and not is_raising(handler.body):
  139. self.add_message('W0702', node=handler)
  140. # check if a "except:" is followed by some other
  141. # except
  142. elif index < (nb_handlers - 1):
  143. msg = 'empty except clause should always appear last'
  144. self.add_message('E0701', node=node, args=msg)
  145. else:
  146. try:
  147. excs = list(unpack_infer(handler.type))
  148. except astng.InferenceError:
  149. continue
  150. for exc in excs:
  151. # XXX skip other non class nodes
  152. if exc is YES or not isinstance(exc, astng.Class):
  153. continue
  154. exc_ancestors = [anc for anc in exc.ancestors()
  155. if isinstance(anc, astng.Class)]
  156. for previous_exc in exceptions_classes:
  157. if previous_exc in exc_ancestors:
  158. msg = '%s is an ancestor class of %s' % (
  159. previous_exc.name, exc.name)
  160. self.add_message('E0701', node=handler.type, args=msg)
  161. if (exc.name in self.config.overgeneral_exceptions
  162. and exc.root().name == EXCEPTIONS_MODULE
  163. and nb_handlers == 1 and not is_raising(handler.body)):
  164. self.add_message('W0703', args=exc.name, node=handler.type)
  165. exceptions_classes += excs
  166. def inherit_from_std_ex(node):
  167. """return true if the given class node is subclass of
  168. exceptions.Exception
  169. """
  170. if node.name in ('Exception', 'BaseException') \
  171. and node.root().name == EXCEPTIONS_MODULE:
  172. return True
  173. for parent in node.ancestors(recurs=False):
  174. if inherit_from_std_ex(parent):
  175. return True
  176. return False
  177. def register(linter):
  178. """required method to auto register this checker"""
  179. linter.register_checker(ExceptionsChecker(linter))