summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Vandenberg <jayvdb@gmail.com>2015-11-16 23:08:12 +1100
committerJohn Vandenberg <jayvdb@gmail.com>2015-11-20 02:12:43 +1100
commit265766da8921620b44752bdc6ba95b3775f0390b (patch)
treeb76a3ac7ea6b8d6b09f1d336a584fd8d5531743f
parent3088ffbd6256521e0213b361bc2294c1e218e6fb (diff)
downloadpyflakes-265766da8921620b44752bdc6ba95b3775f0390b.tar.gz
Fix undefined name in generators in class
8c8a27b8 provided a partial solution for generators in the class scope, however it did not account for generators enclosing other generators.
-rw-r--r--pyflakes/checker.py27
-rw-r--r--pyflakes/test/test_undefined_names.py25
2 files changed, 36 insertions, 16 deletions
diff --git a/pyflakes/checker.py b/pyflakes/checker.py
index f538d3f..ddaf86e 100644
--- a/pyflakes/checker.py
+++ b/pyflakes/checker.py
@@ -480,23 +480,17 @@ class Checker(object):
name = getNodeName(node)
if not name:
return
- # try local scope
- try:
- self.scope[name].used = (self.scope, node)
- except KeyError:
- pass
- else:
- return
- scopes = [scope for scope in self.scopeStack[:-1]
- if isinstance(scope, (FunctionScope, ModuleScope, GeneratorScope))]
- if isinstance(self.scope, GeneratorScope) and scopes[-1] != self.scopeStack[-2]:
- scopes.append(self.scopeStack[-2])
+ in_generators = None
+ importStarred = None
# try enclosing function scopes and global scope
- importStarred = self.scope.importStarred
- for scope in reversed(scopes):
- importStarred = importStarred or scope.importStarred
+ for scope in self.scopeStack[-1::-1]:
+ # only generators used in a class scope can access the names
+ # of the class. this is skipped during the first iteration
+ if in_generators is False and isinstance(scope, ClassScope):
+ continue
+
try:
scope[name].used = (self.scope, node)
except KeyError:
@@ -504,6 +498,11 @@ class Checker(object):
else:
return
+ importStarred = importStarred or scope.importStarred
+
+ if in_generators is not False:
+ in_generators = isinstance(scope, GeneratorScope)
+
# look in the built-ins
if importStarred or name in self.builtIns:
return
diff --git a/pyflakes/test/test_undefined_names.py b/pyflakes/test/test_undefined_names.py
index 54af0eb..81dab13 100644
--- a/pyflakes/test/test_undefined_names.py
+++ b/pyflakes/test/test_undefined_names.py
@@ -481,8 +481,20 @@ class Test(TestCase):
Using the loop variable of a generator expression results in no
warnings.
"""
- self.flakes('(a for a in %srange(10) if a)' %
- ('x' if version_info < (3,) else ''))
+ self.flakes('(a for a in [1, 2, 3] if a)')
+
+ self.flakes('(b for b in (a for a in [1, 2, 3] if a) if b)')
+
+ def test_undefinedInGenExpNested(self):
+ """
+ The loop variables of generator expressions nested together are
+ not defined in the other generator.
+ """
+ self.flakes('(b for b in (a for a in [1, 2, 3] if b) if b)',
+ m.UndefinedName)
+
+ self.flakes('(b for b in (a for a in [1, 2, 3] if a) if a)',
+ m.UndefinedName)
def test_undefinedWithErrorHandler(self):
"""
@@ -537,6 +549,15 @@ class Test(TestCase):
Y = {x:x for x in T}
''')
+ def test_definedInClassNested(self):
+ """Defined name for nested generator expressions in a class."""
+ self.flakes('''
+ class A:
+ T = range(10)
+
+ Z = (x for x in (a for a in T))
+ ''')
+
def test_undefinedInLoop(self):
"""
The loop variable is defined after the expression is computed.