Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(351)

Side by Side Diff: third_party/pylint/checkers/exceptions.py

Issue 876793002: pylint: upgrade to 1.4.1 (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/depot_tools.git@master
Patch Set: Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « third_party/pylint/checkers/classes.py ('k') | third_party/pylint/checkers/misc.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
20 BUILTINS_NAME = builtins.__name__
21 import astroid 19 import astroid
22 from astroid import YES, Instance, unpack_infer, List, Tuple 20 from astroid import YES, Instance, unpack_infer, List, Tuple
21 from logilab.common.compat import builtins
23 22
24 from pylint.checkers import BaseChecker 23 from pylint.checkers import BaseChecker
25 from pylint.checkers.utils import ( 24 from pylint.checkers.utils import (
26 is_empty, is_raising, 25 is_empty,
27 check_messages, inherit_from_std_ex, 26 is_raising,
28 EXCEPTIONS_MODULE, has_known_bases) 27 check_messages,
28 inherit_from_std_ex,
29 EXCEPTIONS_MODULE,
30 has_known_bases,
31 safe_infer)
29 from pylint.interfaces import IAstroidChecker, INFERENCE, INFERENCE_FAILURE 32 from pylint.interfaces import IAstroidChecker, INFERENCE, INFERENCE_FAILURE
30 33
34
31 def _annotated_unpack_infer(stmt, context=None): 35 def _annotated_unpack_infer(stmt, context=None):
32 """ 36 """
33 Recursively generate nodes inferred by the given statement. 37 Recursively generate nodes inferred by the given statement.
34 If the inferred value is a list or a tuple, recurse on the elements. 38 If the inferred value is a list or a tuple, recurse on the elements.
35 Returns an iterator which yields tuples in the format 39 Returns an iterator which yields tuples in the format
36 ('original node', 'infered node'). 40 ('original node', 'infered node').
37 """ 41 """
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)): 42 if isinstance(stmt, (List, Tuple)):
44 for elt in stmt.elts: 43 for elt in stmt.elts:
45 for infered_elt in unpack_infer(elt, context): 44 inferred = safe_infer(elt)
46 yield elt, infered_elt 45 if inferred and inferred is not YES:
46 yield elt, inferred
47 return 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): 48 for infered in stmt.infer(context):
55 if infered is YES: 49 if infered is YES:
56 yield stmt, infered 50 continue
57 else: 51 yield stmt, infered
58 for inf_inf in unpack_infer(infered, context):
59 yield stmt, inf_inf
60 52
61 53
62 PY3K = sys.version_info >= (3, 0) 54 PY3K = sys.version_info >= (3, 0)
63 OVERGENERAL_EXCEPTIONS = ('Exception',) 55 OVERGENERAL_EXCEPTIONS = ('Exception',)
64 56 BUILTINS_NAME = builtins.__name__
65 MSGS = { 57 MSGS = {
66 'E0701': ('Bad except clauses order (%s)', 58 'E0701': ('Bad except clauses order (%s)',
67 'bad-except-order', 59 'bad-except-order',
68 'Used when except clauses are not in the correct order (from the ' 60 'Used when except clauses are not in the correct order (from the '
69 'more specific to the more generic). If you don\'t fix the order, ' 61 'more specific to the more generic). If you don\'t fix the order, '
70 'some exceptions may not be catched by the most specific handler.' ), 62 'some exceptions may not be catched by the most specific handler.' ),
71 'E0702': ('Raising %s while only classes or instances are allowed', 63 'E0702': ('Raising %s while only classes or instances are allowed',
72 'raising-bad-type', 64 'raising-bad-type',
73 'Used when something which is neither a class, an instance or a \ 65 'Used when something which is neither a class, an instance or a \
74 string is raised (i.e. a `TypeError` will be raised).'), 66 string is raised (i.e. a `TypeError` will be raised).'),
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
138 130
139 @check_messages('nonstandard-exception', 131 @check_messages('nonstandard-exception',
140 'raising-bad-type', 'raising-non-exception', 132 'raising-bad-type', 'raising-non-exception',
141 'notimplemented-raised', 'bad-exception-context') 133 'notimplemented-raised', 'bad-exception-context')
142 def visit_raise(self, node): 134 def visit_raise(self, node):
143 """visit raise possibly inferring value""" 135 """visit raise possibly inferring value"""
144 # ignore empty raise 136 # ignore empty raise
145 if node.exc is None: 137 if node.exc is None:
146 return 138 return
147 if PY3K and node.cause: 139 if PY3K and node.cause:
148 try: 140 self._check_bad_exception_context(node)
149 cause = next(node.cause.infer()) 141
150 except astroid.InferenceError:
151 pass
152 else:
153 if cause is YES:
154 return
155 if isinstance(cause, astroid.Const):
156 if cause.value is not None:
157 self.add_message('bad-exception-context',
158 node=node)
159 elif (not isinstance(cause, astroid.Class) and
160 not inherit_from_std_ex(cause)):
161 self.add_message('bad-exception-context',
162 node=node)
163 expr = node.exc 142 expr = node.exc
164 if self._check_raise_value(node, expr): 143 if self._check_raise_value(node, expr):
165 return 144 return
166 else: 145 else:
167 try: 146 try:
168 value = next(unpack_infer(expr)) 147 value = next(unpack_infer(expr))
169 except astroid.InferenceError: 148 except astroid.InferenceError:
170 return 149 return
171 self._check_raise_value(node, value) 150 self._check_raise_value(node, value)
172 151
152 def _check_bad_exception_context(self, node):
153 """Verify that the exception context is properly set.
154
155 An exception context can be only `None` or an exception.
156 """
157 cause = safe_infer(node.cause)
158 if cause in (YES, None):
159 return
160 if isinstance(cause, astroid.Const):
161 if cause.value is not None:
162 self.add_message('bad-exception-context',
163 node=node)
164 elif (not isinstance(cause, astroid.Class) and
165 not inherit_from_std_ex(cause)):
166 self.add_message('bad-exception-context',
167 node=node)
168
173 def _check_raise_value(self, node, expr): 169 def _check_raise_value(self, node, expr):
174 """check for bad values, string exception and class inheritance 170 """check for bad values, string exception and class inheritance
175 """ 171 """
176 value_found = True 172 value_found = True
177 if isinstance(expr, astroid.Const): 173 if isinstance(expr, astroid.Const):
178 value = expr.value 174 value = expr.value
179 if isinstance(value, str): 175 if not isinstance(value, str):
180 # raising-string will be emitted from python3 porting checker. 176 # raising-string will be emitted from python3 porting checker.
181 pass
182 else:
183 self.add_message('raising-bad-type', node=node, 177 self.add_message('raising-bad-type', node=node,
184 args=value.__class__.__name__) 178 args=value.__class__.__name__)
185 elif (isinstance(expr, astroid.Name) and \ 179 elif ((isinstance(expr, astroid.Name) and
186 expr.name in ('None', 'True', 'False')) or \ 180 expr.name in ('None', 'True', 'False')) or
187 isinstance(expr, (astroid.List, astroid.Dict, astroid.Tuple, 181 isinstance(expr, (astroid.List, astroid.Dict, astroid.Tuple,
188 astroid.Module, astroid.Function)): 182 astroid.Module, astroid.Function))):
189 self.add_message('raising-bad-type', node=node, args=expr.name) 183 emit = True
184 if not PY3K and isinstance(expr, astroid.Tuple):
185 # On Python 2, using the following is not an error:
186 # raise (ZeroDivisionError, None)
187 # raise (ZeroDivisionError, )
188 # What's left to do is to check that the first
189 # argument is indeed an exception.
190 # Verifying the other arguments is not
191 # the scope of this check.
192 first = expr.elts[0]
193 inferred = safe_infer(first)
194 if isinstance(inferred, Instance):
195 # pylint: disable=protected-access
196 inferred = inferred._proxied
197 if (inferred is YES or
198 isinstance(inferred, astroid.Class)
199 and inherit_from_std_ex(inferred)):
200 emit = False
201 if emit:
202 self.add_message('raising-bad-type',
203 node=node,
204 args=expr.name)
190 elif ((isinstance(expr, astroid.Name) and expr.name == 'NotImplemented') 205 elif ((isinstance(expr, astroid.Name) and expr.name == 'NotImplemented')
191 or (isinstance(expr, astroid.CallFunc) and 206 or (isinstance(expr, astroid.CallFunc) and
192 isinstance(expr.func, astroid.Name) and 207 isinstance(expr.func, astroid.Name) and
193 expr.func.name == 'NotImplemented')): 208 expr.func.name == 'NotImplemented')):
194 self.add_message('notimplemented-raised', node=node) 209 self.add_message('notimplemented-raised', node=node)
195 elif isinstance(expr, (Instance, astroid.Class)): 210 elif isinstance(expr, (Instance, astroid.Class)):
196 if isinstance(expr, Instance): 211 if isinstance(expr, Instance):
212 # pylint: disable=protected-access
197 expr = expr._proxied 213 expr = expr._proxied
198 if (isinstance(expr, astroid.Class) and 214 if (isinstance(expr, astroid.Class) and
199 not inherit_from_std_ex(expr) and 215 not inherit_from_std_ex(expr)):
200 expr.root().name != BUILTINS_NAME):
201 if expr.newstyle: 216 if expr.newstyle:
202 self.add_message('raising-non-exception', node=node) 217 self.add_message('raising-non-exception', node=node)
203 else: 218 else:
219 if has_known_bases(expr):
220 confidence = INFERENCE
221 else:
222 confidence = INFERENCE_FAILURE
204 self.add_message( 223 self.add_message(
205 'nonstandard-exception', node=node, 224 'nonstandard-exception', node=node,
206 confidence=INFERENCE if has_known_bases(expr) else INFER ENCE_FAILURE) 225 confidence=confidence)
207 else: 226 else:
208 value_found = False 227 value_found = False
209 else: 228 else:
210 value_found = False 229 value_found = False
211 return value_found 230 return value_found
212 231
232 def _check_catching_non_exception(self, handler, exc, part):
233 if isinstance(exc, astroid.Tuple):
234 # Check if it is a tuple of exceptions.
235 inferred = [safe_infer(elt) for elt in exc.elts]
236 if any(node is astroid.YES for node in inferred):
237 # Don't emit if we don't know every component.
238 return
239 if all(node and inherit_from_std_ex(node)
240 for node in inferred):
241 return
242
243 if not isinstance(exc, astroid.Class):
244 # Don't emit the warning if the infered stmt
245 # is None, but the exception handler is something else,
246 # maybe it was redefined.
247 if (isinstance(exc, astroid.Const) and
248 exc.value is None):
249 if ((isinstance(handler.type, astroid.Const) and
250 handler.type.value is None) or
251 handler.type.parent_of(exc)):
252 # If the exception handler catches None or
253 # the exception component, which is None, is
254 # defined by the entire exception handler, then
255 # emit a warning.
256 self.add_message('catching-non-exception',
257 node=handler.type,
258 args=(part.as_string(), ))
259 else:
260 self.add_message('catching-non-exception',
261 node=handler.type,
262 args=(part.as_string(), ))
263 return
264 if (not inherit_from_std_ex(exc) and
265 exc.root().name != BUILTINS_NAME):
266 if has_known_bases(exc):
267 self.add_message('catching-non-exception',
268 node=handler.type,
269 args=(exc.name, ))
270
213 @check_messages('bare-except', 'broad-except', 'pointless-except', 271 @check_messages('bare-except', 'broad-except', 'pointless-except',
214 'binary-op-exception', 'bad-except-order', 272 'binary-op-exception', 'bad-except-order',
215 'catching-non-exception') 273 'catching-non-exception')
216 def visit_tryexcept(self, node): 274 def visit_tryexcept(self, node):
217 """check for empty except""" 275 """check for empty except"""
218 exceptions_classes = [] 276 exceptions_classes = []
219 nb_handlers = len(node.handlers) 277 nb_handlers = len(node.handlers)
220 for index, handler in enumerate(node.handlers): 278 for index, handler in enumerate(node.handlers):
221 # single except doing nothing but "pass" without else clause 279 # single except doing nothing but "pass" without else clause
222 if is_empty(handler.body) and not node.orelse: 280 if is_empty(handler.body) and not node.orelse:
(...skipping 12 matching lines...) Expand all
235 self.add_message('binary-op-exception', 293 self.add_message('binary-op-exception',
236 node=handler, args=handler.type.op) 294 node=handler, args=handler.type.op)
237 else: 295 else:
238 try: 296 try:
239 excs = list(_annotated_unpack_infer(handler.type)) 297 excs = list(_annotated_unpack_infer(handler.type))
240 except astroid.InferenceError: 298 except astroid.InferenceError:
241 continue 299 continue
242 for part, exc in excs: 300 for part, exc in excs:
243 if exc is YES: 301 if exc is YES:
244 continue 302 continue
245 if isinstance(exc, astroid.Instance) and inherit_from_std_ex (exc): 303 if (isinstance(exc, astroid.Instance)
304 and inherit_from_std_ex(exc)):
305 # pylint: disable=protected-access
246 exc = exc._proxied 306 exc = exc._proxied
307
308 self._check_catching_non_exception(handler, exc, part)
309
247 if not isinstance(exc, astroid.Class): 310 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 311 continue
268 312
269 exc_ancestors = [anc for anc in exc.ancestors() 313 exc_ancestors = [anc for anc in exc.ancestors()
270 if isinstance(anc, astroid.Class)] 314 if isinstance(anc, astroid.Class)]
271 for previous_exc in exceptions_classes: 315 for previous_exc in exceptions_classes:
272 if previous_exc in exc_ancestors: 316 if previous_exc in exc_ancestors:
273 msg = '%s is an ancestor class of %s' % ( 317 msg = '%s is an ancestor class of %s' % (
274 previous_exc.name, exc.name) 318 previous_exc.name, exc.name)
275 self.add_message('bad-except-order', 319 self.add_message('bad-except-order',
276 node=handler.type, args=msg) 320 node=handler.type, args=msg)
277 if (exc.name in self.config.overgeneral_exceptions 321 if (exc.name in self.config.overgeneral_exceptions
278 and exc.root().name == EXCEPTIONS_MODULE 322 and exc.root().name == EXCEPTIONS_MODULE
279 and not is_raising(handler.body)): 323 and not is_raising(handler.body)):
280 self.add_message('broad-except', 324 self.add_message('broad-except',
281 args=exc.name, node=handler.type) 325 args=exc.name, node=handler.type)
282 326
283 if (not inherit_from_std_ex(exc) and
284 exc.root().name != BUILTINS_NAME):
285 if has_known_bases(exc):
286 self.add_message('catching-non-exception',
287 node=handler.type,
288 args=(exc.name, ))
289
290 exceptions_classes += [exc for _, exc in excs] 327 exceptions_classes += [exc for _, exc in excs]
291 328
292 329
293 def register(linter): 330 def register(linter):
294 """required method to auto register this checker""" 331 """required method to auto register this checker"""
295 linter.register_checker(ExceptionsChecker(linter)) 332 linter.register_checker(ExceptionsChecker(linter))
OLDNEW
« no previous file with comments | « third_party/pylint/checkers/classes.py ('k') | third_party/pylint/checkers/misc.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698