Skip to content

Commit 08d7687

Browse files
gh-128307: Support eager_start=<bool> in create_eager_task_factory and various create_task functions (#128306)
Some create_task() functions were changed from `name=None, context=None` to `**kwargs`. Co-authored-by: Guido van Rossum <[email protected]>
1 parent c4cc5d5 commit 08d7687

File tree

7 files changed

+85
-19
lines changed

7 files changed

+85
-19
lines changed

Doc/library/asyncio-eventloop.rst

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@ Creating Futures and Tasks
361361

362362
.. versionadded:: 3.5.2
363363

364-
.. method:: loop.create_task(coro, *, name=None, context=None)
364+
.. method:: loop.create_task(coro, *, name=None, context=None, eager_start=None)
365365

366366
Schedule the execution of :ref:`coroutine <coroutine>` *coro*.
367367
Return a :class:`Task` object.
@@ -377,12 +377,20 @@ Creating Futures and Tasks
377377
custom :class:`contextvars.Context` for the *coro* to run in.
378378
The current context copy is created when no *context* is provided.
379379

380+
An optional keyword-only *eager_start* argument allows specifying
381+
if the task should execute eagerly during the call to create_task,
382+
or be scheduled later. If *eager_start* is not passed the mode set
383+
by :meth:`loop.set_task_factory` will be used.
384+
380385
.. versionchanged:: 3.8
381386
Added the *name* parameter.
382387

383388
.. versionchanged:: 3.11
384389
Added the *context* parameter.
385390

391+
.. versionchanged:: next
392+
Added the *eager_start* parameter.
393+
386394
.. method:: loop.set_task_factory(factory)
387395

388396
Set a task factory that will be used by

Lib/asyncio/base_events.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@ def create_future(self):
459459
return futures.Future(loop=self)
460460

461461
def create_task(self, coro, **kwargs):
462-
"""Schedule a coroutine object.
462+
"""Schedule or begin executing a coroutine object.
463463
464464
Return a task object.
465465
"""

Lib/asyncio/taskgroups.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ async def _aexit(self, et, exc):
179179
exc = None
180180

181181

182-
def create_task(self, coro, *, name=None, context=None):
182+
def create_task(self, coro, **kwargs):
183183
"""Create a new task in this group and return it.
184184
185185
Similar to `asyncio.create_task`.
@@ -193,10 +193,7 @@ def create_task(self, coro, *, name=None, context=None):
193193
if self._aborting:
194194
coro.close()
195195
raise RuntimeError(f"TaskGroup {self!r} is shutting down")
196-
if context is None:
197-
task = self._loop.create_task(coro, name=name)
198-
else:
199-
task = self._loop.create_task(coro, name=name, context=context)
196+
task = self._loop.create_task(coro, **kwargs)
200197

201198
futures.future_add_to_awaited_by(task, self._parent_task)
202199

Lib/asyncio/tasks.py

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -386,19 +386,13 @@ def __wakeup(self, future):
386386
Task = _CTask = _asyncio.Task
387387

388388

389-
def create_task(coro, *, name=None, context=None):
389+
def create_task(coro, **kwargs):
390390
"""Schedule the execution of a coroutine object in a spawn task.
391391
392392
Return a Task object.
393393
"""
394394
loop = events.get_running_loop()
395-
if context is None:
396-
# Use legacy API if context is not needed
397-
task = loop.create_task(coro, name=name)
398-
else:
399-
task = loop.create_task(coro, name=name, context=context)
400-
401-
return task
395+
return loop.create_task(coro, **kwargs)
402396

403397

404398
# wait() and as_completed() similar to those in PEP 3148.
@@ -1030,9 +1024,9 @@ def create_eager_task_factory(custom_task_constructor):
10301024
used. E.g. `loop.set_task_factory(asyncio.eager_task_factory)`.
10311025
"""
10321026

1033-
def factory(loop, coro, *, name=None, context=None):
1027+
def factory(loop, coro, *, eager_start=True, **kwargs):
10341028
return custom_task_constructor(
1035-
coro, loop=loop, name=name, context=context, eager_start=True)
1029+
coro, loop=loop, eager_start=eager_start, **kwargs)
10361030

10371031
return factory
10381032

Lib/test/test_asyncio/test_eager_task_factory.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,24 @@ async def run():
263263

264264
self.run_coro(run())
265265

266+
def test_eager_start_false(self):
267+
name = None
268+
269+
async def asyncfn():
270+
nonlocal name
271+
name = asyncio.current_task().get_name()
272+
273+
async def main():
274+
t = asyncio.get_running_loop().create_task(
275+
asyncfn(), eager_start=False, name="example"
276+
)
277+
self.assertFalse(t.done())
278+
self.assertIsNone(name)
279+
await t
280+
self.assertEqual(name, "example")
281+
282+
self.run_coro(main())
283+
266284

267285
class PyEagerTaskFactoryLoopTests(EagerTaskFactoryLoopTests, test_utils.TestCase):
268286
Task = tasks._PyTask
@@ -505,5 +523,24 @@ def tearDown(self):
505523
asyncio.current_task = asyncio.tasks.current_task = self._current_task
506524
return super().tearDown()
507525

526+
527+
class DefaultTaskFactoryEagerStart(test_utils.TestCase):
528+
def test_eager_start_true_with_default_factory(self):
529+
name = None
530+
531+
async def asyncfn():
532+
nonlocal name
533+
name = asyncio.current_task().get_name()
534+
535+
async def main():
536+
t = asyncio.get_running_loop().create_task(
537+
asyncfn(), eager_start=True, name="example"
538+
)
539+
self.assertTrue(t.done())
540+
self.assertEqual(name, "example")
541+
await t
542+
543+
asyncio.run(main(), loop_factory=asyncio.EventLoop)
544+
508545
if __name__ == '__main__':
509546
unittest.main()

Lib/test/test_asyncio/test_tasks.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,8 @@ class BaseTaskTests:
8989
Future = None
9090
all_tasks = None
9191

92-
def new_task(self, loop, coro, name='TestTask', context=None):
93-
return self.__class__.Task(coro, loop=loop, name=name, context=context)
92+
def new_task(self, loop, coro, name='TestTask', context=None, eager_start=None):
93+
return self.__class__.Task(coro, loop=loop, name=name, context=context, eager_start=eager_start)
9494

9595
def new_future(self, loop):
9696
return self.__class__.Future(loop=loop)
@@ -2686,6 +2686,35 @@ async def main():
26862686

26872687
self.assertEqual([None, 1, 2], ret)
26882688

2689+
def test_eager_start_true(self):
2690+
name = None
2691+
2692+
async def asyncfn():
2693+
nonlocal name
2694+
name = self.current_task().get_name()
2695+
2696+
async def main():
2697+
t = self.new_task(coro=asyncfn(), loop=asyncio.get_running_loop(), eager_start=True, name="example")
2698+
self.assertTrue(t.done())
2699+
self.assertEqual(name, "example")
2700+
await t
2701+
2702+
def test_eager_start_false(self):
2703+
name = None
2704+
2705+
async def asyncfn():
2706+
nonlocal name
2707+
name = self.current_task().get_name()
2708+
2709+
async def main():
2710+
t = self.new_task(coro=asyncfn(), loop=asyncio.get_running_loop(), eager_start=False, name="example")
2711+
self.assertFalse(t.done())
2712+
self.assertIsNone(name)
2713+
await t
2714+
self.assertEqual(name, "example")
2715+
2716+
asyncio.run(main(), loop_factory=asyncio.EventLoop)
2717+
26892718
def test_get_coro(self):
26902719
loop = asyncio.new_event_loop()
26912720
coro = coroutine_function()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add ``eager_start`` keyword argument to :meth:`asyncio.loop.create_task`

0 commit comments

Comments
 (0)