gvsig-scripting / org.gvsig.scripting / trunk / org.gvsig.scripting / org.gvsig.scripting.app / org.gvsig.scripting.app.mainplugin / src / main / resources-plugin / scripting / lib / pylint / checkers / variables.py @ 745
History | View | Annotate | Download (50.5 KB)
1 |
# Copyright (c) 2003-2014 LOGILAB S.A. (Paris, FRANCE).
|
---|---|
2 |
# http://www.logilab.fr/ -- mailto:contact@logilab.fr
|
3 |
#
|
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
|
6 |
# Foundation; either version 2 of the License, or (at your option) any later
|
7 |
# version.
|
8 |
#
|
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
|
11 |
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
12 |
#
|
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.,
|
15 |
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
16 |
"""variables checkers for Python code
|
17 |
"""
|
18 |
import os |
19 |
import sys |
20 |
import re |
21 |
from copy import copy |
22 |
|
23 |
import six |
24 |
|
25 |
import astroid |
26 |
from astroid import modutils |
27 |
from pylint.interfaces import IAstroidChecker, INFERENCE, INFERENCE_FAILURE, HIGH |
28 |
from pylint.utils import get_global_option |
29 |
from pylint.checkers import BaseChecker |
30 |
from pylint.checkers.utils import ( |
31 |
PYMETHODS, is_ancestor_name, is_builtin, |
32 |
is_defined_before, is_error, is_func_default, is_func_decorator, |
33 |
assign_parent, check_messages, is_inside_except, clobber_in_except, |
34 |
get_all_elements, has_known_bases, node_ignores_exception, |
35 |
is_inside_abstract_class, is_comprehension, is_iterable, |
36 |
safe_infer) |
37 |
|
38 |
SPECIAL_OBJ = re.compile("^_{2}[a-z]+_{2}$")
|
39 |
FUTURE = '__future__'
|
40 |
PY3K = sys.version_info >= (3, 0) |
41 |
|
42 |
|
43 |
def _is_from_future_import(stmt, name): |
44 |
"""Check if the name is a future import from another module."""
|
45 |
try:
|
46 |
module = stmt.do_import_module(stmt.modname) |
47 |
except astroid.InferenceError:
|
48 |
return
|
49 |
|
50 |
for local_node in module.locals.get(name, []): |
51 |
if (isinstance(local_node, astroid.ImportFrom) |
52 |
and local_node.modname == FUTURE):
|
53 |
return True |
54 |
|
55 |
|
56 |
def in_for_else_branch(parent, stmt): |
57 |
"""Returns True if stmt in inside the else branch for a parent For stmt."""
|
58 |
return (isinstance(parent, astroid.For) and |
59 |
any(else_stmt.parent_of(stmt) for else_stmt in parent.orelse)) |
60 |
|
61 |
def overridden_method(klass, name): |
62 |
"""get overridden method if any"""
|
63 |
try:
|
64 |
parent = next(klass.local_attr_ancestors(name))
|
65 |
except (StopIteration, KeyError): |
66 |
return None |
67 |
try:
|
68 |
meth_node = parent[name] |
69 |
except KeyError: |
70 |
# We have found an ancestor defining <name> but it's not in the local
|
71 |
# dictionary. This may happen with astroid built from living objects.
|
72 |
return None |
73 |
if isinstance(meth_node, astroid.FunctionDef): |
74 |
return meth_node
|
75 |
return None |
76 |
|
77 |
def _get_unpacking_extra_info(node, infered): |
78 |
"""return extra information to add to the message for unpacking-non-sequence
|
79 |
and unbalanced-tuple-unpacking errors
|
80 |
"""
|
81 |
more = ''
|
82 |
infered_module = infered.root().name |
83 |
if node.root().name == infered_module:
|
84 |
if node.lineno == infered.lineno:
|
85 |
more = ' %s' % infered.as_string()
|
86 |
elif infered.lineno:
|
87 |
more = ' defined at line %s' % infered.lineno
|
88 |
elif infered.lineno:
|
89 |
more = ' defined at line %s of %s' % (infered.lineno, infered_module)
|
90 |
return more
|
91 |
|
92 |
def _detect_global_scope(node, frame, defframe): |
93 |
""" Detect that the given frames shares a global
|
94 |
scope.
|
95 |
|
96 |
Two frames shares a global scope when neither
|
97 |
of them are hidden under a function scope, as well
|
98 |
as any of parent scope of them, until the root scope.
|
99 |
In this case, depending from something defined later on
|
100 |
will not work, because it is still undefined.
|
101 |
|
102 |
Example:
|
103 |
class A:
|
104 |
# B has the same global scope as `C`, leading to a NameError.
|
105 |
class B(C): ...
|
106 |
class C: ...
|
107 |
|
108 |
"""
|
109 |
def_scope = scope = None
|
110 |
if frame and frame.parent: |
111 |
scope = frame.parent.scope() |
112 |
if defframe and defframe.parent: |
113 |
def_scope = defframe.parent.scope() |
114 |
if isinstance(frame, astroid.FunctionDef): |
115 |
# If the parent of the current node is a
|
116 |
# function, then it can be under its scope
|
117 |
# (defined in, which doesn't concern us) or
|
118 |
# the `->` part of annotations. The same goes
|
119 |
# for annotations of function arguments, they'll have
|
120 |
# their parent the Arguments node.
|
121 |
if not isinstance(node.parent, |
122 |
(astroid.FunctionDef, astroid.Arguments)): |
123 |
return False |
124 |
elif any(not isinstance(f, (astroid.ClassDef, astroid.Module)) |
125 |
for f in (frame, defframe)): |
126 |
# Not interested in other frames, since they are already
|
127 |
# not in a global scope.
|
128 |
return False |
129 |
|
130 |
break_scopes = [] |
131 |
for s in (scope, def_scope): |
132 |
# Look for parent scopes. If there is anything different
|
133 |
# than a module or a class scope, then they frames don't
|
134 |
# share a global scope.
|
135 |
parent_scope = s |
136 |
while parent_scope:
|
137 |
if not isinstance(parent_scope, (astroid.ClassDef, astroid.Module)): |
138 |
break_scopes.append(parent_scope) |
139 |
break
|
140 |
if parent_scope.parent:
|
141 |
parent_scope = parent_scope.parent.scope() |
142 |
else:
|
143 |
break
|
144 |
if break_scopes and len(set(break_scopes)) != 1: |
145 |
# Store different scopes than expected.
|
146 |
# If the stored scopes are, in fact, the very same, then it means
|
147 |
# that the two frames (frame and defframe) shares the same scope,
|
148 |
# and we could apply our lineno analysis over them.
|
149 |
# For instance, this works when they are inside a function, the node
|
150 |
# that uses a definition and the definition itself.
|
151 |
return False |
152 |
# At this point, we are certain that frame and defframe shares a scope
|
153 |
# and the definition of the first depends on the second.
|
154 |
return frame.lineno < defframe.lineno
|
155 |
|
156 |
def _fix_dot_imports(not_consumed): |
157 |
""" Try to fix imports with multiple dots, by returning a dictionary
|
158 |
with the import names expanded. The function unflattens root imports,
|
159 |
like 'xml' (when we have both 'xml.etree' and 'xml.sax'), to 'xml.etree'
|
160 |
and 'xml.sax' respectively.
|
161 |
"""
|
162 |
# TODO: this should be improved in issue astroid #46
|
163 |
names = {} |
164 |
for name, stmts in six.iteritems(not_consumed): |
165 |
if any(isinstance(stmt, astroid.AssignName) |
166 |
and isinstance(stmt.assign_type(), astroid.AugAssign) |
167 |
for stmt in stmts): |
168 |
continue
|
169 |
for stmt in stmts: |
170 |
if not isinstance(stmt, (astroid.ImportFrom, astroid.Import)): |
171 |
continue
|
172 |
for imports in stmt.names: |
173 |
second_name = None
|
174 |
if imports[0] == "*": |
175 |
# In case of wildcard imports,
|
176 |
# pick the name from inside the imported module.
|
177 |
second_name = name |
178 |
else:
|
179 |
if imports[0].find(".") > -1 or name in imports: |
180 |
# Most likely something like 'xml.etree',
|
181 |
# which will appear in the .locals as 'xml'.
|
182 |
# Only pick the name if it wasn't consumed.
|
183 |
second_name = imports[0]
|
184 |
if second_name and second_name not in names: |
185 |
names[second_name] = stmt |
186 |
return sorted(names.items(), key=lambda a: a[1].fromlineno) |
187 |
|
188 |
def _find_frame_imports(name, frame): |
189 |
"""
|
190 |
Detect imports in the frame, with the required
|
191 |
*name*. Such imports can be considered assignments.
|
192 |
Returns True if an import for the given name was found.
|
193 |
"""
|
194 |
imports = frame.nodes_of_class((astroid.Import, astroid.ImportFrom)) |
195 |
for import_node in imports: |
196 |
for import_name, import_alias in import_node.names: |
197 |
# If the import uses an alias, check only that.
|
198 |
# Otherwise, check only the import name.
|
199 |
if import_alias:
|
200 |
if import_alias == name:
|
201 |
return True |
202 |
elif import_name and import_name == name: |
203 |
return True |
204 |
|
205 |
|
206 |
MSGS = { |
207 |
'E0601': ('Using variable %r before assignment', |
208 |
'used-before-assignment',
|
209 |
'Used when a local variable is accessed before it\'s \
|
210 |
assignment.'),
|
211 |
'E0602': ('Undefined variable %r', |
212 |
'undefined-variable',
|
213 |
'Used when an undefined variable is accessed.'),
|
214 |
'E0603': ('Undefined variable name %r in __all__', |
215 |
'undefined-all-variable',
|
216 |
'Used when an undefined variable name is referenced in __all__.'),
|
217 |
'E0604': ('Invalid object %r in __all__, must contain only strings', |
218 |
'invalid-all-object',
|
219 |
'Used when an invalid (non-string) object occurs in __all__.'),
|
220 |
'E0611': ('No name %r in module %r', |
221 |
'no-name-in-module',
|
222 |
'Used when a name cannot be found in a module.'),
|
223 |
|
224 |
'W0601': ('Global variable %r undefined at the module level', |
225 |
'global-variable-undefined',
|
226 |
'Used when a variable is defined through the "global" statement \
|
227 |
but the variable is not defined in the module scope.'),
|
228 |
'W0602': ('Using global for %r but no assignment is done', |
229 |
'global-variable-not-assigned',
|
230 |
'Used when a variable is defined through the "global" statement \
|
231 |
but no assignment to this variable is done.'),
|
232 |
'W0603': ('Using the global statement', # W0121 |
233 |
'global-statement',
|
234 |
'Used when you use the "global" statement to update a global \
|
235 |
variable. Pylint just try to discourage this \
|
236 |
usage. That doesn\'t mean you can not use it !'),
|
237 |
'W0604': ('Using the global statement at the module level', # W0103 |
238 |
'global-at-module-level',
|
239 |
'Used when you use the "global" statement at the module level \
|
240 |
since it has no effect'),
|
241 |
'W0611': ('Unused %s', |
242 |
'unused-import',
|
243 |
'Used when an imported module or variable is not used.'),
|
244 |
'W0612': ('Unused variable %r', |
245 |
'unused-variable',
|
246 |
'Used when a variable is defined but not used.'),
|
247 |
'W0613': ('Unused argument %r', |
248 |
'unused-argument',
|
249 |
'Used when a function or method argument is not used.'),
|
250 |
'W0614': ('Unused import %s from wildcard import', |
251 |
'unused-wildcard-import',
|
252 |
'Used when an imported module or variable is not used from a \
|
253 |
`\'from X import *\'` style import.'),
|
254 |
|
255 |
'W0621': ('Redefining name %r from outer scope (line %s)', |
256 |
'redefined-outer-name',
|
257 |
'Used when a variable\'s name hide a name defined in the outer \
|
258 |
scope.'),
|
259 |
'W0622': ('Redefining built-in %r', |
260 |
'redefined-builtin',
|
261 |
'Used when a variable or function override a built-in.'),
|
262 |
'W0623': ('Redefining name %r from %s in exception handler', |
263 |
'redefine-in-handler',
|
264 |
'Used when an exception handler assigns the exception \
|
265 |
to an existing name'),
|
266 |
|
267 |
'W0631': ('Using possibly undefined loop variable %r', |
268 |
'undefined-loop-variable',
|
269 |
'Used when an loop variable (i.e. defined by a for loop or \
|
270 |
a list comprehension or a generator expression) is used outside \
|
271 |
the loop.'),
|
272 |
|
273 |
'E0632': ('Possible unbalanced tuple unpacking with ' |
274 |
'sequence%s: '
|
275 |
'left side has %d label(s), right side has %d value(s)',
|
276 |
'unbalanced-tuple-unpacking',
|
277 |
'Used when there is an unbalanced tuple unpacking in assignment',
|
278 |
{'old_names': [('W0632', 'unbalanced-tuple-unpacking')]}), |
279 |
|
280 |
'E0633': ('Attempting to unpack a non-sequence%s', |
281 |
'unpacking-non-sequence',
|
282 |
'Used when something which is not '
|
283 |
'a sequence is used in an unpack assignment',
|
284 |
{'old_names': [('W0633', 'unpacking-non-sequence')]}), |
285 |
|
286 |
'W0640': ('Cell variable %s defined in loop', |
287 |
'cell-var-from-loop',
|
288 |
'A variable used in a closure is defined in a loop. '
|
289 |
'This will result in all closures using the same value for '
|
290 |
'the closed-over variable.'),
|
291 |
|
292 |
} |
293 |
|
294 |
class VariablesChecker(BaseChecker): |
295 |
"""checks for
|
296 |
* unused variables / imports
|
297 |
* undefined variables
|
298 |
* redefinition of variable from builtins or from an outer scope
|
299 |
* use of variable before assignment
|
300 |
* __all__ consistency
|
301 |
"""
|
302 |
|
303 |
__implements__ = IAstroidChecker |
304 |
|
305 |
name = 'variables'
|
306 |
msgs = MSGS |
307 |
priority = -1
|
308 |
options = (("init-import",
|
309 |
{'default': 0, 'type' : 'yn', 'metavar' : '<y_or_n>', |
310 |
'help' : 'Tells whether we should check for unused import in \ |
311 |
__init__ files.'}),
|
312 |
("dummy-variables-rgx",
|
313 |
{'default': ('_$|dummy'), |
314 |
'type' :'regexp', 'metavar' : '<regexp>', |
315 |
'help' : 'A regular expression matching the name of dummy \ |
316 |
variables (i.e. expectedly not used).'}),
|
317 |
("additional-builtins",
|
318 |
{'default': (), 'type' : 'csv', |
319 |
'metavar' : '<comma separated list>', |
320 |
'help' : 'List of additional names supposed to be defined in \ |
321 |
builtins. Remember that you should avoid to define new builtins when possible.'
|
322 |
}), |
323 |
("callbacks",
|
324 |
{'default' : ('cb_', '_cb'), 'type' : 'csv', |
325 |
'metavar' : '<callbacks>', |
326 |
'help' : 'List of strings which can identify a callback ' |
327 |
'function by name. A callback name must start or '
|
328 |
'end with one of those strings.'}
|
329 |
) |
330 |
) |
331 |
def __init__(self, linter=None): |
332 |
BaseChecker.__init__(self, linter)
|
333 |
self._to_consume = None # list of tuples: (to_consume:dict, consumed:dict, scope_type:str) |
334 |
self._checking_mod_attr = None |
335 |
|
336 |
def visit_module(self, node): |
337 |
"""visit module : update consumption analysis variable
|
338 |
checks globals doesn't overrides builtins
|
339 |
"""
|
340 |
self._to_consume = [(copy(node.locals), {}, 'module')] |
341 |
for name, stmts in six.iteritems(node.locals): |
342 |
if is_builtin(name) and not is_inside_except(stmts[0]): |
343 |
# do not print Redefining builtin for additional builtins
|
344 |
self.add_message('redefined-builtin', args=name, node=stmts[0]) |
345 |
|
346 |
@check_messages('unused-import', 'unused-wildcard-import', |
347 |
'redefined-builtin', 'undefined-all-variable', |
348 |
'invalid-all-object')
|
349 |
def leave_module(self, node): |
350 |
"""leave module: check globals
|
351 |
"""
|
352 |
assert len(self._to_consume) == 1 |
353 |
not_consumed = self._to_consume.pop()[0] |
354 |
# attempt to check for __all__ if defined
|
355 |
if '__all__' in node.locals: |
356 |
self._check_all(node, not_consumed)
|
357 |
# don't check unused imports in __init__ files
|
358 |
if not self.config.init_import and node.package: |
359 |
return
|
360 |
|
361 |
self._check_imports(not_consumed)
|
362 |
|
363 |
def _check_all(self, node, not_consumed): |
364 |
assigned = next(node.igetattr('__all__')) |
365 |
if assigned is astroid.YES: |
366 |
return
|
367 |
|
368 |
for elt in getattr(assigned, 'elts', ()): |
369 |
try:
|
370 |
elt_name = next(elt.infer())
|
371 |
except astroid.InferenceError:
|
372 |
continue
|
373 |
if elt_name is astroid.YES: |
374 |
continue
|
375 |
|
376 |
if (not isinstance(elt_name, astroid.Const) |
377 |
or not isinstance(elt_name.value, six.string_types)): |
378 |
self.add_message('invalid-all-object', |
379 |
args=elt.as_string(), node=elt) |
380 |
continue
|
381 |
|
382 |
elt_name = elt_name.value |
383 |
# If elt is in not_consumed, remove it from not_consumed
|
384 |
if elt_name in not_consumed: |
385 |
del not_consumed[elt_name]
|
386 |
continue
|
387 |
|
388 |
if elt_name not in node.locals: |
389 |
if not node.package: |
390 |
self.add_message('undefined-all-variable', |
391 |
args=(elt_name, ), |
392 |
node=elt) |
393 |
else:
|
394 |
basename = os.path.splitext(node.file)[0]
|
395 |
if os.path.basename(basename) == '__init__': |
396 |
name = node.name + "." + elt_name
|
397 |
try:
|
398 |
modutils.file_from_modpath(name.split("."))
|
399 |
except ImportError: |
400 |
self.add_message('undefined-all-variable', |
401 |
args=(elt_name, ), |
402 |
node=elt) |
403 |
except SyntaxError: |
404 |
# don't yield an syntax-error warning,
|
405 |
# because it will be later yielded
|
406 |
# when the file will be checked
|
407 |
pass
|
408 |
|
409 |
def _check_imports(self, not_consumed): |
410 |
local_names = _fix_dot_imports(not_consumed) |
411 |
checked = set()
|
412 |
for name, stmt in local_names: |
413 |
for imports in stmt.names: |
414 |
real_name = imported_name = imports[0]
|
415 |
if imported_name == "*": |
416 |
real_name = name |
417 |
as_name = imports[1]
|
418 |
if real_name in checked: |
419 |
continue
|
420 |
if name not in (real_name, as_name): |
421 |
continue
|
422 |
checked.add(real_name) |
423 |
|
424 |
if (isinstance(stmt, astroid.Import) or |
425 |
(isinstance(stmt, astroid.ImportFrom) and |
426 |
not stmt.modname)):
|
427 |
if (isinstance(stmt, astroid.ImportFrom) and |
428 |
SPECIAL_OBJ.search(imported_name)): |
429 |
# Filter special objects (__doc__, __all__) etc.,
|
430 |
# because they can be imported for exporting.
|
431 |
continue
|
432 |
if as_name is None: |
433 |
msg = "import %s" % imported_name
|
434 |
else:
|
435 |
msg = "%s imported as %s" % (imported_name, as_name)
|
436 |
self.add_message('unused-import', args=msg, node=stmt) |
437 |
elif (isinstance(stmt, astroid.ImportFrom) |
438 |
and stmt.modname != FUTURE):
|
439 |
|
440 |
if SPECIAL_OBJ.search(imported_name):
|
441 |
# Filter special objects (__doc__, __all__) etc.,
|
442 |
# because they can be imported for exporting.
|
443 |
continue
|
444 |
|
445 |
if _is_from_future_import(stmt, name):
|
446 |
# Check if the name is in fact loaded from a
|
447 |
# __future__ import in another module.
|
448 |
continue
|
449 |
|
450 |
if imported_name == '*': |
451 |
self.add_message('unused-wildcard-import', |
452 |
args=name, node=stmt) |
453 |
else:
|
454 |
if as_name is None: |
455 |
msg = "%s imported from %s" % (imported_name, stmt.modname)
|
456 |
else:
|
457 |
fields = (imported_name, stmt.modname, as_name) |
458 |
msg = "%s imported from %s as %s" % fields
|
459 |
self.add_message('unused-import', args=msg, node=stmt) |
460 |
del self._to_consume |
461 |
|
462 |
def visit_classdef(self, node): |
463 |
"""visit class: update consumption analysis variable
|
464 |
"""
|
465 |
self._to_consume.append((copy(node.locals), {}, 'class')) |
466 |
|
467 |
def leave_classdef(self, _): |
468 |
"""leave class: update consumption analysis variable
|
469 |
"""
|
470 |
# do not check for not used locals here (no sense)
|
471 |
self._to_consume.pop()
|
472 |
|
473 |
def visit_lambda(self, node): |
474 |
"""visit lambda: update consumption analysis variable
|
475 |
"""
|
476 |
self._to_consume.append((copy(node.locals), {}, 'lambda')) |
477 |
|
478 |
def leave_lambda(self, _): |
479 |
"""leave lambda: update consumption analysis variable
|
480 |
"""
|
481 |
# do not check for not used locals here
|
482 |
self._to_consume.pop()
|
483 |
|
484 |
def visit_generatorexp(self, node): |
485 |
"""visit genexpr: update consumption analysis variable
|
486 |
"""
|
487 |
self._to_consume.append((copy(node.locals), {}, 'comprehension')) |
488 |
|
489 |
def leave_generatorexp(self, _): |
490 |
"""leave genexpr: update consumption analysis variable
|
491 |
"""
|
492 |
# do not check for not used locals here
|
493 |
self._to_consume.pop()
|
494 |
|
495 |
def visit_dictcomp(self, node): |
496 |
"""visit dictcomp: update consumption analysis variable
|
497 |
"""
|
498 |
self._to_consume.append((copy(node.locals), {}, 'comprehension')) |
499 |
|
500 |
def leave_dictcomp(self, _): |
501 |
"""leave dictcomp: update consumption analysis variable
|
502 |
"""
|
503 |
# do not check for not used locals here
|
504 |
self._to_consume.pop()
|
505 |
|
506 |
def visit_setcomp(self, node): |
507 |
"""visit setcomp: update consumption analysis variable
|
508 |
"""
|
509 |
self._to_consume.append((copy(node.locals), {}, 'comprehension')) |
510 |
|
511 |
def leave_setcomp(self, _): |
512 |
"""leave setcomp: update consumption analysis variable
|
513 |
"""
|
514 |
# do not check for not used locals here
|
515 |
self._to_consume.pop()
|
516 |
|
517 |
def visit_functiondef(self, node): |
518 |
"""visit function: update consumption analysis variable and check locals
|
519 |
"""
|
520 |
self._to_consume.append((copy(node.locals), {}, 'function')) |
521 |
if not (self.linter.is_message_enabled('redefined-outer-name') or |
522 |
self.linter.is_message_enabled('redefined-builtin')): |
523 |
return
|
524 |
globs = node.root().globals |
525 |
for name, stmt in node.items(): |
526 |
if is_inside_except(stmt):
|
527 |
continue
|
528 |
if name in globs and not isinstance(stmt, astroid.Global): |
529 |
definition = globs[name][0]
|
530 |
if (isinstance(definition, astroid.ImportFrom) |
531 |
and definition.modname == FUTURE):
|
532 |
# It is a __future__ directive, not a symbol.
|
533 |
continue
|
534 |
|
535 |
line = definition.fromlineno |
536 |
dummy_rgx = self.config.dummy_variables_rgx
|
537 |
if not dummy_rgx.match(name): |
538 |
self.add_message('redefined-outer-name', |
539 |
args=(name, line), node=stmt) |
540 |
elif is_builtin(name):
|
541 |
# do not print Redefining builtin for additional builtins
|
542 |
self.add_message('redefined-builtin', args=name, node=stmt) |
543 |
|
544 |
def leave_functiondef(self, node): |
545 |
"""leave function: check function's locals are consumed"""
|
546 |
not_consumed = self._to_consume.pop()[0] |
547 |
if not (self.linter.is_message_enabled('unused-variable') or |
548 |
self.linter.is_message_enabled('unused-argument')): |
549 |
return
|
550 |
# don't check arguments of function which are only raising an exception
|
551 |
if is_error(node):
|
552 |
return
|
553 |
# don't check arguments of abstract methods or within an interface
|
554 |
is_method = node.is_method() |
555 |
klass = node.parent.frame() |
556 |
if is_method and node.is_abstract(): |
557 |
return
|
558 |
if is_method and isinstance(klass, astroid.ClassDef): |
559 |
confidence = INFERENCE if has_known_bases(klass) else INFERENCE_FAILURE |
560 |
else:
|
561 |
confidence = HIGH |
562 |
authorized_rgx = self.config.dummy_variables_rgx
|
563 |
called_overridden = False
|
564 |
argnames = node.argnames() |
565 |
global_names = set()
|
566 |
nonlocal_names = set()
|
567 |
for global_stmt in node.nodes_of_class(astroid.Global): |
568 |
global_names.update(set(global_stmt.names))
|
569 |
for nonlocal_stmt in node.nodes_of_class(astroid.Nonlocal): |
570 |
nonlocal_names.update(set(nonlocal_stmt.names))
|
571 |
|
572 |
for name, stmts in six.iteritems(not_consumed): |
573 |
# ignore some special names specified by user configuration
|
574 |
if authorized_rgx.match(name):
|
575 |
continue
|
576 |
# ignore names imported by the global statement
|
577 |
# FIXME: should only ignore them if it's assigned latter
|
578 |
stmt = stmts[0]
|
579 |
if isinstance(stmt, astroid.Global): |
580 |
continue
|
581 |
if isinstance(stmt, (astroid.Import, astroid.ImportFrom)): |
582 |
# Detect imports, assigned to global statements.
|
583 |
if not global_names: |
584 |
continue
|
585 |
skip = False
|
586 |
for import_name, import_alias in stmt.names: |
587 |
# If the import uses an alias, check only that.
|
588 |
# Otherwise, check only the import name.
|
589 |
if import_alias:
|
590 |
if import_alias in global_names: |
591 |
skip = True
|
592 |
break
|
593 |
elif import_name in global_names: |
594 |
skip = True
|
595 |
break
|
596 |
if skip:
|
597 |
continue
|
598 |
|
599 |
# care about functions with unknown argument (builtins)
|
600 |
if name in argnames: |
601 |
if is_method:
|
602 |
# don't warn for the first argument of a (non static) method
|
603 |
if node.type != 'staticmethod' and name == argnames[0]: |
604 |
continue
|
605 |
# don't warn for argument of an overridden method
|
606 |
if not called_overridden: |
607 |
overridden = overridden_method(klass, node.name) |
608 |
called_overridden = True
|
609 |
if overridden is not None and name in overridden.argnames(): |
610 |
continue
|
611 |
if node.name in PYMETHODS and node.name not in ('__init__', '__new__'): |
612 |
continue
|
613 |
# don't check callback arguments
|
614 |
if any(node.name.startswith(cb) or node.name.endswith(cb) |
615 |
for cb in self.config.callbacks): |
616 |
continue
|
617 |
self.add_message('unused-argument', args=name, node=stmt, |
618 |
confidence=confidence) |
619 |
else:
|
620 |
if stmt.parent and isinstance(stmt.parent, astroid.Assign): |
621 |
if name in nonlocal_names: |
622 |
continue
|
623 |
self.add_message('unused-variable', args=name, node=stmt) |
624 |
|
625 |
visit_asyncfunctiondef = visit_functiondef |
626 |
leave_asyncfunctiondef = leave_functiondef |
627 |
|
628 |
@check_messages('global-variable-undefined', 'global-variable-not-assigned', |
629 |
'global-statement', 'global-at-module-level', |
630 |
'redefined-builtin')
|
631 |
def visit_global(self, node): |
632 |
"""check names imported exists in the global scope"""
|
633 |
frame = node.frame() |
634 |
if isinstance(frame, astroid.Module): |
635 |
self.add_message('global-at-module-level', node=node) |
636 |
return
|
637 |
module = frame.root() |
638 |
default_message = True
|
639 |
for name in node.names: |
640 |
try:
|
641 |
assign_nodes = module.getattr(name) |
642 |
except astroid.NotFoundError:
|
643 |
# unassigned global, skip
|
644 |
assign_nodes = [] |
645 |
for anode in assign_nodes: |
646 |
if anode.parent is None: |
647 |
# node returned for builtin attribute such as __file__,
|
648 |
# __doc__, etc...
|
649 |
continue
|
650 |
if anode.frame() is frame: |
651 |
# same scope level assignment
|
652 |
break
|
653 |
else:
|
654 |
if not _find_frame_imports(name, frame): |
655 |
self.add_message('global-variable-not-assigned', |
656 |
args=name, node=node) |
657 |
default_message = False
|
658 |
if not assign_nodes: |
659 |
continue
|
660 |
for anode in assign_nodes: |
661 |
if anode.parent is None: |
662 |
self.add_message('redefined-builtin', args=name, node=node) |
663 |
break
|
664 |
if anode.frame() is module: |
665 |
# module level assignment
|
666 |
break
|
667 |
else:
|
668 |
# global undefined at the module scope
|
669 |
self.add_message('global-variable-undefined', args=name, node=node) |
670 |
default_message = False
|
671 |
if default_message:
|
672 |
self.add_message('global-statement', node=node) |
673 |
|
674 |
def _check_late_binding_closure(self, node, assignment_node): |
675 |
def _is_direct_lambda_call(): |
676 |
return (isinstance(node_scope.parent, astroid.Call) |
677 |
and node_scope.parent.func is node_scope) |
678 |
|
679 |
node_scope = node.scope() |
680 |
if not isinstance(node_scope, (astroid.Lambda, astroid.FunctionDef)): |
681 |
return
|
682 |
if isinstance(node.parent, astroid.Arguments): |
683 |
return
|
684 |
|
685 |
if isinstance(assignment_node, astroid.Comprehension): |
686 |
if assignment_node.parent.parent_of(node.scope()):
|
687 |
self.add_message('cell-var-from-loop', node=node, args=node.name) |
688 |
else:
|
689 |
assign_scope = assignment_node.scope() |
690 |
maybe_for = assignment_node |
691 |
while not isinstance(maybe_for, astroid.For): |
692 |
if maybe_for is assign_scope: |
693 |
break
|
694 |
maybe_for = maybe_for.parent |
695 |
else:
|
696 |
if (maybe_for.parent_of(node_scope)
|
697 |
and not _is_direct_lambda_call() |
698 |
and not isinstance(node_scope.statement(), astroid.Return)): |
699 |
self.add_message('cell-var-from-loop', node=node, args=node.name) |
700 |
|
701 |
def _loopvar_name(self, node, name): |
702 |
# filter variables according to node's scope
|
703 |
# XXX used to filter parents but don't remember why, and removing this
|
704 |
# fixes a W0631 false positive reported by Paul Hachmann on 2008/12 on
|
705 |
# python-projects (added to func_use_for_or_listcomp_var test)
|
706 |
#astmts = [stmt for stmt in node.lookup(name)[1]
|
707 |
# if hasattr(stmt, 'ass_type')] and
|
708 |
# not stmt.statement().parent_of(node)]
|
709 |
if not self.linter.is_message_enabled('undefined-loop-variable'): |
710 |
return
|
711 |
astmts = [stmt for stmt in node.lookup(name)[1] |
712 |
if hasattr(stmt, 'ass_type')] |
713 |
# filter variables according their respective scope test is_statement
|
714 |
# and parent to avoid #74747. This is not a total fix, which would
|
715 |
# introduce a mechanism similar to special attribute lookup in
|
716 |
# modules. Also, in order to get correct inference in this case, the
|
717 |
# scope lookup rules would need to be changed to return the initial
|
718 |
# assignment (which does not exist in code per se) as well as any later
|
719 |
# modifications.
|
720 |
if not astmts or (astmts[0].is_statement or astmts[0].parent) \ |
721 |
and astmts[0].statement().parent_of(node): |
722 |
_astmts = [] |
723 |
else:
|
724 |
_astmts = astmts[:1]
|
725 |
for i, stmt in enumerate(astmts[1:]): |
726 |
if (astmts[i].statement().parent_of(stmt)
|
727 |
and not in_for_else_branch(astmts[i].statement(), stmt)): |
728 |
continue
|
729 |
_astmts.append(stmt) |
730 |
astmts = _astmts |
731 |
if len(astmts) == 1: |
732 |
assign = astmts[0].assign_type()
|
733 |
if (isinstance(assign, (astroid.For, astroid.Comprehension, |
734 |
astroid.GeneratorExp)) |
735 |
and assign.statement() is not node.statement()): |
736 |
self.add_message('undefined-loop-variable', args=name, node=node) |
737 |
|
738 |
@check_messages('redefine-in-handler') |
739 |
def visit_excepthandler(self, node): |
740 |
for name in get_all_elements(node.name): |
741 |
clobbering, args = clobber_in_except(name) |
742 |
if clobbering:
|
743 |
self.add_message('redefine-in-handler', args=args, node=name) |
744 |
|
745 |
def visit_assignname(self, node): |
746 |
if isinstance(node.assign_type(), astroid.AugAssign): |
747 |
self.visit_name(node)
|
748 |
|
749 |
def visit_delname(self, node): |
750 |
self.visit_name(node)
|
751 |
|
752 |
@staticmethod
|
753 |
def _defined_in_function_definition(node, frame): |
754 |
in_annotation_or_default = False
|
755 |
if (isinstance(frame, astroid.FunctionDef) and |
756 |
node.statement() is frame):
|
757 |
in_annotation_or_default = ( |
758 |
( |
759 |
PY3K and (node in frame.args.annotations |
760 |
or node is frame.args.varargannotation |
761 |
or node is frame.args.kwargannotation) |
762 |
) |
763 |
or
|
764 |
frame.args.parent_of(node) |
765 |
) |
766 |
return in_annotation_or_default
|
767 |
|
768 |
@staticmethod
|
769 |
def _next_to_consume(node, name, to_consume): |
770 |
# mark the name as consumed if it's defined in this scope
|
771 |
found_node = to_consume.get(name) |
772 |
if (found_node
|
773 |
and isinstance(node.parent, astroid.Assign) |
774 |
and node.parent == found_node[0].parent): |
775 |
lhs = found_node[0].parent.targets[0] |
776 |
if lhs.name == name: # this name is defined in this very statement |
777 |
found_node = None
|
778 |
return found_node
|
779 |
|
780 |
@staticmethod
|
781 |
def _is_variable_violation(node, name, defnode, stmt, defstmt, |
782 |
frame, defframe, base_scope_type, |
783 |
recursive_klass): |
784 |
maybee0601 = True
|
785 |
annotation_return = False
|
786 |
if frame is not defframe: |
787 |
maybee0601 = _detect_global_scope(node, frame, defframe) |
788 |
elif defframe.parent is None: |
789 |
# we are at the module level, check the name is not
|
790 |
# defined in builtins
|
791 |
if name in defframe.scope_attrs or astroid.builtin_lookup(name)[1]: |
792 |
maybee0601 = False
|
793 |
else:
|
794 |
# we are in a local scope, check the name is not
|
795 |
# defined in global or builtin scope
|
796 |
if defframe.root().lookup(name)[1]: |
797 |
maybee0601 = False
|
798 |
else:
|
799 |
# check if we have a nonlocal
|
800 |
if name in defframe.locals: |
801 |
maybee0601 = not any(isinstance(child, astroid.Nonlocal) |
802 |
and name in child.names |
803 |
for child in defframe.get_children()) |
804 |
|
805 |
if (base_scope_type == 'lambda' and |
806 |
isinstance(frame, astroid.ClassDef)
|
807 |
and name in frame.locals): |
808 |
|
809 |
# This rule verifies that if the definition node of the
|
810 |
# checked name is an Arguments node and if the name
|
811 |
# is used a default value in the arguments defaults
|
812 |
# and the actual definition of the variable label
|
813 |
# is happening before the Arguments definition.
|
814 |
#
|
815 |
# bar = None
|
816 |
# foo = lambda bar=bar: bar
|
817 |
#
|
818 |
# In this case, maybee0601 should be False, otherwise
|
819 |
# it should be True.
|
820 |
maybee0601 = not (isinstance(defnode, astroid.Arguments) and |
821 |
node in defnode.defaults and |
822 |
frame.locals[name][0].fromlineno < defstmt.fromlineno)
|
823 |
elif (isinstance(defframe, astroid.ClassDef) and |
824 |
isinstance(frame, astroid.FunctionDef)):
|
825 |
# Special rule for function return annotations,
|
826 |
# which uses the same name as the class where
|
827 |
# the function lives.
|
828 |
if (PY3K and node is frame.returns and |
829 |
defframe.parent_of(frame.returns)): |
830 |
maybee0601 = annotation_return = True
|
831 |
|
832 |
if (maybee0601 and defframe.name in defframe.locals and |
833 |
defframe.locals[name][0].lineno < frame.lineno):
|
834 |
# Detect class assignments with the same
|
835 |
# name as the class. In this case, no warning
|
836 |
# should be raised.
|
837 |
maybee0601 = False
|
838 |
if isinstance(node.parent, astroid.Arguments): |
839 |
maybee0601 = stmt.fromlineno <= defstmt.fromlineno |
840 |
elif recursive_klass:
|
841 |
maybee0601 = True
|
842 |
else:
|
843 |
maybee0601 = maybee0601 and stmt.fromlineno <= defstmt.fromlineno
|
844 |
return maybee0601, annotation_return
|
845 |
|
846 |
def _ignore_class_scope(self, node, name, frame): |
847 |
# Detect if we are in a local class scope, as an assignment.
|
848 |
# For example, the following is fair game.
|
849 |
#
|
850 |
# class A:
|
851 |
# b = 1
|
852 |
# c = lambda b=b: b * b
|
853 |
#
|
854 |
# class B:
|
855 |
# tp = 1
|
856 |
# def func(self, arg: tp):
|
857 |
# ...
|
858 |
# class C:
|
859 |
# tp = 2
|
860 |
# def func(self, arg=tp):
|
861 |
# ...
|
862 |
|
863 |
in_annotation_or_default = self._defined_in_function_definition(
|
864 |
node, frame) |
865 |
if in_annotation_or_default:
|
866 |
frame_locals = frame.parent.scope().locals |
867 |
else:
|
868 |
frame_locals = frame.locals |
869 |
return not ((isinstance(frame, astroid.ClassDef) or |
870 |
in_annotation_or_default) and
|
871 |
name in frame_locals)
|
872 |
|
873 |
@check_messages(*(MSGS.keys()))
|
874 |
def visit_name(self, node): |
875 |
"""check that a name is defined if the current scope and doesn't
|
876 |
redefine a built-in
|
877 |
"""
|
878 |
stmt = node.statement() |
879 |
if stmt.fromlineno is None: |
880 |
# name node from a astroid built from live code, skip
|
881 |
assert not stmt.root().file.endswith('.py') |
882 |
return
|
883 |
name = node.name |
884 |
frame = stmt.scope() |
885 |
# if the name node is used as a function default argument's value or as
|
886 |
# a decorator, then start from the parent frame of the function instead
|
887 |
# of the function frame - and thus open an inner class scope
|
888 |
if (is_func_default(node) or is_func_decorator(node) |
889 |
or is_ancestor_name(frame, node)):
|
890 |
start_index = len(self._to_consume) - 2 |
891 |
else:
|
892 |
start_index = len(self._to_consume) - 1 |
893 |
# iterates through parent scopes, from the inner to the outer
|
894 |
base_scope_type = self._to_consume[start_index][-1] |
895 |
# pylint: disable=too-many-nested-blocks; refactoring this block is a pain.
|
896 |
for i in range(start_index, -1, -1): |
897 |
to_consume, consumed, scope_type = self._to_consume[i]
|
898 |
# if the current scope is a class scope but it's not the inner
|
899 |
# scope, ignore it. This prevents to access this scope instead of
|
900 |
# the globals one in function members when there are some common
|
901 |
# names. The only exception is when the starting scope is a
|
902 |
# comprehension and its direct outer scope is a class
|
903 |
if scope_type == 'class' and i != start_index and not ( |
904 |
base_scope_type == 'comprehension' and i == start_index-1): |
905 |
if self._ignore_class_scope(node, name, frame): |
906 |
continue
|
907 |
|
908 |
# the name has already been consumed, only check it's not a loop
|
909 |
# variable used outside the loop
|
910 |
if name in consumed: |
911 |
defnode = assign_parent(consumed[name][0])
|
912 |
self._check_late_binding_closure(node, defnode)
|
913 |
self._loopvar_name(node, name)
|
914 |
break
|
915 |
found_node = self._next_to_consume(node, name, to_consume)
|
916 |
if found_node:
|
917 |
consumed[name] = found_node |
918 |
else:
|
919 |
continue
|
920 |
# checks for use before assignment
|
921 |
defnode = assign_parent(to_consume[name][0])
|
922 |
if defnode is not None: |
923 |
self._check_late_binding_closure(node, defnode)
|
924 |
defstmt = defnode.statement() |
925 |
defframe = defstmt.frame() |
926 |
# The class reuses itself in the class scope.
|
927 |
recursive_klass = (frame is defframe and |
928 |
defframe.parent_of(node) and
|
929 |
isinstance(defframe, astroid.ClassDef) and |
930 |
node.name == defframe.name) |
931 |
|
932 |
maybee0601, annotation_return = self._is_variable_violation(
|
933 |
node, name, defnode, stmt, defstmt, |
934 |
frame, defframe, |
935 |
base_scope_type, recursive_klass) |
936 |
|
937 |
if (maybee0601
|
938 |
and not is_defined_before(node) |
939 |
and not astroid.are_exclusive(stmt, defstmt, ('NameError', |
940 |
'Exception',
|
941 |
'BaseException'))):
|
942 |
|
943 |
# Used and defined in the same place, e.g `x += 1` and `del x`
|
944 |
defined_by_stmt = ( |
945 |
defstmt is stmt
|
946 |
and isinstance(node, (astroid.DelName, astroid.AssignName)) |
947 |
) |
948 |
|
949 |
if (recursive_klass
|
950 |
or defined_by_stmt
|
951 |
or annotation_return
|
952 |
or isinstance(defstmt, astroid.Delete)): |
953 |
if not node_ignores_exception(node, NameError): |
954 |
self.add_message('undefined-variable', args=name, |
955 |
node=node) |
956 |
elif base_scope_type != 'lambda': |
957 |
# E0601 may *not* occurs in lambda scope.
|
958 |
self.add_message('used-before-assignment', args=name, node=node) |
959 |
elif base_scope_type == 'lambda': |
960 |
# E0601 can occur in class-level scope in lambdas, as in
|
961 |
# the following example:
|
962 |
# class A:
|
963 |
# x = lambda attr: f + attr
|
964 |
# f = 42
|
965 |
if isinstance(frame, astroid.ClassDef) and name in frame.locals: |
966 |
if isinstance(node.parent, astroid.Arguments): |
967 |
if stmt.fromlineno <= defstmt.fromlineno:
|
968 |
# Doing the following is fine:
|
969 |
# class A:
|
970 |
# x = 42
|
971 |
# y = lambda attr=x: attr
|
972 |
self.add_message('used-before-assignment', |
973 |
args=name, node=node) |
974 |
else:
|
975 |
self.add_message('undefined-variable', |
976 |
args=name, node=node) |
977 |
elif scope_type == 'lambda': |
978 |
self.add_message('undefined-variable', |
979 |
node=node, args=name) |
980 |
|
981 |
del to_consume[name]
|
982 |
# check it's not a loop variable used outside the loop
|
983 |
self._loopvar_name(node, name)
|
984 |
break
|
985 |
else:
|
986 |
# we have not found the name, if it isn't a builtin, that's an
|
987 |
# undefined name !
|
988 |
if not (name in astroid.Module.scope_attrs or is_builtin(name) |
989 |
or name in self.config.additional_builtins): |
990 |
if not node_ignores_exception(node, NameError): |
991 |
self.add_message('undefined-variable', args=name, node=node) |
992 |
|
993 |
@check_messages('no-name-in-module') |
994 |
def visit_import(self, node): |
995 |
"""check modules attribute accesses"""
|
996 |
if node_ignores_exception(node, ImportError): |
997 |
# No need to verify this, since ImportError is already
|
998 |
# handled by the client code.
|
999 |
return
|
1000 |
|
1001 |
for name, _ in node.names: |
1002 |
parts = name.split('.')
|
1003 |
try:
|
1004 |
module = next(node.infer_name_module(parts[0])) |
1005 |
except astroid.ResolveError:
|
1006 |
continue
|
1007 |
self._check_module_attrs(node, module, parts[1:]) |
1008 |
|
1009 |
@check_messages('no-name-in-module') |
1010 |
def visit_importfrom(self, node): |
1011 |
"""check modules attribute accesses"""
|
1012 |
if node_ignores_exception(node, ImportError): |
1013 |
# No need to verify this, since ImportError is already
|
1014 |
# handled by the client code.
|
1015 |
return
|
1016 |
|
1017 |
name_parts = node.modname.split('.')
|
1018 |
try:
|
1019 |
module = node.do_import_module(name_parts[0])
|
1020 |
except Exception: |
1021 |
return
|
1022 |
module = self._check_module_attrs(node, module, name_parts[1:]) |
1023 |
if not module: |
1024 |
return
|
1025 |
for name, _ in node.names: |
1026 |
if name == '*': |
1027 |
continue
|
1028 |
self._check_module_attrs(node, module, name.split('.')) |
1029 |
|
1030 |
@check_messages('unbalanced-tuple-unpacking', 'unpacking-non-sequence') |
1031 |
def visit_assign(self, node): |
1032 |
"""Check unbalanced tuple unpacking for assignments
|
1033 |
and unpacking non-sequences.
|
1034 |
"""
|
1035 |
if not isinstance(node.targets[0], (astroid.Tuple, astroid.List)): |
1036 |
return
|
1037 |
|
1038 |
targets = node.targets[0].itered()
|
1039 |
try:
|
1040 |
infered = safe_infer(node.value) |
1041 |
if infered is not None: |
1042 |
self._check_unpacking(infered, node, targets)
|
1043 |
except astroid.InferenceError:
|
1044 |
return
|
1045 |
|
1046 |
def _check_unpacking(self, infered, node, targets): |
1047 |
""" Check for unbalanced tuple unpacking
|
1048 |
and unpacking non sequences.
|
1049 |
"""
|
1050 |
if is_inside_abstract_class(node):
|
1051 |
return
|
1052 |
if is_comprehension(node):
|
1053 |
return
|
1054 |
if infered is astroid.YES: |
1055 |
return
|
1056 |
if (isinstance(infered.parent, astroid.Arguments) and |
1057 |
isinstance(node.value, astroid.Name) and |
1058 |
node.value.name == infered.parent.vararg): |
1059 |
# Variable-length argument, we can't determine the length.
|
1060 |
return
|
1061 |
if isinstance(infered, (astroid.Tuple, astroid.List)): |
1062 |
# attempt to check unpacking is properly balanced
|
1063 |
values = infered.itered() |
1064 |
if len(targets) != len(values): |
1065 |
# Check if we have starred nodes.
|
1066 |
if any(isinstance(target, astroid.Starred) |
1067 |
for target in targets): |
1068 |
return
|
1069 |
self.add_message('unbalanced-tuple-unpacking', node=node, |
1070 |
args=(_get_unpacking_extra_info(node, infered), |
1071 |
len(targets),
|
1072 |
len(values)))
|
1073 |
# attempt to check unpacking may be possible (ie RHS is iterable)
|
1074 |
else:
|
1075 |
if not is_iterable(infered): |
1076 |
self.add_message('unpacking-non-sequence', node=node, |
1077 |
args=(_get_unpacking_extra_info(node, infered),)) |
1078 |
|
1079 |
|
1080 |
def _check_module_attrs(self, node, module, module_names): |
1081 |
"""check that module_names (list of string) are accessible through the
|
1082 |
given module
|
1083 |
if the latest access name corresponds to a module, return it
|
1084 |
"""
|
1085 |
assert isinstance(module, astroid.Module), module |
1086 |
ignored_modules = get_global_option(self, 'ignored-modules', |
1087 |
default=[]) |
1088 |
while module_names:
|
1089 |
name = module_names.pop(0)
|
1090 |
if name == '__dict__': |
1091 |
module = None
|
1092 |
break
|
1093 |
try:
|
1094 |
module = next(module.getattr(name)[0].infer()) |
1095 |
if module is astroid.YES: |
1096 |
return None |
1097 |
except astroid.NotFoundError:
|
1098 |
if module.name in ignored_modules: |
1099 |
return None |
1100 |
self.add_message('no-name-in-module', |
1101 |
args=(name, module.name), node=node) |
1102 |
return None |
1103 |
except astroid.InferenceError:
|
1104 |
return None |
1105 |
if module_names:
|
1106 |
# FIXME: other message if name is not the latest part of
|
1107 |
# module_names ?
|
1108 |
modname = module and module.name or '__dict__' |
1109 |
self.add_message('no-name-in-module', node=node, |
1110 |
args=('.'.join(module_names), modname))
|
1111 |
return None |
1112 |
if isinstance(module, astroid.Module): |
1113 |
return module
|
1114 |
return None |
1115 |
|
1116 |
|
1117 |
class VariablesChecker3k(VariablesChecker): |
1118 |
'''Modified variables checker for 3k'''
|
1119 |
# listcomp have now also their scope
|
1120 |
|
1121 |
def visit_listcomp(self, node): |
1122 |
"""visit dictcomp: update consumption analysis variable
|
1123 |
"""
|
1124 |
self._to_consume.append((copy(node.locals), {}, 'comprehension')) |
1125 |
|
1126 |
def leave_listcomp(self, _): |
1127 |
"""leave dictcomp: update consumption analysis variable
|
1128 |
"""
|
1129 |
# do not check for not used locals here
|
1130 |
self._to_consume.pop()
|
1131 |
|
1132 |
def leave_module(self, node): |
1133 |
""" Update consumption analysis variable
|
1134 |
for metaclasses.
|
1135 |
"""
|
1136 |
module_locals = self._to_consume[0][0] |
1137 |
module_imports = self._to_consume[0][1] |
1138 |
consumed = {} |
1139 |
|
1140 |
for klass in node.nodes_of_class(astroid.ClassDef): |
1141 |
found = metaclass = name = None
|
1142 |
if not klass._metaclass: |
1143 |
# Skip if this class doesn't use
|
1144 |
# explictly a metaclass, but inherits it from ancestors
|
1145 |
continue
|
1146 |
|
1147 |
metaclass = klass.metaclass() |
1148 |
|
1149 |
# Look the name in the already found locals.
|
1150 |
# If it's not found there, look in the module locals
|
1151 |
# and in the imported modules.
|
1152 |
if isinstance(klass._metaclass, astroid.Name): |
1153 |
name = klass._metaclass.name |
1154 |
elif metaclass:
|
1155 |
# if it uses a `metaclass=module.Class`
|
1156 |
name = metaclass.root().name |
1157 |
|
1158 |
if name:
|
1159 |
found = consumed.setdefault( |
1160 |
name, module_locals.get(name, module_imports.get(name))) |
1161 |
|
1162 |
if found is None and not metaclass: |
1163 |
name = None
|
1164 |
if isinstance(klass._metaclass, astroid.Name): |
1165 |
name = klass._metaclass.name |
1166 |
elif isinstance(klass._metaclass, astroid.Attribute): |
1167 |
name = klass._metaclass.as_string() |
1168 |
|
1169 |
if name is not None: |
1170 |
if not (name in astroid.Module.scope_attrs or |
1171 |
is_builtin(name) or
|
1172 |
name in self.config.additional_builtins or |
1173 |
name in node.locals):
|
1174 |
self.add_message('undefined-variable', |
1175 |
node=klass, |
1176 |
args=(name, )) |
1177 |
# Pop the consumed items, in order to
|
1178 |
# avoid having unused-import false positives
|
1179 |
for name in consumed: |
1180 |
module_locals.pop(name, None)
|
1181 |
super(VariablesChecker3k, self).leave_module(node) |
1182 |
|
1183 |
if sys.version_info >= (3, 0): |
1184 |
VariablesChecker = VariablesChecker3k |
1185 |
|
1186 |
|
1187 |
def register(linter): |
1188 |
"""required method to auto register this checker"""
|
1189 |
linter.register_checker(VariablesChecker(linter)) |