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.
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.
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.
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.