From 8b30a4b77c4aa32c7efea90b087a2f8d0dd4e030 Mon Sep 17 00:00:00 2001 From: dualfroz Date: Thu, 3 Sep 2026 14:12:03 +0200 Subject: [PATCH] fix(asyncio): accept unsized iterables in as_completed Since Python 3.12 asyncio.as_completed accepts a generator of awaitables, but the tqdm wrapper calls len(fs) unconditionally when total is omitted, so the same input raises TypeError: object of type 'generator' has no len(). Fall back to an unknown total when fs has no length, mirroring how tqdm.std.tqdm.__init__ derives total from an iterable. Sized inputs keep reporting their exact total, and an explicit total is still honoured. Fixes #1811 --- tests/tests_asyncio.py | 20 ++++++++++++++++++++ tqdm/asyncio.py | 5 ++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/tests/tests_asyncio.py b/tests/tests_asyncio.py index 5311701..513066d 100644 --- a/tests/tests_asyncio.py +++ b/tests/tests_asyncio.py @@ -120,6 +120,26 @@ async def test_as_completed(capsys, tol): raise +@mark.asyncio +async def test_as_completed_unsized(capsys): + """Test asyncio as_completed with an unsized iterable""" + fs = (asyncio.sleep(0, result=i) for i in range(3)) + res = sorted([await f for f in as_completed(fs)]) + assert res == [0, 1, 2] + _, err = capsys.readouterr() + assert '3it' in err + + +@mark.asyncio +async def test_as_completed_unsized_with_total(capsys): + """Test asyncio as_completed with an unsized iterable and an explicit total""" + fs = (asyncio.sleep(0, result=i) for i in range(3)) + res = sorted([await f for f in as_completed(fs, total=3)]) + assert res == [0, 1, 2] + _, err = capsys.readouterr() + assert '3/3' in err + + async def double(i): return i * 2 diff --git a/tqdm/asyncio.py b/tqdm/asyncio.py index ac5c2e6..df7ef5a 100644 --- a/tqdm/asyncio.py +++ b/tqdm/asyncio.py @@ -64,7 +64,10 @@ class tqdm_asyncio(std_tqdm): Wrapper for `asyncio.as_completed`. """ if total is None: - total = len(fs) + try: + total = len(fs) + except (TypeError, AttributeError): + total = None kwargs = {} if version_info[:2] < (3, 10): kwargs['loop'] = loop