diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst index 92b98e2b2c862c..91246e34fdc6b3 100644 --- a/Doc/library/asyncio-eventloop.rst +++ b/Doc/library/asyncio-eventloop.rst @@ -384,9 +384,9 @@ Creating Futures and Tasks If *factory* is ``None`` the default task factory will be set. Otherwise, *factory* must be a *callable* with the signature matching - ``(loop, coro, **kwargs)``, where *loop* is a reference to the active + ``(loop, coro, context=None)``, where *loop* is a reference to the active event loop, and *coro* is a coroutine object. The callable - must pass on all *kwargs*, and return a :class:`asyncio.Task`-compatible object. + must return a :class:`asyncio.Future`-compatible object. .. method:: loop.get_task_factory() diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index d2cedb68985853..938c268a6590c5 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -458,18 +458,25 @@ def create_future(self): """Create a Future object attached to the loop.""" return futures.Future(loop=self) - def create_task(self, coro, **kwargs): + def create_task(self, coro, *, name=None, context=None): """Schedule a coroutine object. Return a task object. """ self._check_closed() - if self._task_factory is not None: - return self._task_factory(self, coro, **kwargs) + if self._task_factory is None: + task = tasks.Task(coro, loop=self, name=name, context=context) + if task._source_traceback: + del task._source_traceback[-1] + else: + if context is None: + # Use legacy API if context is not needed + task = self._task_factory(self, coro) + else: + task = self._task_factory(self, coro, context=context) + + task.set_name(name) - task = tasks.Task(coro, loop=self, **kwargs) - if task._source_traceback: - del task._source_traceback[-1] try: return task finally: @@ -483,10 +490,9 @@ def set_task_factory(self, factory): If factory is None the default task factory will be set. If factory is a callable, it should have a signature matching - '(loop, coro, **kwargs)', where 'loop' will be a reference to the active - event loop, 'coro' will be a coroutine object, and **kwargs will be - arbitrary keyword arguments that should be passed on to Task. - The callable must return a Task. + '(loop, coro)', where 'loop' will be a reference to the active + event loop, 'coro' will be a coroutine object. The callable + must return a Future. """ if factory is not None and not callable(factory): raise TypeError('task factory must be a callable or None') diff --git a/Lib/asyncio/events.py b/Lib/asyncio/events.py index 3b740b9b905c0d..be495469a0558b 100644 --- a/Lib/asyncio/events.py +++ b/Lib/asyncio/events.py @@ -292,7 +292,7 @@ def create_future(self): # Method scheduling a coroutine object: create a task. - def create_task(self, coro, **kwargs): + def create_task(self, coro, *, name=None, context=None): raise NotImplementedError # Methods for interacting with threads. diff --git a/Lib/test/test_asyncio/test_base_events.py b/Lib/test/test_asyncio/test_base_events.py index 3d3459f47c8324..22f214ed0b4063 100644 --- a/Lib/test/test_asyncio/test_base_events.py +++ b/Lib/test/test_asyncio/test_base_events.py @@ -838,8 +838,8 @@ async def test(): loop.close() def test_create_named_task_with_custom_factory(self): - def task_factory(loop, coro, **kwargs): - return asyncio.Task(coro, loop=loop, **kwargs) + def task_factory(loop, coro): + return asyncio.Task(coro, loop=loop) async def test(): pass diff --git a/Lib/test/test_asyncio/test_eager_task_factory.py b/Lib/test/test_asyncio/test_eager_task_factory.py index 179a6e44b59934..0e2b189f761521 100644 --- a/Lib/test/test_asyncio/test_eager_task_factory.py +++ b/Lib/test/test_asyncio/test_eager_task_factory.py @@ -302,18 +302,6 @@ async def run(): self.run_coro(run()) - def test_name(self): - name = None - async def coro(): - nonlocal name - name = asyncio.current_task().get_name() - - async def main(): - task = self.loop.create_task(coro(), name="test name") - self.assertEqual(name, "test name") - await task - - self.run_coro(coro()) class AsyncTaskCounter: def __init__(self, loop, *, task_class, eager): diff --git a/Lib/test/test_asyncio/test_taskgroups.py b/Lib/test/test_asyncio/test_taskgroups.py index 9f2211e3232e54..ad61cb46c7c07c 100644 --- a/Lib/test/test_asyncio/test_taskgroups.py +++ b/Lib/test/test_asyncio/test_taskgroups.py @@ -1081,18 +1081,6 @@ async def throw_error(): # cancellation happens here and error is more understandable await asyncio.sleep(0) - async def test_name(self): - name = None - - async def asyncfn(): - nonlocal name - name = asyncio.current_task().get_name() - - async with asyncio.TaskGroup() as tg: - tg.create_task(asyncfn(), name="example name") - - self.assertEqual(name, "example name") - class TestTaskGroup(BaseTestTaskGroup, unittest.IsolatedAsyncioTestCase): loop_factory = asyncio.EventLoop diff --git a/Misc/NEWS.d/next/Library/2025-05-10-09-02-16.gh-issue-128308.y7dVb0.rst b/Misc/NEWS.d/next/Library/2025-05-10-09-02-16.gh-issue-128308.y7dVb0.rst new file mode 100644 index 00000000000000..e32a88f5988e57 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-05-10-09-02-16.gh-issue-128308.y7dVb0.rst @@ -0,0 +1 @@ +Fix breaking change in :mod:`asyncio` task factory to avoid passing name argument.