Skip to content

Do not follow END_ASYNC_FOR's monitoring-only jump when computing stack size (Python 3.14) - #214

Merged
MatthieuDartiailh merged 2 commits into
MatthieuDartiailh:mainfrom
yuri12344:fix-end-async-for-stacksize-py314
Oct 2, 2026
Merged

MatthieuDartiailh merged 2 commits into
MatthieuDartiailh:mainfrom
yuri12344:fix-end-async-for-stacksize-py314

Conversation

@yuri12344

Copy link
Copy Markdown
Contributor

Problem

On Python 3.14, round-tripping code with two consecutive async for loops whose bodies contain a try/except fails during stack-size computation:

async def f(a, b):
    async for x in a:
        try: pass
        except Exception: pass
    async for y in b:
        try: pass
        except Exception: pass

Bytecode.from_code(f.__code__).to_code()
# 0.19.0: AssertionError at `assert self._current_try_begin is None` (cfg.py)
# 0.17.0: RuntimeError: Failed to compute stacksize, got negative size / KeyError

A single such loop works, and Python 3.11–3.13 are unaffected. We hit this in production through ddtrace's Code Origin for Spans, which rewrites functions with this library.

Root cause

Since 3.14, END_ASYNC_FOR has an oparg and is in opcode.hasjump, and since #179 bytecode treats it as a conditional backward jump. The interpreter never takes that jump: _END_ASYNC_FOR in Python/bytecodes.c does JUMPBY(0); // Pretend jump as we need source offset for monitoring. The oparg only lets INSTRUMENTED_END_ASYNC_FOR report the branch source to sys.monitoring. CPython's own stack-depth pass skips this edge (Python/flowgraph.c, calculate_stackdepth: HAS_TARGET(opcode) && opcode != END_ASYNC_FOR).

_StackSizeComputer followed the edge into the loop's SEND/END_SEND block at the wrong depth and with the enclosing TryBegin still open. For the second loop that block starts with another TryBegin, which trips the non-nesting assertion.

Fix

  • Add MONITORING_ONLY_JUMP_OPS ({END_ASYNC_FOR} on 3.14+, empty otherwise) in instr.py.
  • Skip the jump-taken exploration for these opcodes in _StackSizeComputer.run.
  • has_jump() and the other jump classifications are unchanged, because the target is still needed for labels and oparg encoding.

Tests

  • New test_stack_size_computation_consecutive_async_for_try_except. It fails without the fix on 3.14 and passes on 3.12, 3.13 and 3.14.
  • The full suite passes on 3.12, 3.13 and 3.14.
  • Computed stack sizes match co_stacksize for nested async for, async comprehensions, and async with + async for with try/finally.

yuri12344 pushed a commit to yuri12344/bytecode that referenced this pull request Oct 2, 2026
…ck size

On Python 3.14 END_ASYNC_FOR has a jump argument that only provides the
source offset for sys.monitoring: the interpreter never takes it
(JUMPBY(0) in Python/bytecodes.c) and CPython's calculate_stackdepth
skips this edge. Following it made the stack-size computation enter the
loop's SEND/END_SEND block with the enclosing TryBegin still open, which
raised an AssertionError for functions with several async for loops
containing try blocks.
@yuri12344
yuri12344 force-pushed the fix-end-async-for-stacksize-py314 branch from 21b0ddd to e5564e0 Compare October 2, 2026 10:46
@codecov-commenter

codecov-commenter commented Oct 2, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.57%. Comparing base (9eea626) to head (e5564e0).

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #214   +/-   ##
=======================================
  Coverage   95.57%   95.57%           
=======================================
  Files           7        7           
  Lines        2147     2148    +1     
  Branches      461      461           
=======================================
+ Hits         2052     2053    +1     
  Misses         53       53           
  Partials       42       42           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@MatthieuDartiailh MatthieuDartiailh left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the very well explained issue and patch. I will try to release next week.

@MatthieuDartiailh
MatthieuDartiailh merged commit 7ce253f into MatthieuDartiailh:main Oct 2, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants