Commits

Claudiu Popa committed f546437 Merge

Merged in PCManticore/pylint/exception_context (pull request #77)

Add bad-exception-context warning for checking that `raise ... from ...` sets a proper exception context.

Comments (0)

Files changed (4)

 
     * Mark `file` as a bad function when using python2 (closes #8).
 
+    * Add new warning 'bad-exception-context', checking
+      that `raise ... from ...` uses a proper exception context
+      (None or an exception).
+
 
 2013-12-22  --  1.1.0
     * Add new check for use of deprecated pragma directives "pylint:disable-msg"

checkers/exceptions.py

             for base in infer_bases(inferit):
                 yield base
 
+PY3K = sys.version_info >= (3, 0)
 OVERGENERAL_EXCEPTIONS = ('Exception',)
 
 MSGS = {
               'raising-bad-type',
               'Used when something which is neither a class, an instance or a \
               string is raised (i.e. a `TypeError` will be raised).'),
+    'E0703': ('Exception context set to something which is not an '
+              'exception, nor None',
+              'bad-exception-context',
+              'Used when using the syntax "raise ... from ...", '
+              'where the exception context is not an exception, '
+              'nor None.',
+              {'minversion': (3, 0)}),
     'E0710': ('Raising a new style class which doesn\'t inherit from BaseException',
               'raising-non-exception',
               'Used when a new style class which doesn\'t inherit from \
                 ),
                )
 
-    @check_messages('W0701', 'W0710', 'E0702', 'E0710', 'E0711')
+    @check_messages('W0701', 'W0710', 'E0702', 'E0710', 'E0711',
+                    'bad-exception-context')
     def visit_raise(self, node):
         """visit raise possibly inferring value"""
         # ignore empty raise
         if node.exc is None:
             return
+        if PY3K and node.cause:
+            try:
+                cause = node.cause.infer().next()
+            except astroid.InferenceError:
+                pass
+            else:
+                if isinstance(cause, astroid.Const):
+                    if cause.value is not None:
+                        self.add_message('bad-exception-context',
+                                         node=node)
+                elif (not isinstance(cause, astroid.Class) and
+                      not inherit_from_std_ex(cause)):
+                    self.add_message('bad-exception-context',
+                                      node=node)
         expr = node.exc
         if self._check_raise_value(node, expr):
             return

test/input/func_bad_exception_context_py30.py

+"""Check that raise ... from .. uses a proper exception context """
+
+# pylint: disable=unreachable
+
+import socket
+
+__revision__ = 0
+
+class ExceptionSubclass(Exception):
+    """ subclass """
+
+def test():
+    """ docstring """
+    raise IndexError from 1
+    raise IndexError from None
+    raise IndexError from ZeroDivisionError
+    raise IndexError from object()
+    raise IndexError from ExceptionSubclass
+    raise IndexError from socket.error
+    raise IndexError() from None
+    raise IndexError() from ZeroDivisionError
+    raise IndexError() from ZeroDivisionError()
+    raise IndexError() from object()

test/messages/func_bad_exception_context_py30.txt

+E: 14:test: Exception context set to something which is not an exception, nor None
+E: 17:test: Exception context set to something which is not an exception, nor None
+E: 23:test: Exception context set to something which is not an exception, nor None