OLD | NEW |
1 # Copyright (c) 2003-2014 LOGILAB S.A. (Paris, FRANCE). | 1 # Copyright (c) 2003-2014 LOGILAB S.A. (Paris, FRANCE). |
2 # http://www.logilab.fr/ -- mailto:contact@logilab.fr | 2 # http://www.logilab.fr/ -- mailto:contact@logilab.fr |
3 # | 3 # |
4 # This program is free software; you can redistribute it and/or modify it under | 4 # This program is free software; you can redistribute it and/or modify it under |
5 # the terms of the GNU General Public License as published by the Free Software | 5 # the terms of the GNU General Public License as published by the Free Software |
6 # Foundation; either version 2 of the License, or (at your option) any later | 6 # Foundation; either version 2 of the License, or (at your option) any later |
7 # version. | 7 # version. |
8 # | 8 # |
9 # This program is distributed in the hope that it will be useful, but WITHOUT | 9 # This program is distributed in the hope that it will be useful, but WITHOUT |
10 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | 10 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
11 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. | 11 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. |
12 # | 12 # |
13 # You should have received a copy of the GNU General Public License along with | 13 # You should have received a copy of the GNU General Public License along with |
14 # this program; if not, write to the Free Software Foundation, Inc., | 14 # this program; if not, write to the Free Software Foundation, Inc., |
15 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | 15 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
16 """variables checkers for Python code | 16 """variables checkers for Python code |
17 """ | 17 """ |
18 import os | 18 import os |
19 import sys | 19 import sys |
| 20 import re |
20 from copy import copy | 21 from copy import copy |
21 | 22 |
22 import astroid | 23 import astroid |
23 from astroid import are_exclusive, builtin_lookup, AstroidBuildingException | 24 from astroid import are_exclusive, builtin_lookup |
| 25 from astroid import modutils |
24 | 26 |
25 from logilab.common.modutils import file_from_modpath | 27 from pylint.interfaces import IAstroidChecker, INFERENCE, INFERENCE_FAILURE, HIG
H |
26 | |
27 from pylint.interfaces import IAstroidChecker | |
28 from pylint.utils import get_global_option | 28 from pylint.utils import get_global_option |
29 from pylint.checkers import BaseChecker | 29 from pylint.checkers import BaseChecker |
30 from pylint.checkers.utils import ( | 30 from pylint.checkers.utils import ( |
31 PYMETHODS, is_ancestor_name, is_builtin, | 31 PYMETHODS, is_ancestor_name, is_builtin, |
32 is_defined_before, is_error, is_func_default, is_func_decorator, | 32 is_defined_before, is_error, is_func_default, is_func_decorator, |
33 assign_parent, check_messages, is_inside_except, clobber_in_except, | 33 assign_parent, check_messages, is_inside_except, clobber_in_except, |
34 get_all_elements) | 34 get_all_elements, has_known_bases) |
| 35 import six |
35 | 36 |
| 37 SPECIAL_OBJ = re.compile("^_{2}[a-z]+_{2}$") |
| 38 |
| 39 PY3K = sys.version_info >= (3, 0) |
36 | 40 |
37 def in_for_else_branch(parent, stmt): | 41 def in_for_else_branch(parent, stmt): |
38 """Returns True if stmt in inside the else branch for a parent For stmt.""" | 42 """Returns True if stmt in inside the else branch for a parent For stmt.""" |
39 return (isinstance(parent, astroid.For) and | 43 return (isinstance(parent, astroid.For) and |
40 any(else_stmt.parent_of(stmt) for else_stmt in parent.orelse)) | 44 any(else_stmt.parent_of(stmt) for else_stmt in parent.orelse)) |
41 | 45 |
42 def overridden_method(klass, name): | 46 def overridden_method(klass, name): |
43 """get overridden method if any""" | 47 """get overridden method if any""" |
44 try: | 48 try: |
45 parent = klass.local_attr_ancestors(name).next() | 49 parent = next(klass.local_attr_ancestors(name)) |
46 except (StopIteration, KeyError): | 50 except (StopIteration, KeyError): |
47 return None | 51 return None |
48 try: | 52 try: |
49 meth_node = parent[name] | 53 meth_node = parent[name] |
50 except KeyError: | 54 except KeyError: |
51 # We have found an ancestor defining <name> but it's not in the local | 55 # We have found an ancestor defining <name> but it's not in the local |
52 # dictionary. This may happen with astroid built from living objects. | 56 # dictionary. This may happen with astroid built from living objects. |
53 return None | 57 return None |
54 if isinstance(meth_node, astroid.Function): | 58 if isinstance(meth_node, astroid.Function): |
55 return meth_node | 59 return meth_node |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
127 # If the stored scopes are, in fact, the very same, then it means | 131 # If the stored scopes are, in fact, the very same, then it means |
128 # that the two frames (frame and defframe) shares the same scope, | 132 # that the two frames (frame and defframe) shares the same scope, |
129 # and we could apply our lineno analysis over them. | 133 # and we could apply our lineno analysis over them. |
130 # For instance, this works when they are inside a function, the node | 134 # For instance, this works when they are inside a function, the node |
131 # that uses a definition and the definition itself. | 135 # that uses a definition and the definition itself. |
132 return False | 136 return False |
133 # At this point, we are certain that frame and defframe shares a scope | 137 # At this point, we are certain that frame and defframe shares a scope |
134 # and the definition of the first depends on the second. | 138 # and the definition of the first depends on the second. |
135 return frame.lineno < defframe.lineno | 139 return frame.lineno < defframe.lineno |
136 | 140 |
| 141 def _fix_dot_imports(not_consumed): |
| 142 """ Try to fix imports with multiple dots, by returning a dictionary |
| 143 with the import names expanded. The function unflattens root imports, |
| 144 like 'xml' (when we have both 'xml.etree' and 'xml.sax'), to 'xml.etree' |
| 145 and 'xml.sax' respectively. |
| 146 """ |
| 147 # TODO: this should be improved in issue astroid #46 |
| 148 names = {} |
| 149 for name, stmts in six.iteritems(not_consumed): |
| 150 if any(isinstance(stmt, astroid.AssName) |
| 151 and isinstance(stmt.ass_type(), astroid.AugAssign) |
| 152 for stmt in stmts): |
| 153 continue |
| 154 for stmt in stmts: |
| 155 if not isinstance(stmt, (astroid.From, astroid.Import)): |
| 156 continue |
| 157 for imports in stmt.names: |
| 158 second_name = None |
| 159 if imports[0] == "*": |
| 160 # In case of wildcard imports, |
| 161 # pick the name from inside the imported module. |
| 162 second_name = name |
| 163 else: |
| 164 if imports[0].find(".") > -1 or name in imports: |
| 165 # Most likely something like 'xml.etree', |
| 166 # which will appear in the .locals as 'xml'. |
| 167 # Only pick the name if it wasn't consumed. |
| 168 second_name = imports[0] |
| 169 if second_name and second_name not in names: |
| 170 names[second_name] = stmt |
| 171 return sorted(names.items(), key=lambda a: a[1].fromlineno) |
| 172 |
| 173 def _find_frame_imports(name, frame): |
| 174 """ |
| 175 Detect imports in the frame, with the required |
| 176 *name*. Such imports can be considered assignments. |
| 177 Returns True if an import for the given name was found. |
| 178 """ |
| 179 imports = frame.nodes_of_class((astroid.Import, astroid.From)) |
| 180 for import_node in imports: |
| 181 for import_name, import_alias in import_node.names: |
| 182 # If the import uses an alias, check only that. |
| 183 # Otherwise, check only the import name. |
| 184 if import_alias: |
| 185 if import_alias == name: |
| 186 return True |
| 187 elif import_name and import_name == name: |
| 188 return True |
| 189 |
137 | 190 |
138 MSGS = { | 191 MSGS = { |
139 'E0601': ('Using variable %r before assignment', | 192 'E0601': ('Using variable %r before assignment', |
140 'used-before-assignment', | 193 'used-before-assignment', |
141 'Used when a local variable is accessed before it\'s \ | 194 'Used when a local variable is accessed before it\'s \ |
142 assignment.'), | 195 assignment.'), |
143 'E0602': ('Undefined variable %r', | 196 'E0602': ('Undefined variable %r', |
144 'undefined-variable', | 197 'undefined-variable', |
145 'Used when an undefined variable is accessed.'), | 198 'Used when an undefined variable is accessed.'), |
146 'E0603': ('Undefined variable name %r in __all__', | 199 'E0603': ('Undefined variable name %r in __all__', |
(...skipping 10 matching lines...) Expand all Loading... |
157 'global-variable-undefined', | 210 'global-variable-undefined', |
158 'Used when a variable is defined through the "global" statement \ | 211 'Used when a variable is defined through the "global" statement \ |
159 but the variable is not defined in the module scope.'), | 212 but the variable is not defined in the module scope.'), |
160 'W0602': ('Using global for %r but no assignment is done', | 213 'W0602': ('Using global for %r but no assignment is done', |
161 'global-variable-not-assigned', | 214 'global-variable-not-assigned', |
162 'Used when a variable is defined through the "global" statement \ | 215 'Used when a variable is defined through the "global" statement \ |
163 but no assignment to this variable is done.'), | 216 but no assignment to this variable is done.'), |
164 'W0603': ('Using the global statement', # W0121 | 217 'W0603': ('Using the global statement', # W0121 |
165 'global-statement', | 218 'global-statement', |
166 'Used when you use the "global" statement to update a global \ | 219 'Used when you use the "global" statement to update a global \ |
167 variable. PyLint just try to discourage this \ | 220 variable. Pylint just try to discourage this \ |
168 usage. That doesn\'t mean you can not use it !'), | 221 usage. That doesn\'t mean you can not use it !'), |
169 'W0604': ('Using the global statement at the module level', # W0103 | 222 'W0604': ('Using the global statement at the module level', # W0103 |
170 'global-at-module-level', | 223 'global-at-module-level', |
171 'Used when you use the "global" statement at the module level \ | 224 'Used when you use the "global" statement at the module level \ |
172 since it has no effect'), | 225 since it has no effect'), |
173 'W0611': ('Unused import %s', | 226 'W0611': ('Unused %s', |
174 'unused-import', | 227 'unused-import', |
175 'Used when an imported module or variable is not used.'), | 228 'Used when an imported module or variable is not used.'), |
176 'W0612': ('Unused variable %r', | 229 'W0612': ('Unused variable %r', |
177 'unused-variable', | 230 'unused-variable', |
178 'Used when a variable is defined but not used.'), | 231 'Used when a variable is defined but not used.'), |
179 'W0613': ('Unused argument %r', | 232 'W0613': ('Unused argument %r', |
180 'unused-argument', | 233 'unused-argument', |
181 'Used when a function or method argument is not used.'), | 234 'Used when a function or method argument is not used.'), |
182 'W0614': ('Unused import %s from wildcard import', | 235 'W0614': ('Unused import %s from wildcard import', |
183 'unused-wildcard-import', | 236 'unused-wildcard-import', |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
243 {'default': ('_$|dummy'), | 296 {'default': ('_$|dummy'), |
244 'type' :'regexp', 'metavar' : '<regexp>', | 297 'type' :'regexp', 'metavar' : '<regexp>', |
245 'help' : 'A regular expression matching the name of dummy \ | 298 'help' : 'A regular expression matching the name of dummy \ |
246 variables (i.e. expectedly not used).'}), | 299 variables (i.e. expectedly not used).'}), |
247 ("additional-builtins", | 300 ("additional-builtins", |
248 {'default': (), 'type' : 'csv', | 301 {'default': (), 'type' : 'csv', |
249 'metavar' : '<comma separated list>', | 302 'metavar' : '<comma separated list>', |
250 'help' : 'List of additional names supposed to be defined in \ | 303 'help' : 'List of additional names supposed to be defined in \ |
251 builtins. Remember that you should avoid to define new builtins when possible.' | 304 builtins. Remember that you should avoid to define new builtins when possible.' |
252 }), | 305 }), |
| 306 ("callbacks", |
| 307 {'default' : ('cb_', '_cb'), 'type' : 'csv', |
| 308 'metavar' : '<callbacks>', |
| 309 'help' : 'List of strings which can identify a callback ' |
| 310 'function by name. A callback name must start or ' |
| 311 'end with one of those strings.'} |
| 312 ) |
253 ) | 313 ) |
254 def __init__(self, linter=None): | 314 def __init__(self, linter=None): |
255 BaseChecker.__init__(self, linter) | 315 BaseChecker.__init__(self, linter) |
256 self._to_consume = None | 316 self._to_consume = None |
257 self._checking_mod_attr = None | 317 self._checking_mod_attr = None |
258 | 318 |
259 def visit_module(self, node): | 319 def visit_module(self, node): |
260 """visit module : update consumption analysis variable | 320 """visit module : update consumption analysis variable |
261 checks globals doesn't overrides builtins | 321 checks globals doesn't overrides builtins |
262 """ | 322 """ |
263 self._to_consume = [(copy(node.locals), {}, 'module')] | 323 self._to_consume = [(copy(node.locals), {}, 'module')] |
264 for name, stmts in node.locals.iteritems(): | 324 for name, stmts in six.iteritems(node.locals): |
265 if is_builtin(name) and not is_inside_except(stmts[0]): | 325 if is_builtin(name) and not is_inside_except(stmts[0]): |
266 # do not print Redefining builtin for additional builtins | 326 # do not print Redefining builtin for additional builtins |
267 self.add_message('redefined-builtin', args=name, node=stmts[0]) | 327 self.add_message('redefined-builtin', args=name, node=stmts[0]) |
268 | 328 |
269 @check_messages('unused-import', 'unused-wildcard-import', 'redefined-builti
n', 'undefined-all-variable', 'invalid-all-object') | 329 @check_messages('unused-import', 'unused-wildcard-import', |
| 330 'redefined-builtin', 'undefined-all-variable', |
| 331 'invalid-all-object') |
270 def leave_module(self, node): | 332 def leave_module(self, node): |
271 """leave module: check globals | 333 """leave module: check globals |
272 """ | 334 """ |
273 assert len(self._to_consume) == 1 | 335 assert len(self._to_consume) == 1 |
274 not_consumed = self._to_consume.pop()[0] | 336 not_consumed = self._to_consume.pop()[0] |
275 # attempt to check for __all__ if defined | 337 # attempt to check for __all__ if defined |
276 if '__all__' in node.locals: | 338 if '__all__' in node.locals: |
277 assigned = node.igetattr('__all__').next() | 339 assigned = next(node.igetattr('__all__')) |
278 if assigned is not astroid.YES: | 340 if assigned is not astroid.YES: |
279 for elt in getattr(assigned, 'elts', ()): | 341 for elt in getattr(assigned, 'elts', ()): |
280 try: | 342 try: |
281 elt_name = elt.infer().next() | 343 elt_name = next(elt.infer()) |
282 except astroid.InferenceError: | 344 except astroid.InferenceError: |
283 continue | 345 continue |
284 | 346 |
285 if not isinstance(elt_name, astroid.Const) \ | 347 if not isinstance(elt_name, astroid.Const) \ |
286 or not isinstance(elt_name.value, basestring): | 348 or not isinstance(elt_name.value, six.string_types)
: |
287 self.add_message('invalid-all-object', args=elt.as_strin
g(), node=elt) | 349 self.add_message('invalid-all-object', |
| 350 args=elt.as_string(), node=elt) |
288 continue | 351 continue |
289 elt_name = elt_name.value | 352 elt_name = elt_name.value |
290 # If elt is in not_consumed, remove it from not_consumed | 353 # If elt is in not_consumed, remove it from not_consumed |
291 if elt_name in not_consumed: | 354 if elt_name in not_consumed: |
292 del not_consumed[elt_name] | 355 del not_consumed[elt_name] |
293 continue | 356 continue |
294 if elt_name not in node.locals: | 357 if elt_name not in node.locals: |
295 if not node.package: | 358 if not node.package: |
296 self.add_message('undefined-all-variable', | 359 self.add_message('undefined-all-variable', |
297 args=elt_name, | 360 args=elt_name, |
298 node=elt) | 361 node=elt) |
299 else: | 362 else: |
300 basename = os.path.splitext(node.file)[0] | 363 basename = os.path.splitext(node.file)[0] |
301 if os.path.basename(basename) == '__init__': | 364 if os.path.basename(basename) == '__init__': |
302 name = node.name + "." + elt_name | 365 name = node.name + "." + elt_name |
303 try: | 366 try: |
304 file_from_modpath(name.split(".")) | 367 modutils.file_from_modpath(name.split(".")) |
305 except ImportError: | 368 except ImportError: |
306 self.add_message('undefined-all-variable', | 369 self.add_message('undefined-all-variable', |
307 args=elt_name, | 370 args=elt_name, |
308 node=elt) | 371 node=elt) |
309 except SyntaxError: | 372 except SyntaxError: |
310 # don't yield an syntax-error warning, | 373 # don't yield an syntax-error warning, |
311 # because it will be later yielded | 374 # because it will be later yielded |
312 # when the file will be checked | 375 # when the file will be checked |
313 pass | 376 pass |
314 # don't check unused imports in __init__ files | 377 # don't check unused imports in __init__ files |
315 if not self.config.init_import and node.package: | 378 if not self.config.init_import and node.package: |
316 return | 379 return |
317 for name, stmts in not_consumed.iteritems(): | 380 |
318 if any(isinstance(stmt, astroid.AssName) | 381 self._check_imports(not_consumed) |
319 and isinstance(stmt.ass_type(), astroid.AugAssign) | 382 |
320 for stmt in stmts): | 383 def _check_imports(self, not_consumed): |
321 continue | 384 local_names = _fix_dot_imports(not_consumed) |
322 stmt = stmts[0] | 385 checked = set() |
323 if isinstance(stmt, astroid.Import): | 386 for name, stmt in local_names: |
324 self.add_message('unused-import', args=name, node=stmt) | 387 for imports in stmt.names: |
325 elif isinstance(stmt, astroid.From) and stmt.modname != '__future__'
: | 388 real_name = imported_name = imports[0] |
326 if stmt.names[0][0] == '*': | 389 if imported_name == "*": |
327 self.add_message('unused-wildcard-import', args=name, node=s
tmt) | 390 real_name = name |
328 else: | 391 as_name = imports[1] |
329 self.add_message('unused-import', args=name, node=stmt) | 392 if real_name in checked: |
| 393 continue |
| 394 if name not in (real_name, as_name): |
| 395 continue |
| 396 checked.add(real_name) |
| 397 |
| 398 if (isinstance(stmt, astroid.Import) or |
| 399 (isinstance(stmt, astroid.From) and |
| 400 not stmt.modname)): |
| 401 if (isinstance(stmt, astroid.From) and |
| 402 SPECIAL_OBJ.search(imported_name)): |
| 403 # Filter special objects (__doc__, __all__) etc., |
| 404 # because they can be imported for exporting. |
| 405 continue |
| 406 if as_name is None: |
| 407 msg = "import %s" % imported_name |
| 408 else: |
| 409 msg = "%s imported as %s" % (imported_name, as_name) |
| 410 self.add_message('unused-import', args=msg, node=stmt) |
| 411 elif isinstance(stmt, astroid.From) and stmt.modname != '__futur
e__': |
| 412 if SPECIAL_OBJ.search(imported_name): |
| 413 # Filter special objects (__doc__, __all__) etc., |
| 414 # because they can be imported for exporting. |
| 415 continue |
| 416 if imported_name == '*': |
| 417 self.add_message('unused-wildcard-import', |
| 418 args=name, node=stmt) |
| 419 else: |
| 420 if as_name is None: |
| 421 msg = "%s imported from %s" % (imported_name, stmt.m
odname) |
| 422 else: |
| 423 fields = (imported_name, stmt.modname, as_name) |
| 424 msg = "%s imported from %s as %s" % fields |
| 425 self.add_message('unused-import', args=msg, node=stmt) |
330 del self._to_consume | 426 del self._to_consume |
331 | 427 |
332 def visit_class(self, node): | 428 def visit_class(self, node): |
333 """visit class: update consumption analysis variable | 429 """visit class: update consumption analysis variable |
334 """ | 430 """ |
335 self._to_consume.append((copy(node.locals), {}, 'class')) | 431 self._to_consume.append((copy(node.locals), {}, 'class')) |
336 | 432 |
337 def leave_class(self, _): | 433 def leave_class(self, _): |
338 """leave class: update consumption analysis variable | 434 """leave class: update consumption analysis variable |
339 """ | 435 """ |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
411 self.linter.is_message_enabled('unused-argument')): | 507 self.linter.is_message_enabled('unused-argument')): |
412 return | 508 return |
413 # don't check arguments of function which are only raising an exception | 509 # don't check arguments of function which are only raising an exception |
414 if is_error(node): | 510 if is_error(node): |
415 return | 511 return |
416 # don't check arguments of abstract methods or within an interface | 512 # don't check arguments of abstract methods or within an interface |
417 is_method = node.is_method() | 513 is_method = node.is_method() |
418 klass = node.parent.frame() | 514 klass = node.parent.frame() |
419 if is_method and (klass.type == 'interface' or node.is_abstract()): | 515 if is_method and (klass.type == 'interface' or node.is_abstract()): |
420 return | 516 return |
| 517 if is_method and isinstance(klass, astroid.Class): |
| 518 confidence = INFERENCE if has_known_bases(klass) else INFERENCE_FAIL
URE |
| 519 else: |
| 520 confidence = HIGH |
421 authorized_rgx = self.config.dummy_variables_rgx | 521 authorized_rgx = self.config.dummy_variables_rgx |
422 called_overridden = False | 522 called_overridden = False |
423 argnames = node.argnames() | 523 argnames = node.argnames() |
424 global_names = set() | 524 global_names = set() |
425 nonlocal_names = set() | 525 nonlocal_names = set() |
426 for global_stmt in node.nodes_of_class(astroid.Global): | 526 for global_stmt in node.nodes_of_class(astroid.Global): |
427 global_names.update(set(global_stmt.names)) | 527 global_names.update(set(global_stmt.names)) |
428 for nonlocal_stmt in node.nodes_of_class(astroid.Nonlocal): | 528 for nonlocal_stmt in node.nodes_of_class(astroid.Nonlocal): |
429 nonlocal_names.update(set(nonlocal_stmt.names)) | 529 nonlocal_names.update(set(nonlocal_stmt.names)) |
430 | 530 |
431 for name, stmts in not_consumed.iteritems(): | 531 for name, stmts in six.iteritems(not_consumed): |
432 # ignore some special names specified by user configuration | 532 # ignore some special names specified by user configuration |
433 if authorized_rgx.match(name): | 533 if authorized_rgx.match(name): |
434 continue | 534 continue |
435 # ignore names imported by the global statement | 535 # ignore names imported by the global statement |
436 # FIXME: should only ignore them if it's assigned latter | 536 # FIXME: should only ignore them if it's assigned latter |
437 stmt = stmts[0] | 537 stmt = stmts[0] |
438 if isinstance(stmt, astroid.Global): | 538 if isinstance(stmt, astroid.Global): |
439 continue | 539 continue |
440 if isinstance(stmt, (astroid.Import, astroid.From)): | 540 if isinstance(stmt, (astroid.Import, astroid.From)): |
441 # Detect imports, assigned to global statements. | 541 # Detect imports, assigned to global statements. |
(...skipping 19 matching lines...) Expand all Loading... |
461 if node.type != 'staticmethod' and name == argnames[0]: | 561 if node.type != 'staticmethod' and name == argnames[0]: |
462 continue | 562 continue |
463 # don't warn for argument of an overridden method | 563 # don't warn for argument of an overridden method |
464 if not called_overridden: | 564 if not called_overridden: |
465 overridden = overridden_method(klass, node.name) | 565 overridden = overridden_method(klass, node.name) |
466 called_overridden = True | 566 called_overridden = True |
467 if overridden is not None and name in overridden.argnames(): | 567 if overridden is not None and name in overridden.argnames(): |
468 continue | 568 continue |
469 if node.name in PYMETHODS and node.name not in ('__init__',
'__new__'): | 569 if node.name in PYMETHODS and node.name not in ('__init__',
'__new__'): |
470 continue | 570 continue |
471 # don't check callback arguments XXX should be configurable | 571 # don't check callback arguments |
472 if node.name.startswith('cb_') or node.name.endswith('_cb'): | 572 if any(node.name.startswith(cb) or node.name.endswith(cb) |
| 573 for cb in self.config.callbacks): |
473 continue | 574 continue |
474 self.add_message('unused-argument', args=name, node=stmt) | 575 self.add_message('unused-argument', args=name, node=stmt, |
| 576 confidence=confidence) |
475 else: | 577 else: |
476 if stmt.parent and isinstance(stmt.parent, astroid.Assign): | 578 if stmt.parent and isinstance(stmt.parent, astroid.Assign): |
477 if name in nonlocal_names: | 579 if name in nonlocal_names: |
478 continue | 580 continue |
479 self.add_message('unused-variable', args=name, node=stmt) | 581 self.add_message('unused-variable', args=name, node=stmt) |
480 | 582 |
481 @check_messages('global-variable-undefined', 'global-variable-not-assigned',
'global-statement', | 583 @check_messages('global-variable-undefined', 'global-variable-not-assigned',
'global-statement', |
482 'global-at-module-level', 'redefined-builtin') | 584 'global-at-module-level', 'redefined-builtin') |
483 def visit_global(self, node): | 585 def visit_global(self, node): |
484 """check names imported exists in the global scope""" | 586 """check names imported exists in the global scope""" |
(...skipping 11 matching lines...) Expand all Loading... |
496 assign_nodes = [] | 598 assign_nodes = [] |
497 for anode in assign_nodes: | 599 for anode in assign_nodes: |
498 if anode.parent is None: | 600 if anode.parent is None: |
499 # node returned for builtin attribute such as __file__, | 601 # node returned for builtin attribute such as __file__, |
500 # __doc__, etc... | 602 # __doc__, etc... |
501 continue | 603 continue |
502 if anode.frame() is frame: | 604 if anode.frame() is frame: |
503 # same scope level assignment | 605 # same scope level assignment |
504 break | 606 break |
505 else: | 607 else: |
506 # global but no assignment | 608 if not _find_frame_imports(name, frame): |
507 # Detect imports in the current frame, with the required | |
508 # name. Such imports can be considered assignments. | |
509 imports = frame.nodes_of_class((astroid.Import, astroid.From)) | |
510 for import_node in imports: | |
511 found = False | |
512 for import_name, import_alias in import_node.names: | |
513 # If the import uses an alias, check only that. | |
514 # Otherwise, check only the import name. | |
515 if import_alias: | |
516 if import_alias == name: | |
517 found = True | |
518 break | |
519 elif import_name and import_name == name: | |
520 found = True | |
521 break | |
522 if found: | |
523 break | |
524 else: | |
525 self.add_message('global-variable-not-assigned', | 609 self.add_message('global-variable-not-assigned', |
526 args=name, node=node) | 610 args=name, node=node) |
527 default_message = False | 611 default_message = False |
528 if not assign_nodes: | 612 if not assign_nodes: |
529 continue | 613 continue |
530 for anode in assign_nodes: | 614 for anode in assign_nodes: |
531 if anode.parent is None: | 615 if anode.parent is None: |
532 self.add_message('redefined-builtin', args=name, node=node) | 616 self.add_message('redefined-builtin', args=name, node=node) |
533 break | 617 break |
534 if anode.frame() is module: | 618 if anode.frame() is module: |
535 # module level assignment | 619 # module level assignment |
536 break | 620 break |
537 else: | 621 else: |
538 # global undefined at the module scope | 622 # global undefined at the module scope |
539 self.add_message('global-variable-undefined', args=name, node=no
de) | 623 self.add_message('global-variable-undefined', args=name, node=no
de) |
540 default_message = False | 624 default_message = False |
541 if default_message: | 625 if default_message: |
542 self.add_message('global-statement', node=node) | 626 self.add_message('global-statement', node=node) |
543 | 627 |
544 def _check_late_binding_closure(self, node, assignment_node, scope_type): | 628 def _check_late_binding_closure(self, node, assignment_node): |
545 def _is_direct_lambda_call(): | 629 def _is_direct_lambda_call(): |
546 return (isinstance(node_scope.parent, astroid.CallFunc) | 630 return (isinstance(node_scope.parent, astroid.CallFunc) |
547 and node_scope.parent.func is node_scope) | 631 and node_scope.parent.func is node_scope) |
548 | 632 |
549 node_scope = node.scope() | 633 node_scope = node.scope() |
550 if not isinstance(node_scope, (astroid.Lambda, astroid.Function)): | 634 if not isinstance(node_scope, (astroid.Lambda, astroid.Function)): |
551 return | 635 return |
552 if isinstance(node.parent, astroid.Arguments): | 636 if isinstance(node.parent, astroid.Arguments): |
553 return | 637 return |
554 | 638 |
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
644 to_consume, consumed, scope_type = self._to_consume[i] | 728 to_consume, consumed, scope_type = self._to_consume[i] |
645 # if the current scope is a class scope but it's not the inner | 729 # if the current scope is a class scope but it's not the inner |
646 # scope, ignore it. This prevents to access this scope instead of | 730 # scope, ignore it. This prevents to access this scope instead of |
647 # the globals one in function members when there are some common | 731 # the globals one in function members when there are some common |
648 # names. The only exception is when the starting scope is a | 732 # names. The only exception is when the starting scope is a |
649 # comprehension and its direct outer scope is a class | 733 # comprehension and its direct outer scope is a class |
650 if scope_type == 'class' and i != start_index and not ( | 734 if scope_type == 'class' and i != start_index and not ( |
651 base_scope_type == 'comprehension' and i == start_index-1): | 735 base_scope_type == 'comprehension' and i == start_index-1): |
652 # Detect if we are in a local class scope, as an assignment. | 736 # Detect if we are in a local class scope, as an assignment. |
653 # For example, the following is fair game. | 737 # For example, the following is fair game. |
| 738 # |
654 # class A: | 739 # class A: |
655 # b = 1 | 740 # b = 1 |
656 # c = lambda b=b: b * b | 741 # c = lambda b=b: b * b |
657 class_assignment = (isinstance(frame, astroid.Class) and | 742 # |
658 name in frame.locals) | 743 # class B: |
659 if not class_assignment: | 744 # tp = 1 |
| 745 # def func(self, arg: tp): |
| 746 # ... |
| 747 |
| 748 in_annotation = ( |
| 749 PY3K and isinstance(frame, astroid.Function) |
| 750 and node.statement() is frame and |
| 751 (node in frame.args.annotations |
| 752 or node is frame.args.varargannotation |
| 753 or node is frame.args.kwargannotation)) |
| 754 if in_annotation: |
| 755 frame_locals = frame.parent.scope().locals |
| 756 else: |
| 757 frame_locals = frame.locals |
| 758 if not ((isinstance(frame, astroid.Class) or in_annotation) |
| 759 and name in frame_locals): |
660 continue | 760 continue |
661 # the name has already been consumed, only check it's not a loop | 761 # the name has already been consumed, only check it's not a loop |
662 # variable used outside the loop | 762 # variable used outside the loop |
663 if name in consumed: | 763 if name in consumed: |
664 defnode = assign_parent(consumed[name][0]) | 764 defnode = assign_parent(consumed[name][0]) |
665 self._check_late_binding_closure(node, defnode, scope_type) | 765 self._check_late_binding_closure(node, defnode) |
666 self._loopvar_name(node, name) | 766 self._loopvar_name(node, name) |
667 break | 767 break |
668 # mark the name as consumed if it's defined in this scope | 768 # mark the name as consumed if it's defined in this scope |
669 # (i.e. no KeyError is raised by "to_consume[name]") | 769 # (i.e. no KeyError is raised by "to_consume[name]") |
670 try: | 770 try: |
671 consumed[name] = to_consume[name] | 771 consumed[name] = to_consume[name] |
672 except KeyError: | 772 except KeyError: |
673 continue | 773 continue |
674 # checks for use before assignment | 774 # checks for use before assignment |
675 defnode = assign_parent(to_consume[name][0]) | 775 defnode = assign_parent(to_consume[name][0]) |
676 if defnode is not None: | 776 if defnode is not None: |
677 self._check_late_binding_closure(node, defnode, scope_type) | 777 self._check_late_binding_closure(node, defnode) |
678 defstmt = defnode.statement() | 778 defstmt = defnode.statement() |
679 defframe = defstmt.frame() | 779 defframe = defstmt.frame() |
680 maybee0601 = True | 780 maybee0601 = True |
681 if not frame is defframe: | 781 if not frame is defframe: |
682 maybee0601 = _detect_global_scope(node, frame, defframe) | 782 maybee0601 = _detect_global_scope(node, frame, defframe) |
683 elif defframe.parent is None: | 783 elif defframe.parent is None: |
684 # we are at the module level, check the name is not | 784 # we are at the module level, check the name is not |
685 # defined in builtins | 785 # defined in builtins |
686 if name in defframe.scope_attrs or builtin_lookup(name)[1]: | 786 if name in defframe.scope_attrs or builtin_lookup(name)[1]: |
687 maybee0601 = False | 787 maybee0601 = False |
688 else: | 788 else: |
689 # we are in a local scope, check the name is not | 789 # we are in a local scope, check the name is not |
690 # defined in global or builtin scope | 790 # defined in global or builtin scope |
691 if defframe.root().lookup(name)[1]: | 791 if defframe.root().lookup(name)[1]: |
692 maybee0601 = False | 792 maybee0601 = False |
693 else: | 793 else: |
694 # check if we have a nonlocal | 794 # check if we have a nonlocal |
695 if name in defframe.locals: | 795 if name in defframe.locals: |
696 maybee0601 = not any(isinstance(child, astroid.Nonlo
cal) | 796 maybee0601 = not any(isinstance(child, astroid.Nonlo
cal) |
697 and name in child.names | 797 and name in child.names |
698 for child in defframe.get_child
ren()) | 798 for child in defframe.get_child
ren()) |
| 799 |
| 800 # Handle a couple of class scoping issues. |
| 801 annotation_return = False |
| 802 # The class reuses itself in the class scope. |
| 803 recursive_klass = (frame is defframe and |
| 804 defframe.parent_of(node) and |
| 805 isinstance(defframe, astroid.Class) and |
| 806 node.name == defframe.name) |
699 if (self._to_consume[-1][-1] == 'lambda' and | 807 if (self._to_consume[-1][-1] == 'lambda' and |
700 isinstance(frame, astroid.Class) | 808 isinstance(frame, astroid.Class) |
701 and name in frame.locals): | 809 and name in frame.locals): |
702 maybee0601 = True | 810 maybee0601 = True |
| 811 elif (isinstance(defframe, astroid.Class) and |
| 812 isinstance(frame, astroid.Function)): |
| 813 # Special rule for function return annotations, |
| 814 # which uses the same name as the class where |
| 815 # the function lives. |
| 816 if (PY3K and node is frame.returns and |
| 817 defframe.parent_of(frame.returns)): |
| 818 maybee0601 = annotation_return = True |
| 819 |
| 820 if (maybee0601 and defframe.name in defframe.locals and |
| 821 defframe.locals[name][0].lineno < frame.lineno): |
| 822 # Detect class assignments with the same |
| 823 # name as the class. In this case, no warning |
| 824 # should be raised. |
| 825 maybee0601 = False |
| 826 elif recursive_klass: |
| 827 maybee0601 = True |
703 else: | 828 else: |
704 maybee0601 = maybee0601 and stmt.fromlineno <= defstmt.froml
ineno | 829 maybee0601 = maybee0601 and stmt.fromlineno <= defstmt.froml
ineno |
705 | 830 |
706 if (maybee0601 | 831 if (maybee0601 |
707 and not is_defined_before(node) | 832 and not is_defined_before(node) |
708 and not are_exclusive(stmt, defstmt, ('NameError', | 833 and not are_exclusive(stmt, defstmt, ('NameError', |
709 'Exception', | 834 'Exception', |
710 'BaseException')))
: | 835 'BaseException')))
: |
711 if defstmt is stmt and isinstance(node, (astroid.DelName, | 836 if recursive_klass or (defstmt is stmt and |
712 astroid.AssName)): | 837 isinstance(node, (astroid.DelName, |
| 838 astroid.AssName))): |
| 839 self.add_message('undefined-variable', args=name, node=n
ode) |
| 840 elif annotation_return: |
713 self.add_message('undefined-variable', args=name, node=n
ode) | 841 self.add_message('undefined-variable', args=name, node=n
ode) |
714 elif self._to_consume[-1][-1] != 'lambda': | 842 elif self._to_consume[-1][-1] != 'lambda': |
715 # E0601 may *not* occurs in lambda scope. | 843 # E0601 may *not* occurs in lambda scope. |
716 self.add_message('used-before-assignment', args=name, no
de=node) | 844 self.add_message('used-before-assignment', args=name, no
de=node) |
717 elif self._to_consume[-1][-1] == 'lambda': | 845 elif self._to_consume[-1][-1] == 'lambda': |
718 # E0601 can occur in class-level scope in lambdas, as in | 846 # E0601 can occur in class-level scope in lambdas, as in |
719 # the following example: | 847 # the following example: |
720 # class A: | 848 # class A: |
721 # x = lambda attr: f + attr | 849 # x = lambda attr: f + attr |
722 # f = 42 | 850 # f = 42 |
(...skipping 23 matching lines...) Expand all Loading... |
746 if not (name in astroid.Module.scope_attrs or is_builtin(name) | 874 if not (name in astroid.Module.scope_attrs or is_builtin(name) |
747 or name in self.config.additional_builtins): | 875 or name in self.config.additional_builtins): |
748 self.add_message('undefined-variable', args=name, node=node) | 876 self.add_message('undefined-variable', args=name, node=node) |
749 | 877 |
750 @check_messages('no-name-in-module') | 878 @check_messages('no-name-in-module') |
751 def visit_import(self, node): | 879 def visit_import(self, node): |
752 """check modules attribute accesses""" | 880 """check modules attribute accesses""" |
753 for name, _ in node.names: | 881 for name, _ in node.names: |
754 parts = name.split('.') | 882 parts = name.split('.') |
755 try: | 883 try: |
756 module = node.infer_name_module(parts[0]).next() | 884 module = next(node.infer_name_module(parts[0])) |
757 except astroid.ResolveError: | 885 except astroid.ResolveError: |
758 continue | 886 continue |
759 self._check_module_attrs(node, module, parts[1:]) | 887 self._check_module_attrs(node, module, parts[1:]) |
760 | 888 |
761 @check_messages('no-name-in-module') | 889 @check_messages('no-name-in-module') |
762 def visit_from(self, node): | 890 def visit_from(self, node): |
763 """check modules attribute accesses""" | 891 """check modules attribute accesses""" |
764 name_parts = node.modname.split('.') | 892 name_parts = node.modname.split('.') |
765 level = getattr(node, 'level', None) | 893 level = getattr(node, 'level', None) |
766 try: | 894 try: |
767 module = node.root().import_module(name_parts[0], level=level) | 895 module = node.root().import_module(name_parts[0], level=level) |
768 except AstroidBuildingException: | 896 except Exception: # pylint: disable=broad-except |
769 return | |
770 except Exception, exc: | |
771 print 'Unhandled exception in VariablesChecker:', exc | |
772 return | 897 return |
773 module = self._check_module_attrs(node, module, name_parts[1:]) | 898 module = self._check_module_attrs(node, module, name_parts[1:]) |
774 if not module: | 899 if not module: |
775 return | 900 return |
776 for name, _ in node.names: | 901 for name, _ in node.names: |
777 if name == '*': | 902 if name == '*': |
778 continue | 903 continue |
779 self._check_module_attrs(node, module, name.split('.')) | 904 self._check_module_attrs(node, module, name.split('.')) |
780 | 905 |
781 @check_messages('unbalanced-tuple-unpacking', 'unpacking-non-sequence') | 906 @check_messages('unbalanced-tuple-unpacking', 'unpacking-non-sequence') |
(...skipping 10 matching lines...) Expand all Loading... |
792 self._check_unpacking(infered, node, targets) | 917 self._check_unpacking(infered, node, targets) |
793 except astroid.InferenceError: | 918 except astroid.InferenceError: |
794 return | 919 return |
795 | 920 |
796 def _check_unpacking(self, infered, node, targets): | 921 def _check_unpacking(self, infered, node, targets): |
797 """ Check for unbalanced tuple unpacking | 922 """ Check for unbalanced tuple unpacking |
798 and unpacking non sequences. | 923 and unpacking non sequences. |
799 """ | 924 """ |
800 if infered is astroid.YES: | 925 if infered is astroid.YES: |
801 return | 926 return |
| 927 if (isinstance(infered.parent, astroid.Arguments) and |
| 928 isinstance(node.value, astroid.Name) and |
| 929 node.value.name == infered.parent.vararg): |
| 930 # Variable-length argument, we can't determine the length. |
| 931 return |
802 if isinstance(infered, (astroid.Tuple, astroid.List)): | 932 if isinstance(infered, (astroid.Tuple, astroid.List)): |
803 # attempt to check unpacking is properly balanced | 933 # attempt to check unpacking is properly balanced |
804 values = infered.itered() | 934 values = infered.itered() |
805 if len(targets) != len(values): | 935 if len(targets) != len(values): |
806 # Check if we have starred nodes. | 936 # Check if we have starred nodes. |
807 if any(isinstance(target, astroid.Starred) | 937 if any(isinstance(target, astroid.Starred) |
808 for target in targets): | 938 for target in targets): |
809 return | 939 return |
810 self.add_message('unbalanced-tuple-unpacking', node=node, | 940 self.add_message('unbalanced-tuple-unpacking', node=node, |
811 args=(_get_unpacking_extra_info(node, infered), | 941 args=(_get_unpacking_extra_info(node, infered), |
(...skipping 22 matching lines...) Expand all Loading... |
834 """ | 964 """ |
835 assert isinstance(module, astroid.Module), module | 965 assert isinstance(module, astroid.Module), module |
836 ignored_modules = get_global_option(self, 'ignored-modules', | 966 ignored_modules = get_global_option(self, 'ignored-modules', |
837 default=[]) | 967 default=[]) |
838 while module_names: | 968 while module_names: |
839 name = module_names.pop(0) | 969 name = module_names.pop(0) |
840 if name == '__dict__': | 970 if name == '__dict__': |
841 module = None | 971 module = None |
842 break | 972 break |
843 try: | 973 try: |
844 module = module.getattr(name)[0].infer().next() | 974 module = next(module.getattr(name)[0].infer()) |
845 if module is astroid.YES: | 975 if module is astroid.YES: |
846 return None | 976 return None |
847 except astroid.NotFoundError: | 977 except astroid.NotFoundError: |
848 if module.name in ignored_modules: | 978 if module.name in ignored_modules: |
849 return None | 979 return None |
850 self.add_message('no-name-in-module', | 980 self.add_message('no-name-in-module', |
851 args=(name, module.name), node=node) | 981 args=(name, module.name), node=node) |
852 return None | 982 return None |
853 except astroid.InferenceError: | 983 except astroid.InferenceError: |
854 return None | 984 return None |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
930 module_locals.pop(name, None) | 1060 module_locals.pop(name, None) |
931 super(VariablesChecker3k, self).leave_module(node) | 1061 super(VariablesChecker3k, self).leave_module(node) |
932 | 1062 |
933 if sys.version_info >= (3, 0): | 1063 if sys.version_info >= (3, 0): |
934 VariablesChecker = VariablesChecker3k | 1064 VariablesChecker = VariablesChecker3k |
935 | 1065 |
936 | 1066 |
937 def register(linter): | 1067 def register(linter): |
938 """required method to auto register this checker""" | 1068 """required method to auto register this checker""" |
939 linter.register_checker(VariablesChecker(linter)) | 1069 linter.register_checker(VariablesChecker(linter)) |
OLD | NEW |