Skip to content

GH-128534: Fix behavior of branch monitoring for async for #130847

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Mar 7, 2025
5 changes: 3 additions & 2 deletions Include/internal/pycore_magic_number.h
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,8 @@ Known values:
Python 3.14a5 3615 (CALL_FUNCTION_EX always take a kwargs argument)
Python 3.14a5 3616 (Remove BINARY_SUBSCR and family. Make them BINARY_OPs)
Python 3.14a6 3617 (Branch monitoring for async for loops)
Python 3.14a6 3618 (Renumber RESUME opcode from 149 to 128)
Python 3.14a6 3618 (Add oparg to END_ASYNC_FOR)
Python 3.14a6 3619 (Renumber RESUME opcode from 149 to 128)

Python 3.15 will start with 3650

Expand All @@ -283,7 +284,7 @@ PC/launcher.c must also be updated.

*/

#define PYC_MAGIC_NUMBER 3618
#define PYC_MAGIC_NUMBER 3619
/* This is equivalent to converting PYC_MAGIC_NUMBER to 2 bytes
(little-endian) and then appending b'\r\n'. */
#define PYC_MAGIC_NUMBER_TOKEN \
Expand Down
4 changes: 2 additions & 2 deletions Include/internal/pycore_opcode_metadata.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

118 changes: 59 additions & 59 deletions Include/opcode_ids.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

118 changes: 59 additions & 59 deletions Lib/_opcode_metadata.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions Lib/dis.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
STORE_FAST_STORE_FAST = opmap['STORE_FAST_STORE_FAST']
IS_OP = opmap['IS_OP']
CONTAINS_OP = opmap['CONTAINS_OP']
END_ASYNC_FOR = opmap['END_ASYNC_FOR']

CACHE = opmap["CACHE"]

Expand Down Expand Up @@ -605,7 +606,8 @@ def get_argval_argrepr(self, op, arg, offset):
argval = self.offset_from_jump_arg(op, arg, offset)
lbl = self.get_label_for_offset(argval)
assert lbl is not None
argrepr = f"to L{lbl}"
preposition = "from" if deop == END_ASYNC_FOR else "to"
argrepr = f"{preposition} L{lbl}"
elif deop in (LOAD_FAST_LOAD_FAST, STORE_FAST_LOAD_FAST, STORE_FAST_STORE_FAST):
arg1 = arg >> 4
arg2 = arg & 15
Expand Down Expand Up @@ -745,7 +747,8 @@ def _parse_exception_table(code):

def _is_backward_jump(op):
return opname[op] in ('JUMP_BACKWARD',
'JUMP_BACKWARD_NO_INTERRUPT')
'JUMP_BACKWARD_NO_INTERRUPT',
'END_ASYNC_FOR') # Not really a jump, but it has a "target"

def _get_instructions_bytes(code, linestarts=None, line_offset=0, co_positions=None,
original_code=None, arg_resolver=None):
Expand Down
9 changes: 9 additions & 0 deletions Lib/test/test_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -953,6 +953,15 @@ def with_extended_args(x):
get_line_branches(with_extended_args),
[(1,2,8)])

async def afunc():
async for letter in async_iter1:
2
3

self.assertEqual(
get_line_branches(afunc),
[(1,1,3)])

if check_impl_detail(cpython=True) and ctypes is not None:
py = ctypes.pythonapi
freefunc = ctypes.CFUNCTYPE(None,ctypes.c_voidp)
Expand Down
17 changes: 17 additions & 0 deletions Lib/test/test_dis.py
Original file line number Diff line number Diff line change
Expand Up @@ -1256,6 +1256,23 @@ def test__try_compile_no_context_exc_on_error(self):
except Exception as e:
self.assertIsNone(e.__context__)

def test_async_for_presentation(self):

async def afunc():
async for letter in async_iter1:
l2
l3

disassembly = self.get_disassembly(afunc)
for line in disassembly.split("\n"):
if "END_ASYNC_FOR" in line:
break
else:
self.fail("No END_ASYNC_FOR in disassembly of async for")
self.assertNotIn("to", line)
self.assertIn("from", line)


@staticmethod
def code_quicken(f):
_testinternalcapi = import_helper.import_module("_testinternalcapi")
Expand Down
Loading
Loading