Index: third_party/pylint/checkers/variables.py |
diff --git a/third_party/pylint/checkers/variables.py b/third_party/pylint/checkers/variables.py |
index c75768bb66103caa6661a876d09bd22291787a1d..484a9526fede5fce652f82f186f0bb6040108225 100644 |
--- a/third_party/pylint/checkers/variables.py |
+++ b/third_party/pylint/checkers/variables.py |
@@ -1,4 +1,4 @@ |
-# Copyright (c) 2003-2014 LOGILAB S.A. (Paris, FRANCE). |
+# Copyright (c) 2003-2011 LOGILAB S.A. (Paris, FRANCE). |
# http://www.logilab.fr/ -- mailto:contact@logilab.fr |
# |
# This program is free software; you can redistribute it and/or modify it under |
@@ -12,32 +12,27 @@ |
# |
# You should have received a copy of the GNU General Public License along with |
# this program; if not, write to the Free Software Foundation, Inc., |
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
"""variables checkers for Python code |
""" |
-import os |
+ |
import sys |
from copy import copy |
-import astroid |
-from astroid import are_exclusive, builtin_lookup, AstroidBuildingException |
- |
-from logilab.common.modutils import file_from_modpath |
+from logilab import astng |
+from logilab.astng import are_exclusive, builtin_lookup, ASTNGBuildingException |
-from pylint.interfaces import IAstroidChecker |
-from pylint.utils import get_global_option |
+from pylint.interfaces import IASTNGChecker |
from pylint.checkers import BaseChecker |
-from pylint.checkers.utils import ( |
- PYMETHODS, is_ancestor_name, is_builtin, |
- is_defined_before, is_error, is_func_default, is_func_decorator, |
- assign_parent, check_messages, is_inside_except, clobber_in_except, |
- get_all_elements) |
+from pylint.checkers.utils import (PYMETHODS, is_ancestor_name, is_builtin, |
+ is_defined_before, is_error, is_func_default, is_func_decorator, |
+ assign_parent, check_messages, is_inside_except, clobber_in_except) |
def in_for_else_branch(parent, stmt): |
- """Returns True if stmt in inside the else branch for a parent For stmt.""" |
- return (isinstance(parent, astroid.For) and |
- any(else_stmt.parent_of(stmt) for else_stmt in parent.orelse)) |
+ """Returns True if stmt in inside the else branch for a parent For stmt.""" |
+ return (isinstance(parent, astng.For) and |
+ any(else_stmt.parent_of(stmt) for else_stmt in parent.orelse)) |
def overridden_method(klass, name): |
"""get overridden method if any""" |
@@ -49,176 +44,58 @@ def overridden_method(klass, name): |
meth_node = parent[name] |
except KeyError: |
# We have found an ancestor defining <name> but it's not in the local |
- # dictionary. This may happen with astroid built from living objects. |
+ # dictionary. This may happen with astng built from living objects. |
return None |
- if isinstance(meth_node, astroid.Function): |
+ if isinstance(meth_node, astng.Function): |
return meth_node |
return None |
-def _get_unpacking_extra_info(node, infered): |
- """return extra information to add to the message for unpacking-non-sequence |
- and unbalanced-tuple-unpacking errors |
- """ |
- more = '' |
- infered_module = infered.root().name |
- if node.root().name == infered_module: |
- if node.lineno == infered.lineno: |
- more = ' %s' % infered.as_string() |
- elif infered.lineno: |
- more = ' defined at line %s' % infered.lineno |
- elif infered.lineno: |
- more = ' defined at line %s of %s' % (infered.lineno, infered_module) |
- return more |
- |
-def _detect_global_scope(node, frame, defframe): |
- """ Detect that the given frames shares a global |
- scope. |
- |
- Two frames shares a global scope when neither |
- of them are hidden under a function scope, as well |
- as any of parent scope of them, until the root scope. |
- In this case, depending from something defined later on |
- will not work, because it is still undefined. |
- |
- Example: |
- class A: |
- # B has the same global scope as `C`, leading to a NameError. |
- class B(C): ... |
- class C: ... |
- |
- """ |
- def_scope = scope = None |
- if frame and frame.parent: |
- scope = frame.parent.scope() |
- if defframe and defframe.parent: |
- def_scope = defframe.parent.scope() |
- if isinstance(frame, astroid.Function): |
- # If the parent of the current node is a |
- # function, then it can be under its scope |
- # (defined in, which doesn't concern us) or |
- # the `->` part of annotations. The same goes |
- # for annotations of function arguments, they'll have |
- # their parent the Arguments node. |
- if not isinstance(node.parent, |
- (astroid.Function, astroid.Arguments)): |
- return False |
- elif any(not isinstance(f, (astroid.Class, astroid.Module)) |
- for f in (frame, defframe)): |
- # Not interested in other frames, since they are already |
- # not in a global scope. |
- return False |
- |
- break_scopes = [] |
- for s in (scope, def_scope): |
- # Look for parent scopes. If there is anything different |
- # than a module or a class scope, then they frames don't |
- # share a global scope. |
- parent_scope = s |
- while parent_scope: |
- if not isinstance(parent_scope, (astroid.Class, astroid.Module)): |
- break_scopes.append(parent_scope) |
- break |
- if parent_scope.parent: |
- parent_scope = parent_scope.parent.scope() |
- else: |
- break |
- if break_scopes and len(set(break_scopes)) != 1: |
- # Store different scopes than expected. |
- # If the stored scopes are, in fact, the very same, then it means |
- # that the two frames (frame and defframe) shares the same scope, |
- # and we could apply our lineno analysis over them. |
- # For instance, this works when they are inside a function, the node |
- # that uses a definition and the definition itself. |
- return False |
- # At this point, we are certain that frame and defframe shares a scope |
- # and the definition of the first depends on the second. |
- return frame.lineno < defframe.lineno |
- |
MSGS = { |
'E0601': ('Using variable %r before assignment', |
- 'used-before-assignment', |
'Used when a local variable is accessed before it\'s \ |
assignment.'), |
'E0602': ('Undefined variable %r', |
- 'undefined-variable', |
'Used when an undefined variable is accessed.'), |
- 'E0603': ('Undefined variable name %r in __all__', |
- 'undefined-all-variable', |
- 'Used when an undefined variable name is referenced in __all__.'), |
- 'E0604': ('Invalid object %r in __all__, must contain only strings', |
- 'invalid-all-object', |
- 'Used when an invalid (non-string) object occurs in __all__.'), |
'E0611': ('No name %r in module %r', |
- 'no-name-in-module', |
'Used when a name cannot be found in a module.'), |
'W0601': ('Global variable %r undefined at the module level', |
- 'global-variable-undefined', |
'Used when a variable is defined through the "global" statement \ |
but the variable is not defined in the module scope.'), |
'W0602': ('Using global for %r but no assignment is done', |
- 'global-variable-not-assigned', |
'Used when a variable is defined through the "global" statement \ |
but no assignment to this variable is done.'), |
'W0603': ('Using the global statement', # W0121 |
- 'global-statement', |
'Used when you use the "global" statement to update a global \ |
variable. PyLint just try to discourage this \ |
usage. That doesn\'t mean you can not use it !'), |
'W0604': ('Using the global statement at the module level', # W0103 |
- 'global-at-module-level', |
'Used when you use the "global" statement at the module level \ |
since it has no effect'), |
'W0611': ('Unused import %s', |
- 'unused-import', |
'Used when an imported module or variable is not used.'), |
'W0612': ('Unused variable %r', |
- 'unused-variable', |
'Used when a variable is defined but not used.'), |
'W0613': ('Unused argument %r', |
- 'unused-argument', |
'Used when a function or method argument is not used.'), |
'W0614': ('Unused import %s from wildcard import', |
- 'unused-wildcard-import', |
'Used when an imported module or variable is not used from a \ |
\'from X import *\' style import.'), |
'W0621': ('Redefining name %r from outer scope (line %s)', |
- 'redefined-outer-name', |
'Used when a variable\'s name hide a name defined in the outer \ |
scope.'), |
'W0622': ('Redefining built-in %r', |
- 'redefined-builtin', |
'Used when a variable or function override a built-in.'), |
'W0623': ('Redefining name %r from %s in exception handler', |
- 'redefine-in-handler', |
'Used when an exception handler assigns the exception \ |
to an existing name'), |
'W0631': ('Using possibly undefined loop variable %r', |
- 'undefined-loop-variable', |
'Used when an loop variable (i.e. defined by a for loop or \ |
a list comprehension or a generator expression) is used outside \ |
the loop.'), |
- |
- 'W0632': ('Possible unbalanced tuple unpacking with ' |
- 'sequence%s: ' |
- 'left side has %d label(s), right side has %d value(s)', |
- 'unbalanced-tuple-unpacking', |
- 'Used when there is an unbalanced tuple unpacking in assignment'), |
- |
- 'W0633': ('Attempting to unpack a non-sequence%s', |
- 'unpacking-non-sequence', |
- 'Used when something which is not ' |
- 'a sequence is used in an unpack assignment'), |
- |
- 'W0640': ('Cell variable %s defined in loop', |
- 'cell-var-from-loop', |
- 'A variable used in a closure is defined in a loop. ' |
- 'This will result in all closures using the same value for ' |
- 'the closed-over variable.'), |
- |
} |
class VariablesChecker(BaseChecker): |
@@ -227,107 +104,67 @@ class VariablesChecker(BaseChecker): |
* undefined variables |
* redefinition of variable from builtins or from an outer scope |
* use of variable before assignment |
- * __all__ consistency |
""" |
- __implements__ = IAstroidChecker |
+ __implements__ = IASTNGChecker |
name = 'variables' |
msgs = MSGS |
priority = -1 |
- options = (("init-import", |
+ options = ( |
+ ("init-import", |
{'default': 0, 'type' : 'yn', 'metavar' : '<y_or_n>', |
'help' : 'Tells whether we should check for unused import in \ |
__init__ files.'}), |
("dummy-variables-rgx", |
- {'default': ('_$|dummy'), |
+ {'default': ('_|dummy'), |
'type' :'regexp', 'metavar' : '<regexp>', |
- 'help' : 'A regular expression matching the name of dummy \ |
-variables (i.e. expectedly not used).'}), |
+ 'help' : 'A regular expression matching the beginning of \ |
+ the name of dummy variables (i.e. not used).'}), |
("additional-builtins", |
{'default': (), 'type' : 'csv', |
'metavar' : '<comma separated list>', |
'help' : 'List of additional names supposed to be defined in \ |
builtins. Remember that you should avoid to define new builtins when possible.' |
- }), |
- ) |
+ }), |
+ ) |
def __init__(self, linter=None): |
BaseChecker.__init__(self, linter) |
self._to_consume = None |
self._checking_mod_attr = None |
+ self._vars = None |
def visit_module(self, node): |
"""visit module : update consumption analysis variable |
checks globals doesn't overrides builtins |
""" |
self._to_consume = [(copy(node.locals), {}, 'module')] |
- for name, stmts in node.locals.iteritems(): |
+ self._vars = [] |
+ for name, stmts in node.locals.items(): |
if is_builtin(name) and not is_inside_except(stmts[0]): |
# do not print Redefining builtin for additional builtins |
- self.add_message('redefined-builtin', args=name, node=stmts[0]) |
+ self.add_message('W0622', args=name, node=stmts[0]) |
- @check_messages('unused-import', 'unused-wildcard-import', 'redefined-builtin', 'undefined-all-variable', 'invalid-all-object') |
+ @check_messages('W0611', 'W0614') |
def leave_module(self, node): |
"""leave module: check globals |
""" |
assert len(self._to_consume) == 1 |
not_consumed = self._to_consume.pop()[0] |
- # attempt to check for __all__ if defined |
- if '__all__' in node.locals: |
- assigned = node.igetattr('__all__').next() |
- if assigned is not astroid.YES: |
- for elt in getattr(assigned, 'elts', ()): |
- try: |
- elt_name = elt.infer().next() |
- except astroid.InferenceError: |
- continue |
- |
- if not isinstance(elt_name, astroid.Const) \ |
- or not isinstance(elt_name.value, basestring): |
- self.add_message('invalid-all-object', args=elt.as_string(), node=elt) |
- continue |
- elt_name = elt_name.value |
- # If elt is in not_consumed, remove it from not_consumed |
- if elt_name in not_consumed: |
- del not_consumed[elt_name] |
- continue |
- if elt_name not in node.locals: |
- if not node.package: |
- self.add_message('undefined-all-variable', |
- args=elt_name, |
- node=elt) |
- else: |
- basename = os.path.splitext(node.file)[0] |
- if os.path.basename(basename) == '__init__': |
- name = node.name + "." + elt_name |
- try: |
- file_from_modpath(name.split(".")) |
- except ImportError: |
- self.add_message('undefined-all-variable', |
- args=elt_name, |
- node=elt) |
- except SyntaxError: |
- # don't yield an syntax-error warning, |
- # because it will be later yielded |
- # when the file will be checked |
- pass |
# don't check unused imports in __init__ files |
if not self.config.init_import and node.package: |
return |
- for name, stmts in not_consumed.iteritems(): |
- if any(isinstance(stmt, astroid.AssName) |
- and isinstance(stmt.ass_type(), astroid.AugAssign) |
- for stmt in stmts): |
- continue |
+ for name, stmts in not_consumed.items(): |
stmt = stmts[0] |
- if isinstance(stmt, astroid.Import): |
- self.add_message('unused-import', args=name, node=stmt) |
- elif isinstance(stmt, astroid.From) and stmt.modname != '__future__': |
+ if isinstance(stmt, astng.Import): |
+ self.add_message('W0611', args=name, node=stmt) |
+ elif isinstance(stmt, astng.From) and stmt.modname != '__future__': |
if stmt.names[0][0] == '*': |
- self.add_message('unused-wildcard-import', args=name, node=stmt) |
+ self.add_message('W0614', args=name, node=stmt) |
else: |
- self.add_message('unused-import', args=name, node=stmt) |
+ self.add_message('W0611', args=name, node=stmt) |
del self._to_consume |
+ del self._vars |
def visit_class(self, node): |
"""visit class: update consumption analysis variable |
@@ -388,27 +225,25 @@ builtins. Remember that you should avoid to define new builtins when possible.' |
"""visit function: update consumption analysis variable and check locals |
""" |
self._to_consume.append((copy(node.locals), {}, 'function')) |
- if not (self.linter.is_message_enabled('redefined-outer-name') or |
- self.linter.is_message_enabled('redefined-builtin')): |
+ self._vars.append({}) |
+ if not set(('W0621', 'W0622')) & self.active_msgs: |
return |
globs = node.root().globals |
for name, stmt in node.items(): |
if is_inside_except(stmt): |
continue |
- if name in globs and not isinstance(stmt, astroid.Global): |
- line = globs[name][0].fromlineno |
- dummy_rgx = self.config.dummy_variables_rgx |
- if not dummy_rgx.match(name): |
- self.add_message('redefined-outer-name', args=(name, line), node=stmt) |
+ if name in globs and not isinstance(stmt, astng.Global): |
+ line = globs[name][0].lineno |
+ self.add_message('W0621', args=(name, line), node=stmt) |
elif is_builtin(name): |
# do not print Redefining builtin for additional builtins |
- self.add_message('redefined-builtin', args=name, node=stmt) |
+ self.add_message('W0622', args=name, node=stmt) |
def leave_function(self, node): |
"""leave function: check function's locals are consumed""" |
not_consumed = self._to_consume.pop()[0] |
- if not (self.linter.is_message_enabled('unused-variable') or |
- self.linter.is_message_enabled('unused-argument')): |
+ self._vars.pop(0) |
+ if not set(('W0612', 'W0613')) & self.active_msgs: |
return |
# don't check arguments of function which are only raising an exception |
if is_error(node): |
@@ -421,13 +256,6 @@ builtins. Remember that you should avoid to define new builtins when possible.' |
authorized_rgx = self.config.dummy_variables_rgx |
called_overridden = False |
argnames = node.argnames() |
- global_names = set() |
- nonlocal_names = set() |
- for global_stmt in node.nodes_of_class(astroid.Global): |
- global_names.update(set(global_stmt.names)) |
- for nonlocal_stmt in node.nodes_of_class(astroid.Nonlocal): |
- nonlocal_names.update(set(nonlocal_stmt.names)) |
- |
for name, stmts in not_consumed.iteritems(): |
# ignore some special names specified by user configuration |
if authorized_rgx.match(name): |
@@ -435,25 +263,8 @@ builtins. Remember that you should avoid to define new builtins when possible.' |
# ignore names imported by the global statement |
# FIXME: should only ignore them if it's assigned latter |
stmt = stmts[0] |
- if isinstance(stmt, astroid.Global): |
+ if isinstance(stmt, astng.Global): |
continue |
- if isinstance(stmt, (astroid.Import, astroid.From)): |
- # Detect imports, assigned to global statements. |
- if global_names: |
- skip = False |
- for import_name, import_alias in stmt.names: |
- # If the import uses an alias, check only that. |
- # Otherwise, check only the import name. |
- if import_alias: |
- if import_alias in global_names: |
- skip = True |
- break |
- elif import_name in global_names: |
- skip = True |
- break |
- if skip: |
- continue |
- |
# care about functions with unknown argument (builtins) |
if name in argnames: |
if is_method: |
@@ -471,27 +282,23 @@ builtins. Remember that you should avoid to define new builtins when possible.' |
# don't check callback arguments XXX should be configurable |
if node.name.startswith('cb_') or node.name.endswith('_cb'): |
continue |
- self.add_message('unused-argument', args=name, node=stmt) |
+ self.add_message('W0613', args=name, node=stmt) |
else: |
- if stmt.parent and isinstance(stmt.parent, astroid.Assign): |
- if name in nonlocal_names: |
- continue |
- self.add_message('unused-variable', args=name, node=stmt) |
+ self.add_message('W0612', args=name, node=stmt) |
- @check_messages('global-variable-undefined', 'global-variable-not-assigned', 'global-statement', |
- 'global-at-module-level', 'redefined-builtin') |
+ @check_messages('W0601', 'W0602', 'W0603', 'W0604', 'W0622') |
def visit_global(self, node): |
"""check names imported exists in the global scope""" |
frame = node.frame() |
- if isinstance(frame, astroid.Module): |
- self.add_message('global-at-module-level', node=node) |
+ if isinstance(frame, astng.Module): |
+ self.add_message('W0604', node=node) |
return |
module = frame.root() |
default_message = True |
for name in node.names: |
try: |
assign_nodes = module.getattr(name) |
- except astroid.NotFoundError: |
+ except astng.NotFoundError: |
# unassigned global, skip |
assign_nodes = [] |
for anode in assign_nodes: |
@@ -504,69 +311,23 @@ builtins. Remember that you should avoid to define new builtins when possible.' |
break |
else: |
# global but no assignment |
- # Detect imports in the current frame, with the required |
- # name. Such imports can be considered assignments. |
- imports = frame.nodes_of_class((astroid.Import, astroid.From)) |
- for import_node in imports: |
- found = False |
- for import_name, import_alias in import_node.names: |
- # If the import uses an alias, check only that. |
- # Otherwise, check only the import name. |
- if import_alias: |
- if import_alias == name: |
- found = True |
- break |
- elif import_name and import_name == name: |
- found = True |
- break |
- if found: |
- break |
- else: |
- self.add_message('global-variable-not-assigned', |
- args=name, node=node) |
+ self.add_message('W0602', args=name, node=node) |
default_message = False |
if not assign_nodes: |
continue |
for anode in assign_nodes: |
if anode.parent is None: |
- self.add_message('redefined-builtin', args=name, node=node) |
+ self.add_message('W0622', args=name, node=node) |
break |
if anode.frame() is module: |
# module level assignment |
break |
else: |
# global undefined at the module scope |
- self.add_message('global-variable-undefined', args=name, node=node) |
+ self.add_message('W0601', args=name, node=node) |
default_message = False |
if default_message: |
- self.add_message('global-statement', node=node) |
- |
- def _check_late_binding_closure(self, node, assignment_node, scope_type): |
- def _is_direct_lambda_call(): |
- return (isinstance(node_scope.parent, astroid.CallFunc) |
- and node_scope.parent.func is node_scope) |
- |
- node_scope = node.scope() |
- if not isinstance(node_scope, (astroid.Lambda, astroid.Function)): |
- return |
- if isinstance(node.parent, astroid.Arguments): |
- return |
- |
- if isinstance(assignment_node, astroid.Comprehension): |
- if assignment_node.parent.parent_of(node.scope()): |
- self.add_message('cell-var-from-loop', node=node, args=node.name) |
- else: |
- assign_scope = assignment_node.scope() |
- maybe_for = assignment_node |
- while not isinstance(maybe_for, astroid.For): |
- if maybe_for is assign_scope: |
- break |
- maybe_for = maybe_for.parent |
- else: |
- if (maybe_for.parent_of(node_scope) |
- and not _is_direct_lambda_call() |
- and not isinstance(node_scope.statement(), astroid.Return)): |
- self.add_message('cell-var-from-loop', node=node, args=node.name) |
+ self.add_message('W0603', node=node) |
def _loopvar_name(self, node, name): |
# filter variables according to node's scope |
@@ -576,7 +337,7 @@ builtins. Remember that you should avoid to define new builtins when possible.' |
#astmts = [stmt for stmt in node.lookup(name)[1] |
# if hasattr(stmt, 'ass_type')] and |
# not stmt.statement().parent_of(node)] |
- if not self.linter.is_message_enabled('undefined-loop-variable'): |
+ if 'W0631' not in self.active_msgs: |
return |
astmts = [stmt for stmt in node.lookup(name)[1] |
if hasattr(stmt, 'ass_type')] |
@@ -594,38 +355,35 @@ builtins. Remember that you should avoid to define new builtins when possible.' |
_astmts = astmts[:1] |
for i, stmt in enumerate(astmts[1:]): |
if (astmts[i].statement().parent_of(stmt) |
- and not in_for_else_branch(astmts[i].statement(), stmt)): |
+ and not in_for_else_branch(astmts[i].statement(), stmt)): |
continue |
_astmts.append(stmt) |
astmts = _astmts |
if len(astmts) == 1: |
ass = astmts[0].ass_type() |
- if isinstance(ass, (astroid.For, astroid.Comprehension, astroid.GenExpr)) \ |
+ if isinstance(ass, (astng.For, astng.Comprehension, astng.GenExpr)) \ |
and not ass.statement() is node.statement(): |
- self.add_message('undefined-loop-variable', args=name, node=node) |
+ self.add_message('W0631', args=name, node=node) |
- @check_messages('redefine-in-handler') |
def visit_excepthandler(self, node): |
- for name in get_all_elements(node.name): |
- clobbering, args = clobber_in_except(name) |
- if clobbering: |
- self.add_message('redefine-in-handler', args=args, node=name) |
+ clobbering, args = clobber_in_except(node.name) |
+ if clobbering: |
+ self.add_message('W0623', args=args, node=node) |
def visit_assname(self, node): |
- if isinstance(node.ass_type(), astroid.AugAssign): |
+ if isinstance(node.ass_type(), astng.AugAssign): |
self.visit_name(node) |
def visit_delname(self, node): |
self.visit_name(node) |
- @check_messages(*(MSGS.keys())) |
def visit_name(self, node): |
"""check that a name is defined if the current scope and doesn't |
redefine a built-in |
""" |
stmt = node.statement() |
if stmt.fromlineno is None: |
- # name node from a astroid built from live code, skip |
+ # name node from a astng built from live code, skip |
assert not stmt.root().file.endswith('.py') |
return |
name = node.name |
@@ -634,7 +392,7 @@ builtins. Remember that you should avoid to define new builtins when possible.' |
# a decorator, then start from the parent frame of the function instead |
# of the function frame - and thus open an inner class scope |
if (is_func_default(node) or is_func_decorator(node) |
- or is_ancestor_name(frame, node)): |
+ or is_ancestor_name(frame, node)): |
start_index = len(self._to_consume) - 2 |
else: |
start_index = len(self._to_consume) - 1 |
@@ -648,21 +406,12 @@ builtins. Remember that you should avoid to define new builtins when possible.' |
# names. The only exception is when the starting scope is a |
# comprehension and its direct outer scope is a class |
if scope_type == 'class' and i != start_index and not ( |
- base_scope_type == 'comprehension' and i == start_index-1): |
- # Detect if we are in a local class scope, as an assignment. |
- # For example, the following is fair game. |
- # class A: |
- # b = 1 |
- # c = lambda b=b: b * b |
- class_assignment = (isinstance(frame, astroid.Class) and |
- name in frame.locals) |
- if not class_assignment: |
- continue |
+ base_scope_type == 'comprehension' and i == start_index-1): |
+ # XXX find a way to handle class scope in a smoother way |
+ continue |
# the name has already been consumed, only check it's not a loop |
# variable used outside the loop |
if name in consumed: |
- defnode = assign_parent(consumed[name][0]) |
- self._check_late_binding_closure(node, defnode, scope_type) |
self._loopvar_name(node, name) |
break |
# mark the name as consumed if it's defined in this scope |
@@ -674,12 +423,11 @@ builtins. Remember that you should avoid to define new builtins when possible.' |
# checks for use before assignment |
defnode = assign_parent(to_consume[name][0]) |
if defnode is not None: |
- self._check_late_binding_closure(node, defnode, scope_type) |
defstmt = defnode.statement() |
defframe = defstmt.frame() |
maybee0601 = True |
if not frame is defframe: |
- maybee0601 = _detect_global_scope(node, frame, defframe) |
+ maybee0601 = False |
elif defframe.parent is None: |
# we are at the module level, check the name is not |
# defined in builtins |
@@ -690,82 +438,49 @@ builtins. Remember that you should avoid to define new builtins when possible.' |
# defined in global or builtin scope |
if defframe.root().lookup(name)[1]: |
maybee0601 = False |
- else: |
- # check if we have a nonlocal |
- if name in defframe.locals: |
- maybee0601 = not any(isinstance(child, astroid.Nonlocal) |
- and name in child.names |
- for child in defframe.get_children()) |
- if (self._to_consume[-1][-1] == 'lambda' and |
- isinstance(frame, astroid.Class) |
- and name in frame.locals): |
- maybee0601 = True |
- else: |
- maybee0601 = maybee0601 and stmt.fromlineno <= defstmt.fromlineno |
- |
if (maybee0601 |
- and not is_defined_before(node) |
- and not are_exclusive(stmt, defstmt, ('NameError', |
- 'Exception', |
- 'BaseException'))): |
- if defstmt is stmt and isinstance(node, (astroid.DelName, |
- astroid.AssName)): |
- self.add_message('undefined-variable', args=name, node=node) |
+ and stmt.fromlineno <= defstmt.fromlineno |
+ and not is_defined_before(node) |
+ and not are_exclusive(stmt, defstmt, ('NameError', 'Exception', 'BaseException'))): |
+ if defstmt is stmt and isinstance(node, (astng.DelName, |
+ astng.AssName)): |
+ self.add_message('E0602', args=name, node=node) |
elif self._to_consume[-1][-1] != 'lambda': |
- # E0601 may *not* occurs in lambda scope. |
- self.add_message('used-before-assignment', args=name, node=node) |
- elif self._to_consume[-1][-1] == 'lambda': |
- # E0601 can occur in class-level scope in lambdas, as in |
- # the following example: |
- # class A: |
- # x = lambda attr: f + attr |
- # f = 42 |
- if isinstance(frame, astroid.Class) and name in frame.locals: |
- if isinstance(node.parent, astroid.Arguments): |
- # Doing the following is fine: |
- # class A: |
- # x = 42 |
- # y = lambda attr=x: attr |
- if stmt.fromlineno <= defstmt.fromlineno: |
- self.add_message('used-before-assignment', |
- args=name, node=node) |
- else: |
- self.add_message('undefined-variable', |
- args=name, node=node) |
- |
- if isinstance(node, astroid.AssName): # Aug AssName |
- del consumed[name] |
- else: |
+ # E0601 may *not* occurs in lambda scope |
+ self.add_message('E0601', args=name, node=node) |
+ if not isinstance(node, astng.AssName): # Aug AssName |
del to_consume[name] |
+ else: |
+ del consumed[name] |
# check it's not a loop variable used outside the loop |
self._loopvar_name(node, name) |
break |
else: |
# we have not found the name, if it isn't a builtin, that's an |
# undefined name ! |
- if not (name in astroid.Module.scope_attrs or is_builtin(name) |
+ if not (name in astng.Module.scope_attrs or is_builtin(name) |
or name in self.config.additional_builtins): |
- self.add_message('undefined-variable', args=name, node=node) |
+ self.add_message('E0602', args=name, node=node) |
- @check_messages('no-name-in-module') |
+ @check_messages('E0611') |
def visit_import(self, node): |
"""check modules attribute accesses""" |
for name, _ in node.names: |
parts = name.split('.') |
try: |
module = node.infer_name_module(parts[0]).next() |
- except astroid.ResolveError: |
+ except astng.ResolveError: |
continue |
self._check_module_attrs(node, module, parts[1:]) |
- @check_messages('no-name-in-module') |
+ @check_messages('E0611') |
def visit_from(self, node): |
"""check modules attribute accesses""" |
name_parts = node.modname.split('.') |
level = getattr(node, 'level', None) |
try: |
module = node.root().import_module(name_parts[0], level=level) |
- except AstroidBuildingException: |
+ except ASTNGBuildingException: |
return |
except Exception, exc: |
print 'Unhandled exception in VariablesChecker:', exc |
@@ -778,63 +493,12 @@ builtins. Remember that you should avoid to define new builtins when possible.' |
continue |
self._check_module_attrs(node, module, name.split('.')) |
- @check_messages('unbalanced-tuple-unpacking', 'unpacking-non-sequence') |
- def visit_assign(self, node): |
- """Check unbalanced tuple unpacking for assignments |
- and unpacking non-sequences. |
- """ |
- if not isinstance(node.targets[0], (astroid.Tuple, astroid.List)): |
- return |
- |
- targets = node.targets[0].itered() |
- try: |
- for infered in node.value.infer(): |
- self._check_unpacking(infered, node, targets) |
- except astroid.InferenceError: |
- return |
- |
- def _check_unpacking(self, infered, node, targets): |
- """ Check for unbalanced tuple unpacking |
- and unpacking non sequences. |
- """ |
- if infered is astroid.YES: |
- return |
- if isinstance(infered, (astroid.Tuple, astroid.List)): |
- # attempt to check unpacking is properly balanced |
- values = infered.itered() |
- if len(targets) != len(values): |
- # Check if we have starred nodes. |
- if any(isinstance(target, astroid.Starred) |
- for target in targets): |
- return |
- self.add_message('unbalanced-tuple-unpacking', node=node, |
- args=(_get_unpacking_extra_info(node, infered), |
- len(targets), |
- len(values))) |
- # attempt to check unpacking may be possible (ie RHS is iterable) |
- elif isinstance(infered, astroid.Instance): |
- for meth in ('__iter__', '__getitem__'): |
- try: |
- infered.getattr(meth) |
- break |
- except astroid.NotFoundError: |
- continue |
- else: |
- self.add_message('unpacking-non-sequence', node=node, |
- args=(_get_unpacking_extra_info(node, infered),)) |
- else: |
- self.add_message('unpacking-non-sequence', node=node, |
- args=(_get_unpacking_extra_info(node, infered),)) |
- |
- |
def _check_module_attrs(self, node, module, module_names): |
"""check that module_names (list of string) are accessible through the |
given module |
if the latest access name corresponds to a module, return it |
""" |
- assert isinstance(module, astroid.Module), module |
- ignored_modules = get_global_option(self, 'ignored-modules', |
- default=[]) |
+ assert isinstance(module, astng.Module), module |
while module_names: |
name = module_names.pop(0) |
if name == '__dict__': |
@@ -842,24 +506,21 @@ builtins. Remember that you should avoid to define new builtins when possible.' |
break |
try: |
module = module.getattr(name)[0].infer().next() |
- if module is astroid.YES: |
- return None |
- except astroid.NotFoundError: |
- if module.name in ignored_modules: |
+ if module is astng.YES: |
return None |
- self.add_message('no-name-in-module', |
- args=(name, module.name), node=node) |
+ except astng.NotFoundError: |
+ self.add_message('E0611', args=(name, module.name), node=node) |
return None |
- except astroid.InferenceError: |
+ except astng.InferenceError: |
return None |
if module_names: |
# FIXME: other message if name is not the latest part of |
# module_names ? |
modname = module and module.name or '__dict__' |
- self.add_message('no-name-in-module', node=node, |
+ self.add_message('E0611', node=node, |
args=('.'.join(module_names), modname)) |
return None |
- if isinstance(module, astroid.Module): |
+ if isinstance(module, astng.Module): |
return module |
return None |
@@ -879,57 +540,6 @@ class VariablesChecker3k(VariablesChecker): |
# do not check for not used locals here |
self._to_consume.pop() |
- def leave_module(self, node): |
- """ Update consumption analysis variable |
- for metaclasses. |
- """ |
- module_locals = self._to_consume[0][0] |
- module_imports = self._to_consume[0][1] |
- consumed = {} |
- |
- for klass in node.nodes_of_class(astroid.Class): |
- found = metaclass = name = None |
- if not klass._metaclass: |
- # Skip if this class doesn't use |
- # explictly a metaclass, but inherits it from ancestors |
- continue |
- |
- metaclass = klass.metaclass() |
- |
- # Look the name in the already found locals. |
- # If it's not found there, look in the module locals |
- # and in the imported modules. |
- if isinstance(klass._metaclass, astroid.Name): |
- name = klass._metaclass.name |
- elif metaclass: |
- # if it uses a `metaclass=module.Class` |
- name = metaclass.root().name |
- |
- if name: |
- found = consumed.setdefault( |
- name, module_locals.get(name, module_imports.get(name))) |
- |
- if found is None and not metaclass: |
- name = None |
- if isinstance(klass._metaclass, astroid.Name): |
- name = klass._metaclass.name |
- elif isinstance(klass._metaclass, astroid.Getattr): |
- name = klass._metaclass.as_string() |
- |
- if name is not None: |
- if not (name in astroid.Module.scope_attrs or |
- is_builtin(name) or |
- name in self.config.additional_builtins or |
- name in node.locals): |
- self.add_message('undefined-variable', |
- node=klass, |
- args=(name, )) |
- # Pop the consumed items, in order to |
- # avoid having unused-import false positives |
- for name in consumed: |
- module_locals.pop(name, None) |
- super(VariablesChecker3k, self).leave_module(node) |
- |
if sys.version_info >= (3, 0): |
VariablesChecker = VariablesChecker3k |