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
This commit is contained in:
dualfroz committed 2026-09-03 14:12:03 +02:00
1 parent e80bb3a1e7
commit 8b30a4b77c
2 files changed
+24 -1

No files matched your search

+20
View File
@@ -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
+4 -1
View File
@@ -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