summaryrefslogtreecommitdiff
path: root/checkers/exceptions.py
diff options
context:
space:
mode:
authorroot <devnull@localhost>2006-04-26 10:48:09 +0000
committerroot <devnull@localhost>2006-04-26 10:48:09 +0000
commiteea76f1da01a33dec2afc42119e001e4350aaea2 (patch)
tree3bb03a16daa8c780bf60c622dc288eb01cfca145 /checkers/exceptions.py
downloadpylint-eea76f1da01a33dec2afc42119e001e4350aaea2.tar.gz
forget the past.
forget the past.
Diffstat (limited to 'checkers/exceptions.py')
-rw-r--r--checkers/exceptions.py167
1 files changed, 167 insertions, 0 deletions
diff --git a/checkers/exceptions.py b/checkers/exceptions.py
new file mode 100644
index 0000000..0f5fddc
--- /dev/null
+++ b/checkers/exceptions.py
@@ -0,0 +1,167 @@
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 2 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+""" Copyright (c) 2003-2005 LOGILAB S.A. (Paris, FRANCE).
+ http://www.logilab.fr/ -- mailto:contact@logilab.fr
+
+exceptions checkers for Python code
+"""
+
+__revision__ = '$Id: exceptions.py,v 1.27 2006-03-08 15:53:42 syt Exp $'
+
+from logilab.common.compat import enumerate
+from logilab import astng
+from logilab.astng.inference import unpack_infer
+
+from pylint.checkers import BaseChecker
+from pylint.checkers.utils import is_empty
+from pylint.interfaces import IASTNGChecker
+
+MSGS = {
+ 'E0701': (
+ 'Bad except clauses order (%s)',
+ 'Used when except clauses are not in the correct order (from the \
+ more specific to the more generic). If you don\'t fix the order, \
+ some exceptions may not be catched by the most specific handler.'),
+ 'E0702': ('Raising %s while only classes, instances or string are allowed',
+ 'Used when something which is neither a class, an instance or a \
+ string is raised (i.e. a `TypeError` will be raised).'),
+
+ 'W0701': ('Raising a string exception',
+ 'Used when a string exception is raised.'),
+ 'W0702': ('No exception\'s type specified',
+ 'Used when an except clause doesn\'t specify exceptions type to \
+ catch.'),
+ 'W0703': ('Catch "Exception"',
+ 'Used when an except catch Exception instances.'),
+ 'W0704': ('Except doesn\'t do anything',
+ 'Used when an except clause does nothing but "pass" and there is\
+ no "else" clause.'),
+ 'W0706': (
+ 'Identifier %s used to raise an exception is assigned to %s',
+ 'Used when a variable used to raise an exception is initially \
+ assigned to a value which can\'t be used as an exception.'),
+ }
+
+def is_raising(stmt):
+ """return true if the given statement node raise an exception
+ """
+ for node in stmt.nodes:
+ if isinstance(node, astng.Raise):
+ return 1
+ return 0
+
+class ExceptionsChecker(BaseChecker):
+ """checks for
+ * excepts without exception filter
+ * string exceptions
+ """
+
+ __implements__ = IASTNGChecker
+
+ name = 'exceptions'
+ msgs = MSGS
+ priority = -4
+ options = ()
+
+ def visit_raise(self, node):
+ """check for string exception
+ """
+ # ignore empty raise
+ if node.expr1 is None:
+ return
+ expr = node.expr1
+ if isinstance(expr, astng.Const):
+ value = expr.value
+ if isinstance(value, str):
+ self.add_message('W0701', node=node)
+ else:
+ self.add_message('E0702', node=node,
+ args=value.__class__.__name__)
+ elif isinstance(expr, astng.Name) and \
+ expr.name in ('None', 'True', 'False'):
+ self.add_message('E0702', node=node, args=expr.name)
+ elif isinstance(expr, astng.Mod):
+ self.add_message('W0701', node=node)
+ else:
+ try:
+ value = unpack_infer(expr).next()
+ except astng.InferenceError:
+ return
+ if value is astng.YES:
+ return
+ # must to be carefull since Const, Dict, .. inherit from
+ # Instance now
+ if isinstance(value, (astng.Class, astng.Module)):
+ return
+ if isinstance(value, astng.Instance) and \
+ isinstance(value._proxied, astng.Class):
+ return
+ if isinstance(value, astng.Const) and \
+ (value.value is None or
+ value.value is True or value.value is False):
+ # this Const has been generated by resolve
+ # since None, True and False are represented by Name
+ # nodes in the ast, and so this const node doesn't
+ # have the necessary parent, lineno and so on attributes
+ assinfo = value.as_string()
+ else:
+ assinfo = '%s line %s' % (value.as_string(),
+ value.source_line())
+ self.add_message('W0706', node=node,
+ args=(expr.as_string(), assinfo))
+
+ def visit_tryexcept(self, node):
+ """check for empty except
+ """
+ exceptions_classes = []
+ nb_handlers = len(node.handlers)
+ for index, handler in enumerate(node.handlers):
+ exc_type = handler[0]
+ stmt = handler[2]
+ # single except doing nothing but "pass" without else clause
+ if nb_handlers == 1 and is_empty(stmt) and not node.else_:
+ self.add_message('W0704', node=exc_type)
+ if exc_type is None:
+ if nb_handlers == 1 and not is_raising(stmt):
+ self.add_message('W0702', node=stmt.nodes[0])
+ # check if a "except:" is followed by some other
+ # except
+ elif index < (nb_handlers - 1):
+ msg = 'empty except clause should always appears last'
+ self.add_message('E0701', node=node, args=msg)
+ else:
+ try:
+ excs = list(unpack_infer(exc_type))
+ except astng.InferenceError:
+ continue
+ for exc in excs:
+ if exc is astng.YES:
+ continue
+ if not isinstance(exc, astng.Class):
+ continue # XXX
+ exc_ancestors = [anc for anc in exc.ancestors()
+ if isinstance(anc, astng.Class)]
+ for previous_exc in exceptions_classes:
+ if previous_exc in exc_ancestors:
+ msg = '%s is an ancestor class of %s' % (
+ previous_exc.name, exc.name)
+ self.add_message('E0701', node=exc_type, args=msg)
+ if (exc.name == 'Exception'
+ and exc.root().name == 'exceptions'
+ and nb_handlers == 1 and not is_raising(stmt)):
+ self.add_message('W0703', node=exc_type)
+ exceptions_classes += excs
+
+def register(linter):
+ """required method to auto register this checker"""
+ linter.register_checker(ExceptionsChecker(linter))