Setting a asyncio task factory erases task name and it only happens in 3.13

I am currently working on a python library that sets asyncio task factory that basically adds a new attribute for every asyncio.Task.
( A python library that I am working on but encoutered troubles · GitHub )

I don’t know why but asyncio.current_task().get_name() returns “None” for every new task once the task exits the task factory function I set, and it only happens in 3.13.

(The same github gist also provices output on 3.9, 3.12 and 3.13, I tested 3.9 to 3.13 and this particular issue only exists in 3.13)

Please someone tells me what is going wrong?

I tried to produce a minimal code that reproduces this behavior… hope someone can take a look at it.

import asyncio

def task_factory(loop, *args, **kwargs):
    new_task = asyncio.Task(*args, **kwargs)
    name = new_task.get_name()
    # Python 3.9 to 3.13 prints name='Task-2'
    # 'Task-3' and 'Task-4' is also printed because loop is shutting down, which is not in the scope of (possible) bug
    print(f"{name=}")
    return new_task

async def print_task_name():
    current_task = asyncio.current_task()
    name = current_task.get_name()
    # Python 3.13 prints name='None' (but why does it print 'Task-2' before it's returned from task_factory() ?)
    # Python 3.9 to 3.12 prints name='Task-2'
    print(f"{name=}")

async def main():
    loop = asyncio.get_running_loop()
    loop.set_task_factory(task_factory)
    new_task = asyncio.create_task(print_task_name())
    await new_task

asyncio.run(main())

Edit: relevant GitHub issue link I opened In 3.13.* if custom asyncio task factory is set and no task name is provided to asyncio.create_task, task name provided by task factory is overwritten to "None". · Issue #130033 · python/cpython · GitHub

After debugging with pdb I saw something crucial to my problem

(Pdb) n
> /home/control/asyncio-task-ancestors/bug_reproduce.py(17)task_factory()
-> return new_task
(Pdb) n
--Return--
> /home/control/asyncio-task-ancestors/bug_reproduce.py(17)task_factory()-><Task pending...roduce.py:19>>
-> return new_task
(Pdb) n
> /home/control/.local/share/uv/python/cpython-3.13.2-linux-x86_64-gnu/lib/python3.13/asyncio/base_events.py(478)create_task()
-> task.set_name(name)

This line sets task name, which in the call is None, and set_name sets task name to str(None).

As to why this only affects 3.13, it turns out versions before 3.13 calls a different function to set task name (cpython/Lib/asyncio/base_events.py at v3.12.9 · python/cpython · GitHub) (cpython/Lib/asyncio/tasks.py at v3.12.9 · python/cpython · GitHub) which does nothing if name is None instead of converting None to string

I think this is a bug because even if I do something like “different task naming scheme” or subclass asyncio.Task in my custom task_factory(), task’s name gets set to None if no name is passed to asyncio.create_task.