This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: asyncio.create_task() documentation should mention user needs to keep reference to the task
Type: enhancement Stage:
Components: Documentation Versions: Python 3.11, Python 3.10, Python 3.9
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: docs@python Nosy List: alexhartl, bernat, cmeyer, docs@python, nanjekyejoannah
Priority: normal Keywords: patch

Created on 2021-07-18 06:01 by bernat, last changed 2022-04-11 14:59 by admin.

Pull Requests
URL Status Linked Edit
PR 29163 merged nanjekyejoannah, 2021-10-22 14:39
Messages (5)
msg397741 - (view) Author: Vincent Bernat (bernat) * Date: 2021-07-18 06:01
asyncio will only keep weak references to alive tasks (in `_all_tasks`). If a user does not keep a reference to a task and the task is not currently executing or sleeping, the user may get "Task was destroyed but it is pending!".

I would suggest adding the following paragraph to `create_task()` documentation:

Python only keeps weak references to the scheduled tasks. To avoid the task being destroyed by the garbage collector while still pending, a reference to it should be kept until the task is done.

And maybe an example in case a user wants something "fire and forget"?

```python
running_tasks = set()
# [...]
task = asyncio.create_task(some_background_function())
running_tasks.add(task)
task.add_done_callback(lambda t: running_tasks.remove(t))
```

The same applies to ensure_future as it now uses create_task, so maybe a "See create_task()".
msg404776 - (view) Author: Joannah Nanjekye (nanjekyejoannah) * (Python committer) Date: 2021-10-22 14:40
@bernat and ncoghlan, please see if the wording I have used in the linked PR helps to clarify this.
msg404782 - (view) Author: Chris Meyer (cmeyer) * Date: 2021-10-22 15:27
Is there a way to reproduce this issue? I run the following code in Python 3.9 and it works as expected (prints "xyz" twice).

import asyncio
import gc

async def xyz():
    print("xyz")

event_loop = asyncio.get_event_loop()

event_loop.create_task(xyz())

t = event_loop.create_task(xyz())
del t

gc.collect()

event_loop.stop()
event_loop.run_forever()
msg404827 - (view) Author: Vincent Bernat (bernat) * Date: 2021-10-22 21:18
Hummm, I have a hard time finding a short example when `__del__` is not called until the task is finished. Sorry. I did run into this several time, for example here: https://github.com/ldo/dbussy/pull/45 (and some internal projects as well). So, it happens from time to time, but it is hard to find a simple reproducer.
msg409821 - (view) Author: Alexander Hartl (alexhartl) Date: 2022-01-06 08:53
I just found this PR when a task of mine spontaneously crashed with a "Task was destroyed but it is pending" in the middle of program execution.

I think the warning should be added to `loop.create_task()`, too. Not sure if `loop.call_later()` and `loop.call_at()` are also affected?

I think it would be a good idea to add the fire-and-forget example that @bernat gave. At the moment, stackoverflow is full of suggestions to just use `create_task()` in this case, ignoring the return value. Actually, I think it is a true shortcoming that asyncio doesn't provide a fire-and forget functionality by itself.
History
Date User Action Args
2022-04-11 14:59:47adminsetgithub: 88831
2022-01-06 08:53:46alexhartlsetnosy: + alexhartl
messages: + msg409821
2021-10-22 21:18:59bernatsetmessages: + msg404827
2021-10-22 15:27:20cmeyersetnosy: + cmeyer
messages: + msg404782
2021-10-22 14:40:58nanjekyejoannahsetmessages: + msg404776
stage: patch review ->
2021-10-22 14:39:29nanjekyejoannahsetkeywords: + patch
nosy: + nanjekyejoannah

pull_requests: + pull_request27437
stage: patch review
2021-08-24 02:30:11ncoghlanlinkissue42538 superseder
2021-07-18 06:01:26bernatcreate