Skip to content
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

Detect unawaited coroutines #9833

Open
alekssamos opened this issue Feb 5, 2024 · 14 comments · May be fixed by #9911
Open

Detect unawaited coroutines #9833

alekssamos opened this issue Feb 5, 2024 · 14 comments · May be fixed by #9911
Assignees
Labels
rule Implementing or modifying a lint rule type-inference Requires more advanced type inference.

Comments

@alekssamos
Copy link

Hello. I fixed my code via ruff.
Applied fix and unsafe fixes.
But ruff did not find one error.
I forgot to write await when calling an asynchronous function.
Accordingly, I received RuntimeWarning never awaited...
e.g.

async def another():
    ...

async def main():
    result = another()

Is there any way to do this, and if not, will it be taken into account in the new versions of ruff?

@charliermarsh charliermarsh added the rule Implementing or modifying a lint rule label Feb 5, 2024
@charliermarsh
Copy link
Member

I don't believe we check this now, but it looks like a reasonable rule to me.

@charliermarsh charliermarsh changed the title Question: how do I detect a forgotten await? Detect async function without await Feb 5, 2024
@mikeleppane
Copy link
Contributor

WIP

@AlexWaygood
Copy link
Member

AlexWaygood commented Feb 9, 2024

Note that mypy already implements this rule with its unused-awaitable error code: https://mypy.readthedocs.io/en/stable/error_code_list2.html#check-that-awaitable-return-value-is-used-unused-awaitable.

That doesn't stop ruff implementing it as well -- but I feel like it might be a better fit for a type checker, as a type checker has the power to analyse multiple files at a time and track which calls return awaitable objects. Ruff doesn't (yet) have the power to do that (though it's obviously something we'd like to have).

Still, it can be hard to configure mypy to enable just that warning without also enabling other warnings that might be unwelcome -- and maybe we can provide a valuable lint here even while only looking at one file at a time.

@charliermarsh
Copy link
Member

@AlexWaygood - I guess the rule as implemented here is actually a bit different (unclear if better or worse) than what's described in the Mypy docs, since this raises whenever you have an async method that doesn't await anything (like Clippy's unnecessary async rule), whereas the Mypy rule seems to raise on the awaitable rather than the async function. Is that correct?

@AlexWaygood
Copy link
Member

AlexWaygood commented Feb 9, 2024

@AlexWaygood - I guess the rule as implemented here is actually a bit different (unclear if better or worse) than what's described in the Mypy docs, since this raises whenever you have an async method that doesn't await anything (like Clippy's unnecessary async rule), whereas the Mypy rule seems to raise on the awaitable rather than the async function. Is that correct?

Ah interesting -- I think what you describe is possibly what's originally being asked for in this issue, but the implementation that's been put forward for consideration in #9911 is more similar to mypy's unused-awaitable check than what you're describing

@charliermarsh
Copy link
Member

Oh sorry, I hadn't looked at the code yet. I was imagining we'd look for async function definitions that lack any await or async iterator (like async for, etc.).

@zanieb
Copy link
Member

zanieb commented Feb 9, 2024

Yeah it sounds like #9911 addresses a separate (but also valid) use case

@plredmond
Copy link
Contributor

plredmond commented Feb 9, 2024

Hi, I've blocked out time to do this on Monday.

[Edit: I'll work on #9951, not this.]

@zanieb zanieb changed the title Detect async function without await Detect unawaited coroutines Feb 12, 2024
@zanieb zanieb assigned mikeleppane and unassigned plredmond Feb 12, 2024
@Gu1nness
Copy link

Do you know when this will be implemented?
This would be a great addition to ruff!

@MichaReiser
Copy link
Member

This will take a while. Ruff cannot currently resolve functions across files, which is a requirement for this rule to work reliable.

@MichaReiser MichaReiser added type-inference Requires more advanced type inference. and removed multifile-analysis labels Oct 24, 2024
@mikeckennedy
Copy link

I want to just add a strong +1 here for detecting non-awaited coroutines. Check out the discussion here:

https://www.reddit.com/r/Python/comments/1gulzjt/rewriting_4000_lines_of_python_to_migrate_to/

One of the commenters says they have a huge code base but can not (never?) migrate to async because it's just too hard to make sure all the code works together (like no missed awaits). This feature would make Ruff a must have tool for such upgrades.

However, I realize this is super hard. For example, this should be fine even though the coroutines are not directly awaited.

t1 = async_thing_1()
t2 = async_thing_2()

await task.gather(t1, t2)

@Zac-HD
Copy link

Zac-HD commented Feb 5, 2025

Note that the gather() example is only a difficulty for asyncio; neither anyio nor trio permit calling async functions without immediately awaiting them, in large part to avoid this kind of confusion. Ruff could be very valuable for these use-cases, even if the rule had to be disabled for asyncio codebases, or at least disable-if-retval-used.

(idiomatically, you'd call my_gather(partial(fn1, *args), partial(fn2, **kwargs), ...) - a list of functions)

@mikeckennedy
Copy link

Hey @Zac-HD nice to see you here. I'm somewhat familiar with Trio and AnyIO. But how do you actually run two or more things concurrently without starting them before awaiting them?

for x in xes:
   await async_thing(x)

still only runs one thing at a time. And await gather(f(x), g(x), ...) is fine but can be tricky if you have to build up the args for each one in a loop.

@zanieb
Copy link
Member

zanieb commented Feb 8, 2025

@mikeckennedy I believe the canonical example would be using TaskGroup.start_soon (https://anyio.readthedocs.io/en/stable/tasks.html#creating-and-managing-tasks)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
rule Implementing or modifying a lint rule type-inference Requires more advanced type inference.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

10 participants