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

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

Issue 753543006: pylint: upgrade to 1.4.0 (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/depot_tools.git@master
Patch Set: Created 6 years 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/design_analysis.py ('k') | third_party/pylint/checkers/format.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 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))
OLDNEW
« no previous file with comments | « third_party/pylint/checkers/design_analysis.py ('k') | third_party/pylint/checkers/format.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698