Skip to content

Commit e02336c

Browse files
yannsartoriYann Sartori
andauthored
assignment expression in comprehension should target outer scope (#698)
Co-authored-by: Yann Sartori <[email protected]>
1 parent dd446ed commit e02336c

File tree

2 files changed

+33
-1
lines changed

2 files changed

+33
-1
lines changed

pyflakes/checker.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,12 @@ class Assignment(Binding):
540540
"""
541541

542542

543+
class NamedExprAssignment(Assignment):
544+
"""
545+
Represents binding a name with an assignment expression.
546+
"""
547+
548+
543549
class Annotation(Binding):
544550
"""
545551
Represents binding a name to a type without an associated value.
@@ -1159,7 +1165,14 @@ def addBinding(self, node, value):
11591165
# don't treat annotations as assignments if there is an existing value
11601166
# in scope
11611167
if value.name not in self.scope or not isinstance(value, Annotation):
1162-
self.scope[value.name] = value
1168+
cur_scope_pos = -1
1169+
# As per PEP 572, use scope in which outermost generator is defined
1170+
while (
1171+
isinstance(value, NamedExprAssignment) and
1172+
isinstance(self.scopeStack[cur_scope_pos], GeneratorScope)
1173+
):
1174+
cur_scope_pos -= 1
1175+
self.scopeStack[cur_scope_pos][value.name] = value
11631176

11641177
def _unknown_handler(self, node):
11651178
# this environment variable configures whether to error on unknown
@@ -1302,6 +1315,8 @@ def handleNodeStore(self, node):
13021315
binding = ExportBinding(name, node._pyflakes_parent, self.scope)
13031316
elif PY2 and isinstance(getattr(node, 'ctx', None), ast.Param):
13041317
binding = Argument(name, self.getScopeNode(node))
1318+
elif PY38_PLUS and isinstance(parent_stmt, ast.NamedExpr):
1319+
binding = NamedExprAssignment(name, node)
13051320
else:
13061321
binding = Assignment(name, node)
13071322
self.addBinding(node, binding)

pyflakes/test/test_other.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1772,6 +1772,23 @@ def test_assign_expr(self):
17721772
print(x)
17731773
''')
17741774

1775+
@skipIf(version_info < (3, 8), 'new in Python 3.8')
1776+
def test_assign_expr_generator_scope(self):
1777+
"""Test assignment expressions in generator expressions."""
1778+
self.flakes('''
1779+
if (any((y := x[0]) for x in [[True]])):
1780+
print(y)
1781+
''')
1782+
1783+
@skipIf(version_info < (3, 8), 'new in Python 3.8')
1784+
def test_assign_expr_nested(self):
1785+
"""Test assignment expressions in nested expressions."""
1786+
self.flakes('''
1787+
if ([(y:=x) for x in range(4) if [(z:=q) for q in range(4)]]):
1788+
print(y)
1789+
print(z)
1790+
''')
1791+
17751792

17761793
class TestStringFormatting(TestCase):
17771794

0 commit comments

Comments
 (0)