diff --git a/mypyc/lib-rt/misc_ops.c b/mypyc/lib-rt/misc_ops.c index e5f51b4e3c44..870cd4682ea7 100644 --- a/mypyc/lib-rt/misc_ops.c +++ b/mypyc/lib-rt/misc_ops.c @@ -1032,7 +1032,6 @@ PyObject *CPy_GetAIter(PyObject *obj) "'async for' requires an object with " "__aiter__ method, got %.100s", type->tp_name); - Py_DECREF(obj); return NULL; } diff --git a/mypyc/test-data/run-async.test b/mypyc/test-data/run-async.test index ba45eda60dcb..f0170eddcb1a 100644 --- a/mypyc/test-data/run-async.test +++ b/mypyc/test-data/run-async.test @@ -485,8 +485,10 @@ async def sleep(t: float) -> None: ... import asyncio import gc import platform +import sys +from typing import Any, cast -from testutil import is_gil_disabled +from testutil import assertRaises, is_gil_disabled def immediate_deallocation_checks_are_reliable() -> bool: # Free-threaded builds can defer deallocation, so weakref liveness is not deterministic. @@ -697,6 +699,28 @@ async def test_completed_mixed_coroutine_releases_environment() -> None: assert await coro == 1 assert ref() is None +async def iterate(x: Any) -> int: + n = 0 + async for y in x: + n += 1 + return n + +async def comprehension(x: Any) -> list[Any]: + return [y async for y in x] + +def test_async_for_over_sync_iterator() -> None: + getrefcount = getattr(sys, "getrefcount") + it = iter([1, 2]) + rc = getrefcount(it) + msg = "'async for' requires an object with __aiter__ method, got list_iterator" + for i in range(10): + with assertRaises(TypeError, msg): + cast(Any, iterate(it)).send(None) + with assertRaises(TypeError, msg): + cast(Any, comprehension(it)).send(None) + assert getrefcount(it) == rc + assert list(it) == [1, 2] + [file asyncio/__init__.pyi] async def sleep(t: float) -> None: ...