| Index: third_party/pylint/checkers/exceptions.py
|
| diff --git a/third_party/pylint/checkers/exceptions.py b/third_party/pylint/checkers/exceptions.py
|
| index 94186eba72f14c31a38013f27a4ae2d7c241006d..e8e5a54120327730bd0959a3cfd610c6aa110be5 100644
|
| --- a/third_party/pylint/checkers/exceptions.py
|
| +++ b/third_party/pylint/checkers/exceptions.py
|
| @@ -19,29 +19,45 @@ import sys
|
| from logilab.common.compat import builtins
|
| BUILTINS_NAME = builtins.__name__
|
| import astroid
|
| -from astroid import YES, Instance, unpack_infer
|
| +from astroid import YES, Instance, unpack_infer, List, Tuple
|
|
|
| from pylint.checkers import BaseChecker
|
| -from pylint.checkers.utils import is_empty, is_raising, check_messages
|
| -from pylint.interfaces import IAstroidChecker
|
| +from pylint.checkers.utils import (
|
| + is_empty, is_raising,
|
| + check_messages, inherit_from_std_ex,
|
| + EXCEPTIONS_MODULE, has_known_bases)
|
| +from pylint.interfaces import IAstroidChecker, INFERENCE, INFERENCE_FAILURE
|
|
|
| -def infer_bases(klass):
|
| - """ Fully infer the bases of the klass node.
|
| -
|
| - This doesn't use .ancestors(), because we need
|
| - the non-inferable nodes (YES nodes),
|
| - which can't be retrieved from .ancestors()
|
| +def _annotated_unpack_infer(stmt, context=None):
|
| + """
|
| + Recursively generate nodes inferred by the given statement.
|
| + If the inferred value is a list or a tuple, recurse on the elements.
|
| + Returns an iterator which yields tuples in the format
|
| + ('original node', 'infered node').
|
| """
|
| - for base in klass.bases:
|
| - try:
|
| - inferit = base.infer().next()
|
| - except astroid.InferenceError:
|
| - continue
|
| - if inferit is YES:
|
| - yield inferit
|
| + # TODO: the same code as unpack_infer, except for the annotated
|
| + # return. We need this type of annotation only here and
|
| + # there is no point in complicating the API for unpack_infer.
|
| + # If the need arises, this behaviour can be promoted to unpack_infer
|
| + # as well.
|
| + if isinstance(stmt, (List, Tuple)):
|
| + for elt in stmt.elts:
|
| + for infered_elt in unpack_infer(elt, context):
|
| + yield elt, infered_elt
|
| + return
|
| + # if infered is a final node, return it and stop
|
| + infered = next(stmt.infer(context))
|
| + if infered is stmt:
|
| + yield stmt, infered
|
| + return
|
| + # else, infer recursivly, except YES object that should be returned as is
|
| + for infered in stmt.infer(context):
|
| + if infered is YES:
|
| + yield stmt, infered
|
| else:
|
| - for base in infer_bases(inferit):
|
| - yield base
|
| + for inf_inf in unpack_infer(infered, context):
|
| + yield stmt, inf_inf
|
| +
|
|
|
| PY3K = sys.version_info >= (3, 0)
|
| OVERGENERAL_EXCEPTIONS = ('Exception',)
|
| @@ -52,7 +68,7 @@ MSGS = {
|
| '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',
|
| + 'E0702': ('Raising %s while only classes or instances are allowed',
|
| '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).'),
|
| @@ -75,10 +91,6 @@ MSGS = {
|
| 'catching-non-exception',
|
| 'Used when a class which doesn\'t inherit from \
|
| BaseException is used as an exception in an except clause.'),
|
| -
|
| - 'W0701': ('Raising a string exception',
|
| - 'raising-string',
|
| - 'Used when a string exception is raised.'),
|
| 'W0702': ('No exception type(s) specified',
|
| 'bare-except',
|
| 'Used when an except clause doesn\'t specify exceptions type to \
|
| @@ -101,25 +113,9 @@ MSGS = {
|
| 'Used when the exception to catch is of the form \
|
| "except A or B:". If intending to catch multiple, \
|
| rewrite as "except (A, B):"'),
|
| - 'W0712': ('Implicit unpacking of exceptions is not supported in Python 3',
|
| - 'unpacking-in-except',
|
| - 'Python3 will not allow implicit unpacking of exceptions in except '
|
| - 'clauses. '
|
| - 'See http://www.python.org/dev/peps/pep-3110/',
|
| - {'maxversion': (3, 0)}),
|
| - 'W0713': ('Indexing exceptions will not work on Python 3',
|
| - 'indexing-exception',
|
| - 'Indexing exceptions will not work on Python 3. Use '
|
| - '`exception.args[index]` instead.',
|
| - {'maxversion': (3, 0)}),
|
| }
|
|
|
|
|
| -if sys.version_info < (3, 0):
|
| - EXCEPTIONS_MODULE = "exceptions"
|
| -else:
|
| - EXCEPTIONS_MODULE = "builtins"
|
| -
|
| class ExceptionsChecker(BaseChecker):
|
| """checks for
|
| * excepts without exception filter
|
| @@ -140,8 +136,9 @@ class ExceptionsChecker(BaseChecker):
|
| ),
|
| )
|
|
|
| - @check_messages('raising-string', 'nonstandard-exception', 'raising-bad-type',
|
| - 'raising-non-exception', 'notimplemented-raised', 'bad-exception-context')
|
| + @check_messages('nonstandard-exception',
|
| + 'raising-bad-type', 'raising-non-exception',
|
| + 'notimplemented-raised', 'bad-exception-context')
|
| def visit_raise(self, node):
|
| """visit raise possibly inferring value"""
|
| # ignore empty raise
|
| @@ -149,7 +146,7 @@ class ExceptionsChecker(BaseChecker):
|
| return
|
| if PY3K and node.cause:
|
| try:
|
| - cause = node.cause.infer().next()
|
| + cause = next(node.cause.infer())
|
| except astroid.InferenceError:
|
| pass
|
| else:
|
| @@ -168,7 +165,7 @@ class ExceptionsChecker(BaseChecker):
|
| return
|
| else:
|
| try:
|
| - value = unpack_infer(expr).next()
|
| + value = next(unpack_infer(expr))
|
| except astroid.InferenceError:
|
| return
|
| self._check_raise_value(node, value)
|
| @@ -180,7 +177,8 @@ class ExceptionsChecker(BaseChecker):
|
| if isinstance(expr, astroid.Const):
|
| value = expr.value
|
| if isinstance(value, str):
|
| - self.add_message('raising-string', node=node)
|
| + # raising-string will be emitted from python3 porting checker.
|
| + pass
|
| else:
|
| self.add_message('raising-bad-type', node=node,
|
| args=value.__class__.__name__)
|
| @@ -194,8 +192,6 @@ class ExceptionsChecker(BaseChecker):
|
| isinstance(expr.func, astroid.Name) and
|
| expr.func.name == 'NotImplemented')):
|
| self.add_message('notimplemented-raised', node=node)
|
| - elif isinstance(expr, astroid.BinOp) and expr.op == '%':
|
| - self.add_message('raising-string', node=node)
|
| elif isinstance(expr, (Instance, astroid.Class)):
|
| if isinstance(expr, Instance):
|
| expr = expr._proxied
|
| @@ -205,31 +201,15 @@ class ExceptionsChecker(BaseChecker):
|
| if expr.newstyle:
|
| self.add_message('raising-non-exception', node=node)
|
| else:
|
| - self.add_message('nonstandard-exception', node=node)
|
| + self.add_message(
|
| + 'nonstandard-exception', node=node,
|
| + confidence=INFERENCE if has_known_bases(expr) else INFERENCE_FAILURE)
|
| else:
|
| value_found = False
|
| else:
|
| value_found = False
|
| return value_found
|
|
|
| - @check_messages('unpacking-in-except')
|
| - def visit_excepthandler(self, node):
|
| - """Visit an except handler block and check for exception unpacking."""
|
| - if isinstance(node.name, (astroid.Tuple, astroid.List)):
|
| - self.add_message('unpacking-in-except', node=node)
|
| -
|
| - @check_messages('indexing-exception')
|
| - def visit_subscript(self, node):
|
| - """ Look for indexing exceptions. """
|
| - try:
|
| - for infered in node.value.infer():
|
| - if not isinstance(infered, astroid.Instance):
|
| - continue
|
| - if inherit_from_std_ex(infered):
|
| - self.add_message('indexing-exception', node=node)
|
| - except astroid.InferenceError:
|
| - return
|
| -
|
| @check_messages('bare-except', 'broad-except', 'pointless-except',
|
| 'binary-op-exception', 'bad-except-order',
|
| 'catching-non-exception')
|
| @@ -237,10 +217,11 @@ class ExceptionsChecker(BaseChecker):
|
| """check for empty except"""
|
| exceptions_classes = []
|
| nb_handlers = len(node.handlers)
|
| - for index, handler in enumerate(node.handlers):
|
| + for index, handler in enumerate(node.handlers):
|
| # single except doing nothing but "pass" without else clause
|
| if is_empty(handler.body) and not node.orelse:
|
| - self.add_message('pointless-except', node=handler.type or handler.body[0])
|
| + self.add_message('pointless-except',
|
| + node=handler.type or handler.body[0])
|
| if handler.type is None:
|
| if not is_raising(handler.body):
|
| self.add_message('bare-except', node=handler)
|
| @@ -251,56 +232,64 @@ class ExceptionsChecker(BaseChecker):
|
| self.add_message('bad-except-order', node=node, args=msg)
|
|
|
| elif isinstance(handler.type, astroid.BoolOp):
|
| - self.add_message('binary-op-exception', node=handler, args=handler.type.op)
|
| + self.add_message('binary-op-exception',
|
| + node=handler, args=handler.type.op)
|
| else:
|
| try:
|
| - excs = list(unpack_infer(handler.type))
|
| + excs = list(_annotated_unpack_infer(handler.type))
|
| except astroid.InferenceError:
|
| continue
|
| - for exc in excs:
|
| - # XXX skip other non class nodes
|
| - if exc is YES or not isinstance(exc, astroid.Class):
|
| + for part, exc in excs:
|
| + if exc is YES:
|
| continue
|
| + if isinstance(exc, astroid.Instance) and inherit_from_std_ex(exc):
|
| + exc = exc._proxied
|
| + if not isinstance(exc, astroid.Class):
|
| + # Don't emit the warning if the infered stmt
|
| + # is None, but the exception handler is something else,
|
| + # maybe it was redefined.
|
| + if (isinstance(exc, astroid.Const) and
|
| + exc.value is None):
|
| + if ((isinstance(handler.type, astroid.Const) and
|
| + handler.type.value is None) or
|
| + handler.type.parent_of(exc)):
|
| + # If the exception handler catches None or
|
| + # the exception component, which is None, is
|
| + # defined by the entire exception handler, then
|
| + # emit a warning.
|
| + self.add_message('catching-non-exception',
|
| + node=handler.type,
|
| + args=(part.as_string(), ))
|
| + else:
|
| + self.add_message('catching-non-exception',
|
| + node=handler.type,
|
| + args=(part.as_string(), ))
|
| + continue
|
| +
|
| exc_ancestors = [anc for anc in exc.ancestors()
|
| if isinstance(anc, astroid.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('bad-except-order', node=handler.type, args=msg)
|
| + self.add_message('bad-except-order',
|
| + node=handler.type, args=msg)
|
| if (exc.name in self.config.overgeneral_exceptions
|
| and exc.root().name == EXCEPTIONS_MODULE
|
| and not is_raising(handler.body)):
|
| - self.add_message('broad-except', args=exc.name, node=handler.type)
|
| + self.add_message('broad-except',
|
| + args=exc.name, node=handler.type)
|
|
|
| if (not inherit_from_std_ex(exc) and
|
| exc.root().name != BUILTINS_NAME):
|
| - # try to see if the exception is based on a C based
|
| - # exception, by infering all the base classes and
|
| - # looking for inference errors
|
| - bases = infer_bases(exc)
|
| - fully_infered = all(inferit is not YES
|
| - for inferit in bases)
|
| - if fully_infered:
|
| + if has_known_bases(exc):
|
| self.add_message('catching-non-exception',
|
| node=handler.type,
|
| args=(exc.name, ))
|
|
|
| - exceptions_classes += excs
|
| + exceptions_classes += [exc for _, exc in excs]
|
|
|
|
|
| -def inherit_from_std_ex(node):
|
| - """return true if the given class node is subclass of
|
| - exceptions.Exception
|
| - """
|
| - if node.name in ('Exception', 'BaseException') \
|
| - and node.root().name == EXCEPTIONS_MODULE:
|
| - return True
|
| - for parent in node.ancestors(recurs=False):
|
| - if inherit_from_std_ex(parent):
|
| - return True
|
| - return False
|
| -
|
| def register(linter):
|
| """required method to auto register this checker"""
|
| linter.register_checker(ExceptionsChecker(linter))
|
|
|