]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-128307: Support eager_start=<bool> in create_eager_task_factory and various creat...
authorThomas Grainger <tagrain@gmail.com>
Mon, 5 May 2025 04:58:07 +0000 (05:58 +0100)
committerGitHub <noreply@github.com>
Mon, 5 May 2025 04:58:07 +0000 (04:58 +0000)
Some create_task() functions were changed from `name=None, context=None` to `**kwargs`.

Co-authored-by: Guido van Rossum <gvanrossum@gmail.com>
Doc/library/asyncio-eventloop.rst
Lib/asyncio/base_events.py
Lib/asyncio/taskgroups.py
Lib/asyncio/tasks.py
Lib/test/test_asyncio/test_eager_task_factory.py
Lib/test/test_asyncio/test_tasks.py
Misc/NEWS.d/next/Library/2024-12-28-11-01-36.gh-issue-128307.BRCYTA.rst [new file with mode: 0644]

index 8f561744fe4981fdb051a5b00224aff715fe3051..e25f6e52efc1a03d5d6923a05d9cac6880513ee9 100644 (file)
@@ -361,7 +361,7 @@ Creating Futures and Tasks
 
    .. versionadded:: 3.5.2
 
-.. method:: loop.create_task(coro, *, name=None, context=None)
+.. method:: loop.create_task(coro, *, name=None, context=None, eager_start=None)
 
    Schedule the execution of :ref:`coroutine <coroutine>` *coro*.
    Return a :class:`Task` object.
@@ -377,12 +377,20 @@ Creating Futures and Tasks
    custom :class:`contextvars.Context` for the *coro* to run in.
    The current context copy is created when no *context* is provided.
 
+   An optional keyword-only *eager_start* argument allows specifying
+   if the task should execute eagerly during the call to create_task,
+   or be scheduled later. If *eager_start* is not passed the mode set
+   by :meth:`loop.set_task_factory` will be used.
+
    .. versionchanged:: 3.8
       Added the *name* parameter.
 
    .. versionchanged:: 3.11
       Added the *context* parameter.
 
+   .. versionchanged:: next
+      Added the *eager_start* parameter.
+
 .. method:: loop.set_task_factory(factory)
 
    Set a task factory that will be used by
index 29b872ce00ec8116ecdd70e2f84d864258a24a27..04fb961e9985e4d69c146c0c9d515deca6e366bb 100644 (file)
@@ -459,7 +459,7 @@ class BaseEventLoop(events.AbstractEventLoop):
         return futures.Future(loop=self)
 
     def create_task(self, coro, **kwargs):
-        """Schedule a coroutine object.
+        """Schedule or begin executing a coroutine object.
 
         Return a task object.
         """
index 1633478d1c87c2d3441118657c1b3d6405be9026..00e8f6d5d1a68b2028e9304e19cc3006bf1f2c49 100644 (file)
@@ -179,7 +179,7 @@ class TaskGroup:
                 exc = None
 
 
-    def create_task(self, coro, *, name=None, context=None):
+    def create_task(self, coro, **kwargs):
         """Create a new task in this group and return it.
 
         Similar to `asyncio.create_task`.
@@ -193,10 +193,7 @@ class TaskGroup:
         if self._aborting:
             coro.close()
             raise RuntimeError(f"TaskGroup {self!r} is shutting down")
-        if context is None:
-            task = self._loop.create_task(coro, name=name)
-        else:
-            task = self._loop.create_task(coro, name=name, context=context)
+        task = self._loop.create_task(coro, **kwargs)
 
         futures.future_add_to_awaited_by(task, self._parent_task)
 
index 825e91f5594d9873a2de0c83c85188f625cecdcb..888615f8e5e1b34dbbb52fce8068f1b5f6258838 100644 (file)
@@ -386,19 +386,13 @@ else:
     Task = _CTask = _asyncio.Task
 
 
-def create_task(coro, *, name=None, context=None):
+def create_task(coro, **kwargs):
     """Schedule the execution of a coroutine object in a spawn task.
 
     Return a Task object.
     """
     loop = events.get_running_loop()
-    if context is None:
-        # Use legacy API if context is not needed
-        task = loop.create_task(coro, name=name)
-    else:
-        task = loop.create_task(coro, name=name, context=context)
-
-    return task
+    return loop.create_task(coro, **kwargs)
 
 
 # wait() and as_completed() similar to those in PEP 3148.
