Skip to content

inconsistent behavior regarding asyncio-dangling-task (RUF006) #16811

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
amirsoroush opened this issue Mar 17, 2025 · 5 comments
Open

inconsistent behavior regarding asyncio-dangling-task (RUF006) #16811

amirsoroush opened this issue Mar 17, 2025 · 5 comments
Labels
question Asking for support or clarification rule Implementing or modifying a lint rule

Comments

@amirsoroush
Copy link

amirsoroush commented Mar 17, 2025

Summary

I see an inconsistent behavior in detecting RUF006 rule.

import asyncio


async def coro1() -> None:
    while True:
        print("inside coro1...")
        await asyncio.sleep(1)

async def main() -> None:
    global t1
    s = set()

    asyncio.create_task(coro1())        # 1  (detects correctly)
    t1 = asyncio.create_task(coro1())   # 2  (discards correctly)
    t2 = asyncio.create_task(coro1())   # 3  (?)
    s.add(asyncio.create_task(coro1())) # 4  (?)

    await asyncio.sleep(5)
    print("done")

asyncio.run(main())

Output for # 1 and # 3 is:

Store a reference to the return value of `asyncio.create_task`

Ruff correctly detects # 1 and correctly discards # 2(it has a reference in global). Now if Ruff decides to detect # 3 as error then # 4 should also be detected as error. There is no difference in how the returned references are stored.

If Ruff detects # 3 due to the fact that t2 is only referenced inside the contained coroutine(and creates a circular reference) then it's the same as # 4. One of them inside a local variable and one of them inside a set object. In this case it should only discards # 4 if the set object is defined outside in a global namespace.

I don't know what the correct decision is but I guess those should be treated equally.

I might have missed something here, I would be grateful if you correct me. Thanks.

Version

ruff==0.10.0 (shipped with VSCode Ruff extension version 2025.18.0)

@ntBre
Copy link
Contributor

ntBre commented Mar 17, 2025

Thanks for the report! I think this corresponds to one of the known false negative test cases added in #9060, but I don't see a tracking issue for it.

I'm not that familiar with these async semantics, but I think I agree with you that ruff is correctly flagging (3), which means it should also flag (4) since these are both local variables. (playground link)

@ntBre ntBre added the rule Implementing or modifying a lint rule label Mar 17, 2025
@MichaReiser
Copy link
Member

I think the reason ruff doesn't dedect 4 is because s.add could await its parameter in some form or another. This seems a case where type checking can find this isse easily but we'd have to encode a long list of known methods that don't accept tasks

@MichaReiser MichaReiser added the question Asking for support or clarification label Mar 17, 2025
@amirsoroush
Copy link
Author

I think the reason ruff doesn't dedect 4 is because s.add could await its parameter in some form or another.

I'm pretty sure s.add can't await its arguments. It's just a builtin method of set object and behave semantically like list.append. Whether the task is get executed immediately or not depends on the eagerness of the Task object itself which is another story.

@ntBre
Copy link
Contributor

ntBre commented Mar 17, 2025

I think @MichaReiser was referring to a more general case, where it's hard to tell the type of s. As a (likely silly) example,

class Awaiter:
    async def add(self, task):
        await task

s = Awaiter()
...
s.add(task)

ruff could currently handle specific cases like set if we can resolve its type within a single file, but a more general approach requires type checking (and likely multi-file analysis) to really know what s.add means in a given context. Otherwise we end up with the long list Micha mentioned of methods like set.add and list.append. So we'll need better type inference to handle this robustly.

@amirsoroush
Copy link
Author

@ntBre oops my bad. Yes I got you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Asking for support or clarification rule Implementing or modifying a lint rule
Projects
None yet
Development

No branches or pull requests

3 participants