mirror of
https://github.com/python/cpython.git
synced 2026-01-06 07:22:09 +00:00
[3.13] gh-133745: Fix asyncio task factory name/context kwarg breaks (#133948)
In 3.13.3 we accidentally broke the interface for custom task factory. Factory authors added workarounds. This PR (for 3.13.4) unbreaks task factories that haven't made a workaround yet while also supporting those that have. NOTE: The custom task factory API will change to what we accidentally released in 3.13.3. Co-authored-by: Kumar Aditya <kumaraditya@python.org> Co-authored-by: Guido van Rossum <gvanrossum@gmail.com>
This commit is contained in:
parent
ebe54d7ab7
commit
fd6a602d04
4 changed files with 32 additions and 18 deletions
|
|
@ -355,7 +355,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, **kwargs)
|
||||
|
||||
Schedule the execution of :ref:`coroutine <coroutine>` *coro*.
|
||||
Return a :class:`Task` object.
|
||||
|
|
@ -364,6 +364,11 @@ Creating Futures and Tasks
|
|||
for interoperability. In this case, the result type is a subclass
|
||||
of :class:`Task`.
|
||||
|
||||
The full function signature is largely the same as that of the
|
||||
:class:`Task` constructor (or factory) - all of the keyword arguments to
|
||||
this function are passed through to that interface, except *name*,
|
||||
or *context* if it is ``None``.
|
||||
|
||||
If the *name* argument is provided and not ``None``, it is set as
|
||||
the name of the task using :meth:`Task.set_name`.
|
||||
|
||||
|
|
@ -377,6 +382,13 @@ Creating Futures and Tasks
|
|||
.. versionchanged:: 3.11
|
||||
Added the *context* parameter.
|
||||
|
||||
.. versionchanged:: 3.13.3
|
||||
Added ``kwargs`` which passes on arbitrary extra parameters, including ``name`` and ``context``.
|
||||
|
||||
.. versionchanged:: 3.13.4
|
||||
Rolled back the change that passes on *name* and *context* (if it is None),
|
||||
while still passing on other arbitrary keyword arguments (to avoid breaking backwards compatibility with 3.13.3).
|
||||
|
||||
.. method:: loop.set_task_factory(factory)
|
||||
|
||||
Set a task factory that will be used by
|
||||
|
|
@ -388,6 +400,13 @@ Creating Futures and Tasks
|
|||
event loop, and *coro* is a coroutine object. The callable
|
||||
must pass on all *kwargs*, and return a :class:`asyncio.Task`-compatible object.
|
||||
|
||||
.. versionchanged:: 3.13.3
|
||||
Required that all *kwargs* are passed on to :class:`asyncio.Task`.
|
||||
|
||||
.. versionchanged:: 3.13.4
|
||||
*name* is no longer passed to task factories. *context* is no longer passed
|
||||
to task factories if it is ``None``.
|
||||
|
||||
.. method:: loop.get_task_factory()
|
||||
|
||||
Return a task factory or ``None`` if the default one is in use.
|
||||
|
|
|
|||
|
|
@ -458,18 +458,24 @@ 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, **kwargs):
|
||||
"""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 context is not None:
|
||||
kwargs["context"] = context
|
||||
|
||||
task = self._task_factory(self, coro, **kwargs)
|
||||
task.set_name(name)
|
||||
|
||||
else:
|
||||
task = tasks.Task(coro, loop=self, name=name, context=context, **kwargs)
|
||||
if task._source_traceback:
|
||||
del task._source_traceback[-1]
|
||||
|
||||
task = tasks.Task(coro, loop=self, **kwargs)
|
||||
if task._source_traceback:
|
||||
del task._source_traceback[-1]
|
||||
try:
|
||||
return task
|
||||
finally:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
In 3.13.3 we accidentally changed the signature of the asyncio ``create_task()`` family of methods and how it calls a custom task factory in a backwards incompatible way. Since some 3rd party libraries have already made changes to work around the issue that might break if we simply reverted the changes, we're instead changing things to be backwards compatible with 3.13.2 while still supporting those workarounds for 3.13.3. In particular, the special-casing of ``name`` and ``context`` is back (until 3.14) and consequently eager tasks may still find that their name hasn't been set before they execute their first yielding await.
|
||||
Loading…
Add table
Add a link
Reference in a new issue