Skip to content
This repository was archived by the owner on Mar 2, 2022. It is now read-only.

Commit 00f9478

Browse files
committed
Match async get iter code generation
Summary: 3.7 no longer emits this random LOAD_CONST/YIELD_FROM pair - this is beacuse Python 3.7 drops support for asynchronous __aiter__: https://bugs.python.org/issue31709 Test Plan: ./python -m test.test_compiler
1 parent 977a699 commit 00f9478

File tree

3 files changed

+55
-6
lines changed

3 files changed

+55
-6
lines changed

compiler/pycodegen.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,10 @@ def visitFor(self, node):
555555
self.visit(node.orelse)
556556
self.nextBlock(after)
557557

558+
def emitAsyncIterYieldFrom(self):
559+
self.emit('LOAD_CONST', None)
560+
self.emit('YIELD_FROM')
561+
558562
def visitAsyncFor(self, node):
559563
try_ = self.newBlock('async_for_try')
560564
except_ = self.newBlock('except')
@@ -570,8 +574,7 @@ def visitAsyncFor(self, node):
570574

571575
self.visit(node.iter)
572576
self.emit('GET_AITER')
573-
self.emit('LOAD_CONST', None)
574-
self.emit('YIELD_FROM')
577+
self.emitAsyncIterYieldFrom()
575578

576579
self.nextBlock(try_)
577580

@@ -801,8 +804,7 @@ class Holder: pass
801804
self.visit(node.generators[0].iter)
802805
if node.generators[0].is_async:
803806
self.emit('GET_AITER')
804-
self.emit('LOAD_CONST', None)
805-
self.emit('YIELD_FROM')
807+
self.emitAsyncIterYieldFrom()
806808
else:
807809
self.emit('GET_ITER')
808810
self.emit('CALL_FUNCTION', 1)
@@ -844,8 +846,7 @@ def compile_async_comprehension(self, generators, gen_index, elt, val, type):
844846
else:
845847
self.visit(gen.iter)
846848
self.emit('GET_AITER')
847-
self.emit('LOAD_CONST', None)
848-
self.emit('YIELD_FROM')
849+
self.emitAsyncIterYieldFrom()
849850

850851
self.nextBlock(try_)
851852
self.emit('SETUP_EXCEPT', except_)
@@ -2081,6 +2082,15 @@ def visitConstant(self, node: ast.Constant):
20812082
self.update_lineno(node)
20822083
self.emit('LOAD_CONST', node.value)
20832084

2085+
def emitAsyncIterYieldFrom(self):
2086+
pass
2087+
2088+
def visitAsyncWith(self, node):
2089+
if not self.scope.coroutine:
2090+
raise SyntaxError("'async with' outside async function", self.syntax_error_position(node))
2091+
2092+
return super().visitAsyncWith(node)
2093+
20842094

20852095
def get_default_generator():
20862096
if sys.version_info >= (3, 7):

test_compiler/test_errors.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import ast
22
import dis
33
import inspect
4+
import sys
45
import unittest
56
from unittest import TestCase
67
from .common import CompilerTest
@@ -51,6 +52,21 @@ def test_return_outside_func_class(self):
5152
class C:
5253
return""")
5354

55+
@unittest.skipIf(sys.version_info < (3, 7), "parse fails on 3.6")
56+
def test_async_with_outside_async_func(self):
57+
with self.assertRaisesRegex(SyntaxError, "'async with' outside async function"):
58+
self.compile("""
59+
def f():
60+
async with x:
61+
pass""")
62+
63+
@unittest.skipIf(sys.version_info < (3, 7), "parse fails on 3.6")
64+
def test_async_with_outside_func(self):
65+
with self.assertRaisesRegex(SyntaxError, "'async with' outside async function"):
66+
self.compile("""
67+
async with x:
68+
pass""")
69+
5470
def test_return_with_value_async_gen(self):
5571
with self.assertRaisesRegex(
5672
SyntaxError, "'return' with value in async generator"

test_compiler/test_py37.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import ast
22
import dis
33
from .common import CompilerTest
4+
from compiler.pyassem import PyFlowGraph
45
from compiler.pycodegen import CodeGenerator, Python37CodeGenerator
56
from compiler.optimizer import AstOptimizer
67
from compiler.unparse import to_expr
@@ -60,6 +61,28 @@ def test_future_annotations_flag(self):
6061
CO_NOFREE | CO_FUTURE_ANNOTATIONS,
6162
)
6263

64+
def test_async_aiter(self):
65+
# Make sure GET_AITER isn't followed by LOAD_CONST None as Python 3.7 doesn't support async __aiter__
66+
for generator in (Python37CodeGenerator, CodeGenerator):
67+
outer_graph = self.to_graph("""
68+
async def f():
69+
async for x in y:
70+
pass
71+
""", generator)
72+
for outer_instr in self.graph_to_instrs(outer_graph):
73+
if outer_instr.opname == "LOAD_CONST" and isinstance(outer_instr.oparg, CodeGenerator):
74+
saw_aiter = False
75+
for instr in self.graph_to_instrs(outer_instr.oparg.graph):
76+
if saw_aiter:
77+
self.assertEqual(instr.opname == "LOAD_CONST", generator is CodeGenerator)
78+
if generator is CodeGenerator:
79+
self.assertIsNone(instr.oparg)
80+
break
81+
82+
if instr.opname == "GET_AITER":
83+
saw_aiter = True
84+
break
85+
6386
def test_future_annotations(self):
6487
annotations = ["42"]
6588
for annotation in annotations:

0 commit comments

Comments
 (0)