Where should the task lifecycle warning belong in asyncio docs?

Should the important note be added to loop.create_task() as well, similar to the one in asyncio.create_task()? Or should that note instead be moved to asyncio.Task?

Important

Save a reference to the result of this function, to avoid a task disappearing mid-execution. […]

Currently, this note appears in asyncio.create_task() and asyncio.ensure_future()

Adding it to loop.create_task() would result in duplication across three places.

I have no opinion on the documentation. But even though it’s just how Python’s GC and ref counting works, this has always struck me as odd.

In which situations, is the possibility of having tasks randomly disappearing mid-execution acceptable, or even the user’s intention?

The conceptual overview of tasks describes the need to hold references. I think function reference sections should link to there with only a brief one-line description:

Important: you must keep a reference to the task to ensure it doesn’t disappear before finishing. See the conceptual overview of tasks for more details.

Code examples and longer explanation can be in the conceptual overview, once.

1 Like

There are cases where managing task outcomes (results, timing, failures) is not worth it at all. The task is best-effort: if it runs late or fails, it has no value. Think of soft real-time system scenarios such as metrics collection, best-effort caching, or low-priority UI updates.

However, it is still recommended to follow proper fire-and-forget practices, as described in the important note.

1 Like

Those are great examples - thanks.

Anyway, it’s no hardship at all to add a local variable, or append to a list, in order to get guarantees of task execution, predictable behaviour instead of GC-dependent-stochastic behaviour, and code that’s easier to reason about.

Personally I just think those benefits should be opt out, instead of opt in, e.g. with a kwarg.

2 Likes

I’ve just realised, I’ve never been hit by GC / unreferenced Task shenanigans, as I’ve been using TaskGroup.create_task(*) instead (and haven’t yet been able to reproduce them with asyncio.create_Task).

Also, the entire warning contains: “The event loop only keeps weak references to tasks.”, so clearly this was the intent of the creators. I suppose maintaining both a weakset, and a set (and other work arounds) is a little clunky, but I’m still curious to learn more.

(*)


class TaskGroup:

    def __init__(self):
        ...     
        self._tasks = set()


    def create_task(self, coro, **kwargs):
        ...
        self._tasks.add(task)

I couldn’t find any previous discussion here, but here is one from Stack Overflow: