Skip to content

Commit e6a91ae

Browse files
committed
Fix branch monitoring for . Both branches in a pair now have a common source and are included in co_branches
1 parent b3c18bf commit e6a91ae

File tree

13 files changed

+197
-140
lines changed

13 files changed

+197
-140
lines changed

Include/internal/pycore_opcode_metadata.h

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/opcode_ids.h

Lines changed: 59 additions & 59 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/_opcode_metadata.py

Lines changed: 59 additions & 59 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/test/test_code.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -936,6 +936,15 @@ def with_extended_args(x):
936936
get_line_branches(with_extended_args),
937937
[(1,2,8)])
938938

939+
async def afunc():
940+
async for letter in async_iter1:
941+
2
942+
3
943+
944+
self.assertEqual(
945+
get_line_branches(afunc),
946+
[(1,2,3)])
947+
939948
if check_impl_detail(cpython=True) and ctypes is not None:
940949
py = ctypes.pythonapi
941950
freefunc = ctypes.CFUNCTYPE(None,ctypes.c_voidp)

Lib/test/test_monitoring.py

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1683,7 +1683,9 @@ async def foo():
16831683

16841684
class TestBranchConsistency(MonitoringTestBase, unittest.TestCase):
16851685

1686-
def check_branches(self, func, tool=TEST_TOOL, recorders=BRANCH_OFFSET_RECORDERS):
1686+
def check_branches(self, run_func, test_func=None, tool=TEST_TOOL, recorders=BRANCH_OFFSET_RECORDERS):
1687+
if test_func is None:
1688+
test_func = run_func
16871689
try:
16881690
self.assertEqual(sys.monitoring._all_events(), {})
16891691
event_list = []
@@ -1692,25 +1694,26 @@ def check_branches(self, func, tool=TEST_TOOL, recorders=BRANCH_OFFSET_RECORDERS
16921694
ev = recorder.event_type
16931695
sys.monitoring.register_callback(tool, ev, recorder(event_list))
16941696
all_events |= ev
1695-
sys.monitoring.set_local_events(tool, func.__code__, all_events)
1696-
func()
1697-
sys.monitoring.set_local_events(tool, func.__code__, 0)
1697+
sys.monitoring.set_local_events(tool, test_func.__code__, all_events)
1698+
run_func()
1699+
sys.monitoring.set_local_events(tool, test_func.__code__, 0)
16981700
for recorder in recorders:
16991701
sys.monitoring.register_callback(tool, recorder.event_type, None)
17001702
lefts = set()
17011703
rights = set()
1702-
for (src, left, right) in func.__code__.co_branches():
1704+
for (src, left, right) in test_func.__code__.co_branches():
17031705
lefts.add((src, left))
17041706
rights.add((src, right))
1707+
print(event_list)
17051708
for event in event_list:
1706-
way, _, src, dest = event
1709+
way, name, src, dest = event
17071710
if "left" in way:
17081711
self.assertIn((src, dest), lefts)
17091712
else:
17101713
self.assertIn("right", way)
17111714
self.assertIn((src, dest), rights)
17121715
finally:
1713-
sys.monitoring.set_local_events(tool, func.__code__, 0)
1716+
sys.monitoring.set_local_events(tool, test_func.__code__, 0)
17141717
for recorder in recorders:
17151718
sys.monitoring.register_callback(tool, recorder.event_type, None)
17161719

@@ -1762,6 +1765,26 @@ def foo(n=0):
17621765

17631766
self.check_branches(foo)
17641767

1768+
def test_async_for(self):
1769+
1770+
async def gen():
1771+
yield 2
1772+
yield 3
1773+
1774+
async def foo():
1775+
async for y in gen():
1776+
2
1777+
pass # line 3
1778+
1779+
def func():
1780+
try:
1781+
foo().send(None)
1782+
except StopIteration:
1783+
pass
1784+
1785+
print(list(foo.__code__.co_branches()))
1786+
self.check_branches(func, foo)
1787+
17651788

17661789
class TestLoadSuperAttr(CheckEvents):
17671790
RECORDERS = CallRecorder, LineRecorder, CRaiseRecorder, CReturnRecorder

Python/assemble.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,10 @@ makecode(_PyCompile_CodeUnitMetadata *umd, struct assembler *a, PyObject *const_
632632
return co;
633633
}
634634

635+
636+
// The offset (in code units) of the END_SEND from the SEND in the `yield from` sequence.
637+
#define END_SEND_OFFSET 5
638+
635639
static int
636640
resolve_jump_offsets(instr_sequence *instrs)
637641
{
@@ -670,7 +674,11 @@ resolve_jump_offsets(instr_sequence *instrs)
670674
if (OPCODE_HAS_JUMP(instr->i_opcode)) {
671675
instruction *target = &instrs->s_instrs[instr->i_target];
672676
instr->i_oparg = target->i_offset;
673-
if (instr->i_oparg < offset) {
677+
if (instr->i_opcode == END_ASYNC_FOR) {
678+
// Adjust the offset so that target is the END_SEND
679+
instr->i_oparg = offset - instr->i_oparg - END_SEND_OFFSET;
680+
}
681+
else if (instr->i_oparg < offset) {
674682
assert(IS_BACKWARDS_JUMP_OPCODE(instr->i_opcode));
675683
instr->i_oparg = offset - instr->i_oparg;
676684
}

0 commit comments

Comments
 (0)