Skip to content

Commit 38a9956

Browse files
gh-128308: pass **kwargs to asyncio task_factory (#128768)
Co-authored-by: Kumar Aditya <[email protected]>
1 parent 6c914bf commit 38a9956

File tree

8 files changed

+48
-29
lines changed

8 files changed

+48
-29
lines changed

Doc/library/asyncio-eventloop.rst

+2-2
Original file line numberDiff line numberDiff line change
@@ -392,9 +392,9 @@ Creating Futures and Tasks
392392

393393
If *factory* is ``None`` the default task factory will be set.
394394
Otherwise, *factory* must be a *callable* with the signature matching
395-
``(loop, coro, context=None)``, where *loop* is a reference to the active
395+
``(loop, coro, **kwargs)``, where *loop* is a reference to the active
396396
event loop, and *coro* is a coroutine object. The callable
397-
must return a :class:`asyncio.Future`-compatible object.
397+
must pass on all *kwargs*, and return a :class:`asyncio.Task`-compatible object.
398398

399399
.. method:: loop.get_task_factory()
400400

Lib/asyncio/base_events.py

+10-16
Original file line numberDiff line numberDiff line change
@@ -458,25 +458,18 @@ def create_future(self):
458458
"""Create a Future object attached to the loop."""
459459
return futures.Future(loop=self)
460460

461-
def create_task(self, coro, *, name=None, context=None):
461+
def create_task(self, coro, **kwargs):
462462
"""Schedule a coroutine object.
463463
464464
Return a task object.
465465
"""
466466
self._check_closed()
467-
if self._task_factory is None:
468-
task = tasks.Task(coro, loop=self, name=name, context=context)
469-
if task._source_traceback:
470-
del task._source_traceback[-1]
471-
else:
472-
if context is None:
473-
# Use legacy API if context is not needed
474-
task = self._task_factory(self, coro)
475-
else:
476-
task = self._task_factory(self, coro, context=context)
477-
478-
task.set_name(name)
467+
if self._task_factory is not None:
468+
return self._task_factory(self, coro, **kwargs)
479469

470+
task = tasks.Task(coro, loop=self, **kwargs)
471+
if task._source_traceback:
472+
del task._source_traceback[-1]
480473
try:
481474
return task
482475
finally:
@@ -490,9 +483,10 @@ def set_task_factory(self, factory):
490483
If factory is None the default task factory will be set.
491484
492485
If factory is a callable, it should have a signature matching
493-
'(loop, coro)', where 'loop' will be a reference to the active
494-
event loop, 'coro' will be a coroutine object. The callable
495-
must return a Future.
486+
'(loop, coro, **kwargs)', where 'loop' will be a reference to the active
487+
event loop, 'coro' will be a coroutine object, and **kwargs will be
488+
arbitrary keyword arguments that should be passed on to Task.
489+
The callable must return a Task.
496490
"""
497491
if factory is not None and not callable(factory):
498492
raise TypeError('task factory must be a callable or None')

Lib/asyncio/events.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ def create_future(self):
329329

330330
# Method scheduling a coroutine object: create a task.
331331

332-
def create_task(self, coro, *, name=None, context=None):
332+
def create_task(self, coro, **kwargs):
333333
raise NotImplementedError
334334

335335
# Methods for interacting with threads.

Lib/test/test_asyncio/test_base_events.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -833,8 +833,8 @@ async def test():
833833
loop.close()
834834

835835
def test_create_named_task_with_custom_factory(self):
836-
def task_factory(loop, coro):
837-
return asyncio.Task(coro, loop=loop)
836+
def task_factory(loop, coro, **kwargs):
837+
return asyncio.Task(coro, loop=loop, **kwargs)
838838

839839
async def test():
840840
pass

Lib/test/test_asyncio/test_eager_task_factory.py

+12
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,18 @@ async def run():
302302

303303
self.run_coro(run())
304304

305+
def test_name(self):
306+
name = None
307+
async def coro():
308+
nonlocal name
309+
name = asyncio.current_task().get_name()
310+
311+
async def main():
312+
task = self.loop.create_task(coro(), name="test name")
313+
self.assertEqual(name, "test name")
314+
await task
315+
316+
self.run_coro(coro())
305317

306318
class AsyncTaskCounter:
307319
def __init__(self, loop, *, task_class, eager):

Lib/test/test_asyncio/test_free_threading.py

+8-8
Original file line numberDiff line numberDiff line change
@@ -112,25 +112,25 @@ class TestPyFreeThreading(TestFreeThreading, TestCase):
112112
all_tasks = staticmethod(asyncio.tasks._py_all_tasks)
113113
current_task = staticmethod(asyncio.tasks._py_current_task)
114114

115-
def factory(self, loop, coro, context=None):
116-
return asyncio.tasks._PyTask(coro, loop=loop, context=context)
115+
def factory(self, loop, coro, **kwargs):
116+
return asyncio.tasks._PyTask(coro, loop=loop, **kwargs)
117117

118118

119119
@unittest.skipUnless(hasattr(asyncio.tasks, "_c_all_tasks"), "requires _asyncio")
120120
class TestCFreeThreading(TestFreeThreading, TestCase):
121121
all_tasks = staticmethod(getattr(asyncio.tasks, "_c_all_tasks", None))
122122
current_task = staticmethod(getattr(asyncio.tasks, "_c_current_task", None))
123123

124-
def factory(self, loop, coro, context=None):
125-
return asyncio.tasks._CTask(coro, loop=loop, context=context)
124+
def factory(self, loop, coro, **kwargs):
125+
return asyncio.tasks._CTask(coro, loop=loop, **kwargs)
126126

127127

128128
class TestEagerPyFreeThreading(TestPyFreeThreading):
129-
def factory(self, loop, coro, context=None):
130-
return asyncio.tasks._PyTask(coro, loop=loop, context=context, eager_start=True)
129+
def factory(self, loop, coro, eager_start=True, **kwargs):
130+
return asyncio.tasks._PyTask(coro, loop=loop, **kwargs, eager_start=eager_start)
131131

132132

133133
@unittest.skipUnless(hasattr(asyncio.tasks, "_c_all_tasks"), "requires _asyncio")
134134
class TestEagerCFreeThreading(TestCFreeThreading, TestCase):
135-
def factory(self, loop, coro, context=None):
136-
return asyncio.tasks._CTask(coro, loop=loop, context=context, eager_start=True)
135+
def factory(self, loop, coro, eager_start=True, **kwargs):
136+
return asyncio.tasks._CTask(coro, loop=loop, **kwargs, eager_start=eager_start)

Lib/test/test_asyncio/test_taskgroups.py

+12
Original file line numberDiff line numberDiff line change
@@ -1040,6 +1040,18 @@ class MyKeyboardInterrupt(KeyboardInterrupt):
10401040
self.assertIsNotNone(exc)
10411041
self.assertListEqual(gc.get_referrers(exc), no_other_refs())
10421042

1043+
async def test_name(self):
1044+
name = None
1045+
1046+
async def asyncfn():
1047+
nonlocal name
1048+
name = asyncio.current_task().get_name()
1049+
1050+
async with asyncio.TaskGroup() as tg:
1051+
tg.create_task(asyncfn(), name="example name")
1052+
1053+
self.assertEqual(name, "example name")
1054+
10431055

10441056
class TestTaskGroup(BaseTestTaskGroup, unittest.IsolatedAsyncioTestCase):
10451057
loop_factory = asyncio.EventLoop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Support the *name* keyword argument for eager tasks in :func:`asyncio.loop.create_task`, :func:`asyncio.create_task` and :func:`asyncio.TaskGroup.create_task`, by passing on all *kwargs* to the task factory set by :func:`asyncio.loop.set_task_factory`.

0 commit comments

Comments
 (0)