@@ -1030,9 +1024,9 @@ def create_eager_task_factory(custom_task_constructor):
         used. E.g. `loop.set_task_factory(asyncio.eager_task_factory)`.
         """
 
-    def factory(loop, coro, *, name=None, context=None):
+    def factory(loop, coro, *, eager_start=True, **kwargs):
         return custom_task_constructor(
-            coro, loop=loop, name=name, context=context, eager_start=True)
+            coro, loop=loop, eager_start=eager_start, **kwargs)
 
     return factory
 
index a2fb1022ae4d3fb2b7ff971d9fe941c26623d219..9f3b6f9acef3b621ac2ce0716a11f0f577791743 100644 (file)
@@ -263,6 +263,24 @@ class EagerTaskFactoryLoopTests:
 
         self.run_coro(run())
 
+    def test_eager_start_false(self):
+        name = None
+
+        async def asyncfn():
+            nonlocal name
+            name = asyncio.current_task().get_name()
+
+        async def main():
+            t = asyncio.get_running_loop().create_task(
+                asyncfn(), eager_start=False, name="example"
+            )
+            self.assertFalse(t.done())
+            self.assertIsNone(name)
+            await t
+            self.assertEqual(name, "example")
+
+        self.run_coro(main())
+
 
 class PyEagerTaskFactoryLoopTests(EagerTaskFactoryLoopTests, test_utils.TestCase):
     Task = tasks._PyTask
@@ -505,5 +523,24 @@ class EagerCTaskTests(BaseEagerTaskFactoryTests, test_utils.TestCase):
         asyncio.current_task = asyncio.tasks.current_task = self._current_task
         return super().tearDown()
 
+
+class DefaultTaskFactoryEagerStart(test_utils.TestCase):
+    def test_eager_start_true_with_default_factory(self):
+        name = None
+
+        async def asyncfn():
+            nonlocal name
+            name = asyncio.current_task().get_name()
+
+        async def main():
+            t = asyncio.get_running_loop().create_task(
+                asyncfn(), eager_start=True, name="example"
+            )
+            self.assertTrue(t.done())
+            self.assertEqual(name, "example")
+            await t
+
+        asyncio.run(main(), loop_factory=asyncio.EventLoop)
+
 if __name__ == '__main__':
     unittest.main()
index 8d7f17334547b3db723ebb3c1281a78e4cecfebe..44498ef790e450dab3bc5db46d753234b25e2d03 100644 (file)
@@ -89,8 +89,8 @@ class BaseTaskTests:
     Future = None
     all_tasks = None
 
-    def new_task(self, loop, coro, name='TestTask', context=None):
-        return self.__class__.Task(coro, loop=loop, name=name, context=context)
+    def new_task(self, loop, coro, name='TestTask', context=None, eager_start=None):
+        return self.__class__.Task(coro, loop=loop, name=name, context=context, eager_start=eager_start)
 
     def new_future(self, loop):
         return self.__class__.Future(loop=loop)
@@ -2686,6 +2686,35 @@ class BaseTaskTests:
 
         self.assertEqual([None, 1, 2], ret)
 
+    def test_eager_start_true(self):
+        name = None
+
+        async def asyncfn():
+            nonlocal name
+            name = self.current_task().get_name()
+
+        async def main():
+            t = self.new_task(coro=asyncfn(), loop=asyncio.get_running_loop(), eager_start=True, name="example")
+            self.assertTrue(t.done())
+            self.assertEqual(name, "example")
+            await t
+
+    def test_eager_start_false(self):
+        name = None
+
+        async def asyncfn():
+            nonlocal name
+            name = self.current_task().get_name()
+
+        async def main():
+            t = self.new_task(coro=asyncfn(), loop=asyncio.get_running_loop(), eager_start=False, name="example")
+            self.assertFalse(t.done())
+            self.assertIsNone(name)
+            await t
+            self.assertEqual(name, "example")
+
+        asyncio.run(main(), loop_factory=asyncio.EventLoop)
+
     def test_get_coro(self):
         loop = asyncio.new_event_loop()
         coro = coroutine_function()
diff --git a/Misc/NEWS.d/next/Library/2024-12-28-11-01-36.gh-issue-128307.BRCYTA.rst b/Misc/NEWS.d/next/Library/2024-12-28-11-01-36.gh-issue-128307.BRCYTA.rst
new file mode 100644 (file)
index 0000000..93b2a74
--- /dev/null
@@ -0,0 +1 @@
+Add ``eager_start`` keyword argument to :meth:`asyncio.loop.create_task`