8000 gh-95601: forbid non-futures in asyncio.wait · graingert/cpython@88580c9 · GitHub
[go: up one dir, main page]

Skip to content

Commit 88580c9

Browse files
committed
pythongh-95601: forbid non-futures in asyncio.wait
1 parent 4d02572 commit 88580c9

File tree

2 files changed

+64
-8
lines changed

2 files changed

+64
-8
lines changed

Lib/asyncio/tasks.py

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -412,19 +412,29 @@ async def wait(fs, *, timeout=None, return_when=ALL_COMPLETED):
412412
when the timeout occurs are returned in the second set.
413413
"""
414414
if futures.isfuture(fs) or coroutines.iscoroutine(fs):
415-
raise TypeError(f"expect a list of futures, not {type(fs).__name__}")
416-
if not fs:
417-
raise ValueError('Set of Tasks/Futures is empty.')
415+
raise TypeError(f"expected an iterable of futures, not {type(fs).__name__}")
418416
if return_when not in (FIRST_COMPLETED, FIRST_EXCEPTION, ALL_COMPLETED):
419417
raise ValueError(f'Invalid return_when value: {return_when}')
420418

421-
fs = set(fs)
419+
loop = events.get_running_loop()
420+
new_fs = set()
422421

423-
if any(coroutines.iscoroutine(f) for f in fs):
424-
raise TypeError("Passing coroutines is forbidden, use tasks explicitly.")
422+
for f in fs:
423+
if coroutines.iscoroutine(f):
424+
raise TypeError("Passing coroutines is forbidden, use tasks explicitly.")
425+
if not futures.isfuture(f):
426+
warnings.warn("The explicit passing of awaitable objects to "
427+
"asyncio.wait() is deprecated since Python 3.11, and "
428+
"scheduled for removal in Python 3.14.",
429+
DeprecationWarning, stacklevel=2)
430+
new_fs.add(ensure_future(f, loop=loop))
431+
else:
432+
new_fs.add(f)
425433

426-
loop = events.get_running_loop()
427-
return await _wait(fs, timeout, return_when, loop)
434+
if not new_fs:
435+
raise ValueError('Iterable of Tasks/Futures is empty.')
436+
437+
return await _wait(new_fs, timeout, return_when, loop)
428438

429439

430440
def _release_waiter(waiter, *args):

Lib/test/test_asyncio/test_tasks.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3247,6 +3247,52 @@ async def coro():
32473247
self.assertEqual(result, 11)
32483248

32493249

3250+
class WaitTests(test_utils.TestCase):
3251+
def setUp(self):
3252+
super().setUp()
3253+
self.loop = asyncio.new_event_loop()
3254+
self.set_event_loop(self.loop)
3255+
3256+
def tearDown(self):
3257+
self.loop.close()
3258+
self.loop = None
3259+
super().tearDown()
3260+
3261+
def test_awaitable_is_deprecated_in_wait(self):
3262+
# Remove test when passing awaitables to asyncio.wait() is removed in 3.14
3263+
3264+
class ExampleAwaitable:
3265+
def __await__(self):
3266+
async def _():
3267+
return await asyncio.sleep(0)
3268+
3269+
return _().__await__()
3270+
3271+
3272+
with self.assertWarns(DeprecationWarning):
3273+
self.loop.run_until_complete(
3274+
asyncio.wait([ExampleAwaitable()]))
3275+
3276+
task = self.loop.create_task(coroutine_function())
3277+
with self.assertWarns(DeprecationWarning), self.assertRaises(TypeError):
3278+
self.loop.run_until_complete(
3279+
asyncio.wait([task, ExampleAwaitable(), coroutine_function()]))
3280+
3281+
# avoid: Task was destroyed but it is pending!
3282+
self.loop.run_until_complete(task)
3283+
3284+
def test_wait_supports_iterators(self):
3285+
with self.assertRaisesRegex(ValueError, "Iterable of .* is empty\."):
3286+
self.loop.run_until_complete(asyncio.wait(iter(())))
3287+
3288+
task = self.loop.create_task(coroutine_function())
3289+
done, pending = self.loop.run_until_complete(
3290+
asyncio.wait(iter((task,))),
3291+
)
3292+
self.assertSetEqual(done, {task})
3293+
self.assertSetEqual(pending, set())
3294+
3295+
32503296
class CompatibilityTests(test_utils.TestCase):
32513297
# Tests for checking a bridge between old-styled coroutines
32523298
# and async/await syntax

0 commit comments

Comments
 (0)
0