Skip to content

Breaking API Change in Python 3.13.3: Task Factory Signature Change #133745

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
pablogsal opened this issue May 9, 2025 · 6 comments
Open

Breaking API Change in Python 3.13.3: Task Factory Signature Change #133745

pablogsal opened this issue May 9, 2025 · 6 comments
Assignees
Labels
3.13 bugs and security fixes 3.14 bugs and security fixes 3.15 new features, bugs and security fixes release-blocker stdlib Python modules in the Lib dir topic-asyncio type-bug An unexpected behavior, bug, or error

Comments

@pablogsal
Copy link
Member

pablogsal commented May 9, 2025

In PR #128768 (gh-128308), the asyncio task factory signature was changed from (loop, coro, context=None) to (loop, coro, **kwargs). This seemingly minor change breaks backward compatibility for existing task factory implementations.

The documentation was updated to reflect this new contract:

  • Old: (loop, coro, context=None), where the callable must return a asyncio.Future-compatible object.
  • New: (loop, coro, **kwargs), where the callable must pass on all *kwargs, and return a asyncio.Task-compatible object.

This change causes runtime errors for any code with task factories implemented according to the previous contract, as these factories now unexpectedly receive keyword arguments like name that they aren't designed to handle.

Reproducer

import asyncio

# Task factory implemented according to the previous API contract
def old_style_task_factory(loop, coro, context=None):
    print(f"Creating task with loop={loop}, coro={coro}, context={context}")
    return asyncio.Task(coro, loop=loop)

async def main():
    # Set up a custom task factory following the old documented API
    loop = asyncio.get_event_loop()
    loop.set_task_factory(old_style_task_factory)
    
    # This will fail with TypeError due to unexpected 'name' argument
    print("Attempting to create task with name...")
    await loop.create_task(asyncio.sleep(0.1), name="test_task")
    print("Task completed successfully")  # We won't reach this line

if __name__ == "__main__":
    try:
        asyncio.run(main())
    except TypeError as e:
        print(f"ERROR: {e}")
        # Output: ERROR: old_style_task_factory() got an unexpected keyword argument 'name'

This prints:

Attempting to create task with name...
Creating task with loop=<_UnixSelectorEventLoop running=False closed=False debug=False>, coro=<coroutine object BaseEventLoop.shutdown_asyncgens at 0x72b413aa5450>, context=None
Creating task with loop=<_UnixSelectorEventLoop running=False closed=False debug=False>, coro=<coroutine object BaseEventLoop.shutdown_default_executor at 0x72b413cc5910>, context=None
ERROR: old_style_task_factory() got an unexpected keyword argument 'name'
<sys>:0: RuntimeWarning: coroutine 'sleep' was never awaited
RuntimeWarning: Enable tracemalloc to get the object allocation traceback

This is a breaking change introduced in a patch release, which violates our versioning policy. The change was intended to support the name keyword argument for eager tasks, but the implementation method has unintentionally broken existing code that followed the previously documented API contract.

CPython versions tested on:

3.14

Operating systems tested on:

Linux

Linked PRs

@pablogsal pablogsal added the type-bug An unexpected behavior, bug, or error label May 9, 2025
@picnixz picnixz added topic-asyncio stdlib Python modules in the Lib dir 3.13 bugs and security fixes labels May 9, 2025
@github-project-automation github-project-automation bot moved this to Todo in asyncio May 9, 2025
@picnixz picnixz added 3.14 bugs and security fixes 3.15 new features, bugs and security fixes release-blocker labels May 9, 2025
@picnixz
Copy link
Member

picnixz commented May 9, 2025

(I'm marking it as release-blocker just so that we can find the issue more easily)

@vstinner
Copy link
Member

vstinner commented May 9, 2025

I suppose that the following issue in the toga project is related to this:

There is already a commit to work around the issue.

@Yhg1s
Copy link
Member

Yhg1s commented May 9, 2025

@asvetlov @kumaraditya303 @graingert for 3.13, do you want to propose anything other than a rollback of the relevant PR (and anything that depends on it)? We'll probably want to do a new release to fix the breakage.

For 3.14 it may also be worth considering not breaking the documented API (/cc @hugovk), but we have a bit more time to get that sorted.

@serhiy-storchaka
Copy link
Member

No hurry. 3.13.3 was released a month ago, and only now we found out about that breakage. A few more days is not critical.

@serhiy-storchaka
Copy link
Member

Other breakage of that documented contract happened in 3.11 (bpo-46994/#91150).

I encountered a similar problem in #74696. It was needed to pass additional argument to the user registered function. I solved it by testing the optional supports_root_dir attribute of the function.

We could do the same here. The users which need to pass the name to the constructor (eager tasks) should set a special attribute of the factory. By default, the old method will be used. We should document that this is temporary, and the factory should support arbitrary keyword arguments.

@kumaraditya303
Copy link
Contributor

I created #133808 to revert the breaking change for 3.13.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3.13 bugs and security fixes 3.14 bugs and security fixes 3.15 new features, bugs and security fixes release-blocker stdlib Python modules in the Lib dir topic-asyncio type-bug An unexpected behavior, bug, or error
Projects
Status: Todo
Development

No branches or pull requests

6 participants