diff --git a/src/decompiler.rs b/src/decompiler.rs index 33a3465d..0586915f 100644 --- a/src/decompiler.rs +++ b/src/decompiler.rs @@ -19314,20 +19314,10 @@ let reopen = self return true; } let target = inst.target.unwrap_or(inst.end()); - // async for: the iterable expression is an Await, or we are - // inside an async def using GET_AITER - let is_async = matches!( - self.stack.last(), - Some(Sv::E(e)) if matches!(&**e, Expr::Await(_)) - ); - if is_async { - if let Some(Sv::E(e)) = self.stack.last_mut() { - if let Expr::Await(inner) = &**e { - *e = inner.clone(); - } - } - } - self.handle_for_iter(target, is_async); + // FOR_ITER iterates synchronously, even when the collection + // was produced by `await fetch()`. Genuine async iteration + // uses GET_AITER/GET_ANEXT and its separate decoder path. + self.handle_for_iter(target, false); true } Op::FOR_LOOP => { diff --git a/tests/behavior/cases/b_awaited_synchronous_iteration.py b/tests/behavior/cases/b_awaited_synchronous_iteration.py new file mode 100644 index 00000000..ccf15d1a --- /dev/null +++ b/tests/behavior/cases/b_awaited_synchronous_iteration.py @@ -0,0 +1,14 @@ +# MIN_VERSION: 3.9 +import asyncio + +async def awaited(read): + result = [] + for item in await read(): + result.append(item) + return result + + +async def read(): + print("read") + return [1, 2, 3] +print(asyncio.run(awaited(read))) diff --git a/tests/fixtures/awaited_synchronous_iteration.py b/tests/fixtures/awaited_synchronous_iteration.py new file mode 100644 index 00000000..6d5438be --- /dev/null +++ b/tests/fixtures/awaited_synchronous_iteration.py @@ -0,0 +1,23 @@ +# Versioned .pyc files are consumed by tests/integration.rs. + +# async/await syntax starts in Python 3.5. + +async def awaited(read): + result = [] + for item in await read(): + result.append(item) + return result + + +async def asynchronous(read): + result = [] + async for item in read(): + result.append(item) + return result + + +async def sync(read): + result = [] + for item in read(): + result.append(item) + return result diff --git a/tests/integration.rs b/tests/integration.rs index f4af5082..439d4cfc 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -169,3 +169,28 @@ fn py314_hello() { "py3.14 hello", ); } + +#[test] +fn awaited_collections_use_synchronous_iteration() { + for version in ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] { + let (src, clean) = decompile_fixture(&format!("awaited_synchronous_iteration.{version}.pyc")); + assert!(clean, "Python {version}: output marked incomplete:\n{src}"); + assert_contains(&src, &[ + r#"async def awaited(read): + result = [] + for item in await read(): + result.append(item) + return result"#, + r#"async def asynchronous(read): + result = [] + async for item in read(): + result.append(item) + return result"#, + r#"async def sync(read): + result = [] + for item in read(): + result.append(item) + return result"#, + ], version); + } +} diff --git a/tests/pyc/awaited_synchronous_iteration.3.10.pyc b/tests/pyc/awaited_synchronous_iteration.3.10.pyc new file mode 100644 index 00000000..9cc60d84 Binary files /dev/null and b/tests/pyc/awaited_synchronous_iteration.3.10.pyc differ diff --git a/tests/pyc/awaited_synchronous_iteration.3.11.pyc b/tests/pyc/awaited_synchronous_iteration.3.11.pyc new file mode 100644 index 00000000..866f96b3 Binary files /dev/null and b/tests/pyc/awaited_synchronous_iteration.3.11.pyc differ diff --git a/tests/pyc/awaited_synchronous_iteration.3.12.pyc b/tests/pyc/awaited_synchronous_iteration.3.12.pyc new file mode 100644 index 00000000..2d87c32b Binary files /dev/null and b/tests/pyc/awaited_synchronous_iteration.3.12.pyc differ diff --git a/tests/pyc/awaited_synchronous_iteration.3.13.pyc b/tests/pyc/awaited_synchronous_iteration.3.13.pyc new file mode 100644 index 00000000..882a80e5 Binary files /dev/null and b/tests/pyc/awaited_synchronous_iteration.3.13.pyc differ diff --git a/tests/pyc/awaited_synchronous_iteration.3.14.pyc b/tests/pyc/awaited_synchronous_iteration.3.14.pyc new file mode 100644 index 00000000..fb7bb10b Binary files /dev/null and b/tests/pyc/awaited_synchronous_iteration.3.14.pyc differ diff --git a/tests/pyc/awaited_synchronous_iteration.3.8.pyc b/tests/pyc/awaited_synchronous_iteration.3.8.pyc new file mode 100644 index 00000000..f88c010c Binary files /dev/null and b/tests/pyc/awaited_synchronous_iteration.3.8.pyc differ diff --git a/tests/pyc/awaited_synchronous_iteration.3.9.pyc b/tests/pyc/awaited_synchronous_iteration.3.9.pyc new file mode 100644 index 00000000..1adf702d Binary files /dev/null and b/tests/pyc/awaited_synchronous_iteration.3.9.pyc differ