Transform ID:Documentation Index
Fetch the complete documentation index at: https://docs.refactron.dev/llms.txt
Use this file to discover all available pages before exploring further.
yield_from_for_loop
Language: Python
What it does
Rewrites a generator loop that does nothing but forward each value (for x in y: yield x) to the equivalent yield from y. Both the indented-block and one-line for-suite shapes are recognised, and the rewrite preserves leading comments above the for and any trailing comment on the yield line. After the rewrite, the sidecar does a defensive cst.parse_module on the output and refuses (rewrite_unparseable) rather than emit broken source. No version gate is needed — yield from has been available since Python 3.3. Sidecar at src/transform/transforms/python/_py/yield_from_for_loop.py.
Detector pattern
The detector atsrc/analyze/detectors/python/yield-in-trivial-loop.ts walks for statements whose target is a single identifier and whose body is exactly one statement, a yield of that same identifier — with no else: branch.
Preconditions
- The loop target is a single
Name(no tuple targets likefor x, y in z:). - The loop body is exactly one
Expr(Yield(<loop-var>))— no additional statements, no print/log calls alongside the yield. - No
else:branch. Python’sfor/elseclause runs on loop completion-without-break;yield fromdoes not preserve that semantic. - The expression is not already
yield from <iter>(idempotent — no-op rewrite is skipped cleanly). - The enclosing function must not be
async def. Inside anasync def,for x in y: yield xmakes the function an async generator (PEP 525).yield fromis a SyntaxError inside async generators — and crucially, CPython enforces this at the compile stage, not in the grammar, so LibCST’s parser will happily accept the broken output. The sidecar tracksasync defdepth and refuses any rewrite under one. rewrite_unparseable— defensive: if the rewritten module failscst.parse_module, the transform reports rather than write corrupted source.
Before / after
Edge cases NOT handled (skip via precondition)
- Multi-statement loop body (
for x in seq: yield x; print(x)) — not a pure forward. for x in seq: yield x else: …—for/elsesemantics are not preserved byyield from.- Tuple targets (
for k, v in items: yield k) — yielded value isn’t the loop target verbatim. - The loop inside an
async def— would produce a SyntaxError CPython catches at compile time but LibCST’s parser does not. - A yielded expression that isn’t the bare loop variable (
for x in seq: yield f(x)) — the rewrite would change behaviour.
The
async def gate is the load-bearing safety check here. LibCST’s parser accepts yield from inside async def; the SyntaxError only fires when CPython compiles the module. Without an explicit gate the rewrite would silently produce code that crashes at import time.