| OLD | NEW |
| 1 # Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE). | 1 # Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE). |
| 2 # http://www.logilab.fr/ -- mailto:contact@logilab.fr | 2 # http://www.logilab.fr/ -- mailto:contact@logilab.fr |
| 3 # This program is free software; you can redistribute it and/or modify it under | 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 | 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 | 5 # Foundation; either version 2 of the License, or (at your option) any later |
| 6 # version. | 6 # version. |
| 7 # | 7 # |
| 8 # This program is distributed in the hope that it will be useful, but WITHOUT | 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 | 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. | 10 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. |
| 11 # | 11 # |
| 12 # You should have received a copy of the GNU General Public License along with | 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., | 13 # this program; if not, write to the Free Software Foundation, Inc., |
| 14 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | 14 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| 15 """exceptions handling (raising, catching, exceptions classes) checker | 15 """exceptions handling (raising, catching, exceptions classes) checker |
| 16 """ | 16 """ |
| 17 import sys | 17 import sys |
| 18 | 18 |
| 19 from logilab.common.compat import builtins | 19 from logilab.common.compat import builtins |
| 20 BUILTINS_NAME = builtins.__name__ | 20 BUILTINS_NAME = builtins.__name__ |
| 21 import astroid | 21 import astroid |
| 22 from astroid import YES, Instance, unpack_infer | 22 from astroid import YES, Instance, unpack_infer, List, Tuple |
| 23 | 23 |
| 24 from pylint.checkers import BaseChecker | 24 from pylint.checkers import BaseChecker |
| 25 from pylint.checkers.utils import is_empty, is_raising, check_messages | 25 from pylint.checkers.utils import ( |
| 26 from pylint.interfaces import IAstroidChecker | 26 is_empty, is_raising, |
| 27 check_messages, inherit_from_std_ex, |
| 28 EXCEPTIONS_MODULE, has_known_bases) |
| 29 from pylint.interfaces import IAstroidChecker, INFERENCE, INFERENCE_FAILURE |
| 27 | 30 |
| 28 def infer_bases(klass): | 31 def _annotated_unpack_infer(stmt, context=None): |
| 29 """ Fully infer the bases of the klass node. | 32 """ |
| 33 Recursively generate nodes inferred by the given statement. |
| 34 If the inferred value is a list or a tuple, recurse on the elements. |
| 35 Returns an iterator which yields tuples in the format |
| 36 ('original node', 'infered node'). |
| 37 """ |
| 38 # TODO: the same code as unpack_infer, except for the annotated |
| 39 # return. We need this type of annotation only here and |
| 40 # there is no point in complicating the API for unpack_infer. |
| 41 # If the need arises, this behaviour can be promoted to unpack_infer |
| 42 # as well. |
| 43 if isinstance(stmt, (List, Tuple)): |
| 44 for elt in stmt.elts: |
| 45 for infered_elt in unpack_infer(elt, context): |
| 46 yield elt, infered_elt |
| 47 return |
| 48 # if infered is a final node, return it and stop |
| 49 infered = next(stmt.infer(context)) |
| 50 if infered is stmt: |
| 51 yield stmt, infered |
| 52 return |
| 53 # else, infer recursivly, except YES object that should be returned as is |
| 54 for infered in stmt.infer(context): |
| 55 if infered is YES: |
| 56 yield stmt, infered |
| 57 else: |
| 58 for inf_inf in unpack_infer(infered, context): |
| 59 yield stmt, inf_inf |
| 30 | 60 |
| 31 This doesn't use .ancestors(), because we need | |
| 32 the non-inferable nodes (YES nodes), | |
| 33 which can't be retrieved from .ancestors() | |
| 34 """ | |
| 35 for base in klass.bases: | |
| 36 try: | |
| 37 inferit = base.infer().next() | |
| 38 except astroid.InferenceError: | |
| 39 continue | |
| 40 if inferit is YES: | |
| 41 yield inferit | |
| 42 else: | |
| 43 for base in infer_bases(inferit): | |
| 44 yield base | |
| 45 | 61 |
| 46 PY3K = sys.version_info >= (3, 0) | 62 PY3K = sys.version_info >= (3, 0) |
| 47 OVERGENERAL_EXCEPTIONS = ('Exception',) | 63 OVERGENERAL_EXCEPTIONS = ('Exception',) |
| 48 | 64 |
| 49 MSGS = { | 65 MSGS = { |
| 50 'E0701': ('Bad except clauses order (%s)', | 66 'E0701': ('Bad except clauses order (%s)', |
| 51 'bad-except-order', | 67 'bad-except-order', |
| 52 'Used when except clauses are not in the correct order (from the ' | 68 'Used when except clauses are not in the correct order (from the ' |
| 53 'more specific to the more generic). If you don\'t fix the order,
' | 69 'more specific to the more generic). If you don\'t fix the order,
' |
| 54 'some exceptions may not be catched by the most specific handler.'
), | 70 'some exceptions may not be catched by the most specific handler.'
), |
| 55 'E0702': ('Raising %s while only classes, instances or string are allowed', | 71 'E0702': ('Raising %s while only classes or instances are allowed', |
| 56 'raising-bad-type', | 72 'raising-bad-type', |
| 57 'Used when something which is neither a class, an instance or a \ | 73 'Used when something which is neither a class, an instance or a \ |
| 58 string is raised (i.e. a `TypeError` will be raised).'), | 74 string is raised (i.e. a `TypeError` will be raised).'), |
| 59 'E0703': ('Exception context set to something which is not an ' | 75 'E0703': ('Exception context set to something which is not an ' |
| 60 'exception, nor None', | 76 'exception, nor None', |
| 61 'bad-exception-context', | 77 'bad-exception-context', |
| 62 'Used when using the syntax "raise ... from ...", ' | 78 'Used when using the syntax "raise ... from ...", ' |
| 63 'where the exception context is not an exception, ' | 79 'where the exception context is not an exception, ' |
| 64 'nor None.', | 80 'nor None.', |
| 65 {'minversion': (3, 0)}), | 81 {'minversion': (3, 0)}), |
| 66 'E0710': ('Raising a new style class which doesn\'t inherit from BaseExcepti
on', | 82 'E0710': ('Raising a new style class which doesn\'t inherit from BaseExcepti
on', |
| 67 'raising-non-exception', | 83 'raising-non-exception', |
| 68 'Used when a new style class which doesn\'t inherit from \ | 84 'Used when a new style class which doesn\'t inherit from \ |
| 69 BaseException is raised.'), | 85 BaseException is raised.'), |
| 70 'E0711': ('NotImplemented raised - should raise NotImplementedError', | 86 'E0711': ('NotImplemented raised - should raise NotImplementedError', |
| 71 'notimplemented-raised', | 87 'notimplemented-raised', |
| 72 'Used when NotImplemented is raised instead of \ | 88 'Used when NotImplemented is raised instead of \ |
| 73 NotImplementedError'), | 89 NotImplementedError'), |
| 74 'E0712': ('Catching an exception which doesn\'t inherit from BaseException:
%s', | 90 'E0712': ('Catching an exception which doesn\'t inherit from BaseException:
%s', |
| 75 'catching-non-exception', | 91 'catching-non-exception', |
| 76 'Used when a class which doesn\'t inherit from \ | 92 'Used when a class which doesn\'t inherit from \ |
| 77 BaseException is used as an exception in an except clause.'), | 93 BaseException is used as an exception in an except clause.'), |
| 78 | |
| 79 'W0701': ('Raising a string exception', | |
| 80 'raising-string', | |
| 81 'Used when a string exception is raised.'), | |
| 82 'W0702': ('No exception type(s) specified', | 94 'W0702': ('No exception type(s) specified', |
| 83 'bare-except', | 95 'bare-except', |
| 84 'Used when an except clause doesn\'t specify exceptions type to \ | 96 'Used when an except clause doesn\'t specify exceptions type to \ |
| 85 catch.'), | 97 catch.'), |
| 86 'W0703': ('Catching too general exception %s', | 98 'W0703': ('Catching too general exception %s', |
| 87 'broad-except', | 99 'broad-except', |
| 88 'Used when an except catches a too general exception, \ | 100 'Used when an except catches a too general exception, \ |
| 89 possibly burying unrelated errors.'), | 101 possibly burying unrelated errors.'), |
| 90 'W0704': ('Except doesn\'t do anything', | 102 'W0704': ('Except doesn\'t do anything', |
| 91 'pointless-except', | 103 'pointless-except', |
| 92 'Used when an except clause does nothing but "pass" and there is\ | 104 'Used when an except clause does nothing but "pass" and there is\ |
| 93 no "else" clause.'), | 105 no "else" clause.'), |
| 94 'W0710': ('Exception doesn\'t inherit from standard "Exception" class', | 106 'W0710': ('Exception doesn\'t inherit from standard "Exception" class', |
| 95 'nonstandard-exception', | 107 'nonstandard-exception', |
| 96 'Used when a custom exception class is raised but doesn\'t \ | 108 'Used when a custom exception class is raised but doesn\'t \ |
| 97 inherit from the builtin "Exception" class.', | 109 inherit from the builtin "Exception" class.', |
| 98 {'maxversion': (3, 0)}), | 110 {'maxversion': (3, 0)}), |
| 99 'W0711': ('Exception to catch is the result of a binary "%s" operation', | 111 'W0711': ('Exception to catch is the result of a binary "%s" operation', |
| 100 'binary-op-exception', | 112 'binary-op-exception', |
| 101 'Used when the exception to catch is of the form \ | 113 'Used when the exception to catch is of the form \ |
| 102 "except A or B:". If intending to catch multiple, \ | 114 "except A or B:". If intending to catch multiple, \ |
| 103 rewrite as "except (A, B):"'), | 115 rewrite as "except (A, B):"'), |
| 104 'W0712': ('Implicit unpacking of exceptions is not supported in Python 3', | |
| 105 'unpacking-in-except', | |
| 106 'Python3 will not allow implicit unpacking of exceptions in except
' | |
| 107 'clauses. ' | |
| 108 'See http://www.python.org/dev/peps/pep-3110/', | |
| 109 {'maxversion': (3, 0)}), | |
| 110 'W0713': ('Indexing exceptions will not work on Python 3', | |
| 111 'indexing-exception', | |
| 112 'Indexing exceptions will not work on Python 3. Use ' | |
| 113 '`exception.args[index]` instead.', | |
| 114 {'maxversion': (3, 0)}), | |
| 115 } | 116 } |
| 116 | 117 |
| 117 | 118 |
| 118 if sys.version_info < (3, 0): | |
| 119 EXCEPTIONS_MODULE = "exceptions" | |
| 120 else: | |
| 121 EXCEPTIONS_MODULE = "builtins" | |
| 122 | |
| 123 class ExceptionsChecker(BaseChecker): | 119 class ExceptionsChecker(BaseChecker): |
| 124 """checks for | 120 """checks for |
| 125 * excepts without exception filter | 121 * excepts without exception filter |
| 126 * type of raise argument : string, Exceptions, other values | 122 * type of raise argument : string, Exceptions, other values |
| 127 """ | 123 """ |
| 128 | 124 |
| 129 __implements__ = IAstroidChecker | 125 __implements__ = IAstroidChecker |
| 130 | 126 |
| 131 name = 'exceptions' | 127 name = 'exceptions' |
| 132 msgs = MSGS | 128 msgs = MSGS |
| 133 priority = -4 | 129 priority = -4 |
| 134 options = (('overgeneral-exceptions', | 130 options = (('overgeneral-exceptions', |
| 135 {'default' : OVERGENERAL_EXCEPTIONS, | 131 {'default' : OVERGENERAL_EXCEPTIONS, |
| 136 'type' :'csv', 'metavar' : '<comma-separated class names>', | 132 'type' :'csv', 'metavar' : '<comma-separated class names>', |
| 137 'help' : 'Exceptions that will emit a warning ' | 133 'help' : 'Exceptions that will emit a warning ' |
| 138 'when being caught. Defaults to "%s"' % ( | 134 'when being caught. Defaults to "%s"' % ( |
| 139 ', '.join(OVERGENERAL_EXCEPTIONS),)} | 135 ', '.join(OVERGENERAL_EXCEPTIONS),)} |
| 140 ), | 136 ), |
| 141 ) | 137 ) |
| 142 | 138 |
| 143 @check_messages('raising-string', 'nonstandard-exception', 'raising-bad-type
', | 139 @check_messages('nonstandard-exception', |
| 144 'raising-non-exception', 'notimplemented-raised', 'bad-excep
tion-context') | 140 'raising-bad-type', 'raising-non-exception', |
| 141 'notimplemented-raised', 'bad-exception-context') |
| 145 def visit_raise(self, node): | 142 def visit_raise(self, node): |
| 146 """visit raise possibly inferring value""" | 143 """visit raise possibly inferring value""" |
| 147 # ignore empty raise | 144 # ignore empty raise |
| 148 if node.exc is None: | 145 if node.exc is None: |
| 149 return | 146 return |
| 150 if PY3K and node.cause: | 147 if PY3K and node.cause: |
| 151 try: | 148 try: |
| 152 cause = node.cause.infer().next() | 149 cause = next(node.cause.infer()) |
| 153 except astroid.InferenceError: | 150 except astroid.InferenceError: |
| 154 pass | 151 pass |
| 155 else: | 152 else: |
| 156 if cause is YES: | 153 if cause is YES: |
| 157 return | 154 return |
| 158 if isinstance(cause, astroid.Const): | 155 if isinstance(cause, astroid.Const): |
| 159 if cause.value is not None: | 156 if cause.value is not None: |
| 160 self.add_message('bad-exception-context', | 157 self.add_message('bad-exception-context', |
| 161 node=node) | 158 node=node) |
| 162 elif (not isinstance(cause, astroid.Class) and | 159 elif (not isinstance(cause, astroid.Class) and |
| 163 not inherit_from_std_ex(cause)): | 160 not inherit_from_std_ex(cause)): |
| 164 self.add_message('bad-exception-context', | 161 self.add_message('bad-exception-context', |
| 165 node=node) | 162 node=node) |
| 166 expr = node.exc | 163 expr = node.exc |
| 167 if self._check_raise_value(node, expr): | 164 if self._check_raise_value(node, expr): |
| 168 return | 165 return |
| 169 else: | 166 else: |
| 170 try: | 167 try: |
| 171 value = unpack_infer(expr).next() | 168 value = next(unpack_infer(expr)) |
| 172 except astroid.InferenceError: | 169 except astroid.InferenceError: |
| 173 return | 170 return |
| 174 self._check_raise_value(node, value) | 171 self._check_raise_value(node, value) |
| 175 | 172 |
| 176 def _check_raise_value(self, node, expr): | 173 def _check_raise_value(self, node, expr): |
| 177 """check for bad values, string exception and class inheritance | 174 """check for bad values, string exception and class inheritance |
| 178 """ | 175 """ |
| 179 value_found = True | 176 value_found = True |
| 180 if isinstance(expr, astroid.Const): | 177 if isinstance(expr, astroid.Const): |
| 181 value = expr.value | 178 value = expr.value |
| 182 if isinstance(value, str): | 179 if isinstance(value, str): |
| 183 self.add_message('raising-string', node=node) | 180 # raising-string will be emitted from python3 porting checker. |
| 181 pass |
| 184 else: | 182 else: |
| 185 self.add_message('raising-bad-type', node=node, | 183 self.add_message('raising-bad-type', node=node, |
| 186 args=value.__class__.__name__) | 184 args=value.__class__.__name__) |
| 187 elif (isinstance(expr, astroid.Name) and \ | 185 elif (isinstance(expr, astroid.Name) and \ |
| 188 expr.name in ('None', 'True', 'False')) or \ | 186 expr.name in ('None', 'True', 'False')) or \ |
| 189 isinstance(expr, (astroid.List, astroid.Dict, astroid.Tuple, | 187 isinstance(expr, (astroid.List, astroid.Dict, astroid.Tuple, |
| 190 astroid.Module, astroid.Function)): | 188 astroid.Module, astroid.Function)): |
| 191 self.add_message('raising-bad-type', node=node, args=expr.name) | 189 self.add_message('raising-bad-type', node=node, args=expr.name) |
| 192 elif ((isinstance(expr, astroid.Name) and expr.name == 'NotImplemented') | 190 elif ((isinstance(expr, astroid.Name) and expr.name == 'NotImplemented') |
| 193 or (isinstance(expr, astroid.CallFunc) and | 191 or (isinstance(expr, astroid.CallFunc) and |
| 194 isinstance(expr.func, astroid.Name) and | 192 isinstance(expr.func, astroid.Name) and |
| 195 expr.func.name == 'NotImplemented')): | 193 expr.func.name == 'NotImplemented')): |
| 196 self.add_message('notimplemented-raised', node=node) | 194 self.add_message('notimplemented-raised', node=node) |
| 197 elif isinstance(expr, astroid.BinOp) and expr.op == '%': | |
| 198 self.add_message('raising-string', node=node) | |
| 199 elif isinstance(expr, (Instance, astroid.Class)): | 195 elif isinstance(expr, (Instance, astroid.Class)): |
| 200 if isinstance(expr, Instance): | 196 if isinstance(expr, Instance): |
| 201 expr = expr._proxied | 197 expr = expr._proxied |
| 202 if (isinstance(expr, astroid.Class) and | 198 if (isinstance(expr, astroid.Class) and |
| 203 not inherit_from_std_ex(expr) and | 199 not inherit_from_std_ex(expr) and |
| 204 expr.root().name != BUILTINS_NAME): | 200 expr.root().name != BUILTINS_NAME): |
| 205 if expr.newstyle: | 201 if expr.newstyle: |
| 206 self.add_message('raising-non-exception', node=node) | 202 self.add_message('raising-non-exception', node=node) |
| 207 else: | 203 else: |
| 208 self.add_message('nonstandard-exception', node=node) | 204 self.add_message( |
| 205 'nonstandard-exception', node=node, |
| 206 confidence=INFERENCE if has_known_bases(expr) else INFER
ENCE_FAILURE) |
| 209 else: | 207 else: |
| 210 value_found = False | 208 value_found = False |
| 211 else: | 209 else: |
| 212 value_found = False | 210 value_found = False |
| 213 return value_found | 211 return value_found |
| 214 | 212 |
| 215 @check_messages('unpacking-in-except') | |
| 216 def visit_excepthandler(self, node): | |
| 217 """Visit an except handler block and check for exception unpacking.""" | |
| 218 if isinstance(node.name, (astroid.Tuple, astroid.List)): | |
| 219 self.add_message('unpacking-in-except', node=node) | |
| 220 | |
| 221 @check_messages('indexing-exception') | |
| 222 def visit_subscript(self, node): | |
| 223 """ Look for indexing exceptions. """ | |
| 224 try: | |
| 225 for infered in node.value.infer(): | |
| 226 if not isinstance(infered, astroid.Instance): | |
| 227 continue | |
| 228 if inherit_from_std_ex(infered): | |
| 229 self.add_message('indexing-exception', node=node) | |
| 230 except astroid.InferenceError: | |
| 231 return | |
| 232 | |
| 233 @check_messages('bare-except', 'broad-except', 'pointless-except', | 213 @check_messages('bare-except', 'broad-except', 'pointless-except', |
| 234 'binary-op-exception', 'bad-except-order', | 214 'binary-op-exception', 'bad-except-order', |
| 235 'catching-non-exception') | 215 'catching-non-exception') |
| 236 def visit_tryexcept(self, node): | 216 def visit_tryexcept(self, node): |
| 237 """check for empty except""" | 217 """check for empty except""" |
| 238 exceptions_classes = [] | 218 exceptions_classes = [] |
| 239 nb_handlers = len(node.handlers) | 219 nb_handlers = len(node.handlers) |
| 240 for index, handler in enumerate(node.handlers): | 220 for index, handler in enumerate(node.handlers): |
| 241 # single except doing nothing but "pass" without else clause | 221 # single except doing nothing but "pass" without else clause |
| 242 if is_empty(handler.body) and not node.orelse: | 222 if is_empty(handler.body) and not node.orelse: |
| 243 self.add_message('pointless-except', node=handler.type or handle
r.body[0]) | 223 self.add_message('pointless-except', |
| 224 node=handler.type or handler.body[0]) |
| 244 if handler.type is None: | 225 if handler.type is None: |
| 245 if not is_raising(handler.body): | 226 if not is_raising(handler.body): |
| 246 self.add_message('bare-except', node=handler) | 227 self.add_message('bare-except', node=handler) |
| 247 # check if a "except:" is followed by some other | 228 # check if a "except:" is followed by some other |
| 248 # except | 229 # except |
| 249 if index < (nb_handlers - 1): | 230 if index < (nb_handlers - 1): |
| 250 msg = 'empty except clause should always appear last' | 231 msg = 'empty except clause should always appear last' |
| 251 self.add_message('bad-except-order', node=node, args=msg) | 232 self.add_message('bad-except-order', node=node, args=msg) |
| 252 | 233 |
| 253 elif isinstance(handler.type, astroid.BoolOp): | 234 elif isinstance(handler.type, astroid.BoolOp): |
| 254 self.add_message('binary-op-exception', node=handler, args=handl
er.type.op) | 235 self.add_message('binary-op-exception', |
| 236 node=handler, args=handler.type.op) |
| 255 else: | 237 else: |
| 256 try: | 238 try: |
| 257 excs = list(unpack_infer(handler.type)) | 239 excs = list(_annotated_unpack_infer(handler.type)) |
| 258 except astroid.InferenceError: | 240 except astroid.InferenceError: |
| 259 continue | 241 continue |
| 260 for exc in excs: | 242 for part, exc in excs: |
| 261 # XXX skip other non class nodes | 243 if exc is YES: |
| 262 if exc is YES or not isinstance(exc, astroid.Class): | |
| 263 continue | 244 continue |
| 245 if isinstance(exc, astroid.Instance) and inherit_from_std_ex
(exc): |
| 246 exc = exc._proxied |
| 247 if not isinstance(exc, astroid.Class): |
| 248 # Don't emit the warning if the infered stmt |
| 249 # is None, but the exception handler is something else, |
| 250 # maybe it was redefined. |
| 251 if (isinstance(exc, astroid.Const) and |
| 252 exc.value is None): |
| 253 if ((isinstance(handler.type, astroid.Const) and |
| 254 handler.type.value is None) or |
| 255 handler.type.parent_of(exc)): |
| 256 # If the exception handler catches None or |
| 257 # the exception component, which is None, is |
| 258 # defined by the entire exception handler, then |
| 259 # emit a warning. |
| 260 self.add_message('catching-non-exception', |
| 261 node=handler.type, |
| 262 args=(part.as_string(), )) |
| 263 else: |
| 264 self.add_message('catching-non-exception', |
| 265 node=handler.type, |
| 266 args=(part.as_string(), )) |
| 267 continue |
| 268 |
| 264 exc_ancestors = [anc for anc in exc.ancestors() | 269 exc_ancestors = [anc for anc in exc.ancestors() |
| 265 if isinstance(anc, astroid.Class)] | 270 if isinstance(anc, astroid.Class)] |
| 266 for previous_exc in exceptions_classes: | 271 for previous_exc in exceptions_classes: |
| 267 if previous_exc in exc_ancestors: | 272 if previous_exc in exc_ancestors: |
| 268 msg = '%s is an ancestor class of %s' % ( | 273 msg = '%s is an ancestor class of %s' % ( |
| 269 previous_exc.name, exc.name) | 274 previous_exc.name, exc.name) |
| 270 self.add_message('bad-except-order', node=handler.ty
pe, args=msg) | 275 self.add_message('bad-except-order', |
| 276 node=handler.type, args=msg) |
| 271 if (exc.name in self.config.overgeneral_exceptions | 277 if (exc.name in self.config.overgeneral_exceptions |
| 272 and exc.root().name == EXCEPTIONS_MODULE | 278 and exc.root().name == EXCEPTIONS_MODULE |
| 273 and not is_raising(handler.body)): | 279 and not is_raising(handler.body)): |
| 274 self.add_message('broad-except', args=exc.name, node=han
dler.type) | 280 self.add_message('broad-except', |
| 281 args=exc.name, node=handler.type) |
| 275 | 282 |
| 276 if (not inherit_from_std_ex(exc) and | 283 if (not inherit_from_std_ex(exc) and |
| 277 exc.root().name != BUILTINS_NAME): | 284 exc.root().name != BUILTINS_NAME): |
| 278 # try to see if the exception is based on a C based | 285 if has_known_bases(exc): |
| 279 # exception, by infering all the base classes and | |
| 280 # looking for inference errors | |
| 281 bases = infer_bases(exc) | |
| 282 fully_infered = all(inferit is not YES | |
| 283 for inferit in bases) | |
| 284 if fully_infered: | |
| 285 self.add_message('catching-non-exception', | 286 self.add_message('catching-non-exception', |
| 286 node=handler.type, | 287 node=handler.type, |
| 287 args=(exc.name, )) | 288 args=(exc.name, )) |
| 288 | 289 |
| 289 exceptions_classes += excs | 290 exceptions_classes += [exc for _, exc in excs] |
| 290 | 291 |
| 291 | 292 |
| 292 def inherit_from_std_ex(node): | |
| 293 """return true if the given class node is subclass of | |
| 294 exceptions.Exception | |
| 295 """ | |
| 296 if node.name in ('Exception', 'BaseException') \ | |
| 297 and node.root().name == EXCEPTIONS_MODULE: | |
| 298 return True | |
| 299 for parent in node.ancestors(recurs=False): | |
| 300 if inherit_from_std_ex(parent): | |
| 301 return True | |
| 302 return False | |
| 303 | |
| 304 def register(linter): | 293 def register(linter): |
| 305 """required method to auto register this checker""" | 294 """required method to auto register this checker""" |
| 306 linter.register_checker(ExceptionsChecker(linter)) | 295 linter.register_checker(ExceptionsChecker(linter)) |
| OLD | NEW |