-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
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
Warn about unused Awaitables (and potentially other objects) #2499
Comments
I'm pretty sure we've got another similar issue somewhere, but I can't find it. (About results that should never be discarded -- perhaps uncalled functions?) I'm not sure how easy this would be to do -- especially not the generalization, which probably would require us to invent some acceptable syntax for marking functions whose result should never be ignored. |
I think skipping generalization and focusing on |
Awaitable is a bit special too, because "is the value used" isn't quite the right check. For example, here's one of the cpython bots that had a missing The code was something like I was looking at this recently in the context of Trio, where the rules for using awaitables are much simpler: basically just, any call returning an awaitable should have an Is it possible to have a mypy plugin that adds a check like this? And can anyone point me at roughly where in the type checking process we'd want to be to perform a check like that? (I assume somewhere in |
I am not sure writing it as a plugin is the best solution. This will need addition of at least one new plugin hook. I think a better idea is to just add a flag to mypy. This looks like a common scenario. Also the implementation in mypy is very simple. We can add a flag |
That would probably be the easiest approach, yeah. But... this check really is only useful to projects using trio, not to projects using asyncio. Is mypy interested in carrying features like that? |
I have heard similar things from other people. We can start with something, and then maybe generalize the flag to be tunable, for example:
But we can start just with a binary flag |
I don't like the idea of just adding another opt-in warning for this. I'd much prefer if we could have something that is enabled by default, even if it doesn't catch all errors. There's a risk that the majority of users would not realize that such an option exists. What about always rejecting code such as the original example, as the first iteration of this feature: async def f() -> None:
pass
async def g() -> None:
f() # Should be an error Is there ever a good reason to write We could later warn about things like Later, we could consider adding a trio-specific stricter check (possible as a plugin). |
In Trio's case, we'd probably make it enabled by default in our official project template and advertise it prominently in the docs (we already have a whole discussion of this issue right in the tutorial). But yeah, I know what you mean.
If
I hear where you're coming from. But, it is frustrating that mypy has all the information to do a very straightforward, precise, and high-coverage check here, but it's blocked because we can't get at it :-/. Would there be any simple, acceptable change to the plugin interface that would make it possible to implement this as a plugin relatively soon? It looks to me like the two main blockers are:
|
Small plugin API changes are typically not controversial, so this is a possibility.
That would be one way to do it, but ordering issues would make this a bit awkward, as you point out. Another option would to add a new "check only" hook that is called after the signature hooks have already run and we have a final signature. The hook wouldn't be able to change the signature but it can perform additional checks, and it's guaranteed to run after the signature-related hooks. We could allow multiple plugins to define these hooks for a function.
We'd need another attribute in the relevant context named tuple, I guess. Can you create a new issue for the plugin feature? We can then focus on the non-trio/non-plugin aspects in this issue. |
OK, forked this off into #5650 and replied there. |
What about a local check, with the added assumption that a function expecting an This would automatically make functions like For example:
Whenever the awaited state is |
Is there likely to be any movement on this? I've been hitting this problem a lot lately, trying to refactor a bunch of sync code to be async and struggling to find every location that needs an |
As you see, not much is happening. I did implement this check in Quora's typechecker, pyanalyze (https://github.com/quora/pyanalyze); you can see the relevant tests at https://github.com/quora/pyanalyze/blob/26e622e249d5b101323647a29ede386d4de06f1e/pyanalyze/test_name_check_visitor.py#L1868. It is not nearly as well-tested as mypy, though, but feel free to give it a try. |
I have a generic proof of concept in #8951. It should help in this case too. I've meant to make a proper PR out of it for some time now, I'll try to get it done eventually. |
We had a similar discussion in this pyright issue. As others have mentioned in this thread, there are subtleties involved. I ended up implementing a check in pyright that covers the most common error cases (including the one at the top of this thread) while avoiding most false positives. If someone is interested in implementing a similar check in mypy, I'm happy to share the learnings from pyright. |
Thanks, I'll take a look at some of those. Would be nice if we can atleast decide on the correct approach and remove the needs-discussion label though. Is my proposal above (#2499 (comment)) reasonable? Or any issues with it? I think it would work 99% of the time and seems like a relatively easy approach to implement (although I'm not really familiar with the mypy codebase). |
There has recently been movement on this in the form of #12279 |
Looks like a good start. Judging by the examples, I assume this just checks that the return value is assigned to a variable or awaited. Which will catch the simplest mistakes like:
But, it won't yet check if the assigned variable is actually awaited. |
What about this design:
|
I think you have basically just described the purpose of
This is what I proposed above (#2499 (comment)), with an option to change whether an error occurs when passed to a function with |
anyio has the concept of DeprecatedAwaitables that warn when you await them - they are list, float and Noneish subclasses otherwise. Is the advice to do this? class DeprecatedAwaitable:
if not TYPE_CHECKING:
def __await__(self):
warnings.warn(..., stacklevel=2)
return None
yield |
I think trying to find whether all possible control flows await an awaitable is difficult for a type checker. How does it know if I pass it to a function whether that function will await it or not? The statement that passing it to a function is likely to await it is not true. For example appending to a list |
How frequently do you think a function will explicitly declare the type as If a function accepts |
That just handles unused awaitable expressions. Something as simple as I think we still need to have something like #2499 (comment) to cover a lot more cases. It's a bit more complex as you need to track the awaited state through a function in order to see if it was awaited by the end, but would catch a lot more mistakes. |
|
That only works if the variable is unused. You can then do |
Can mypy be configured to require type hints for unawaited assigns? async def do_something(): ...
async def main():
# These start as awaited=False
a = do_something() # ERROR
b : Awaitable = do_something() # OK
c = await do_something() # OK |
That is not an unusual thing to do though, so not sure how many people would find such an option useful. |
My reasoning behind this request is to make delayed calls an active decision and to prevent errors from forgotten awaits. |
This code:
is incorrect because
f()
isn't awaited. This will produce a runtime warning with the right debug settings, but mypy doesn't give any errors.It would be nice if mypy could catch this, since I think it's a pretty common error in async code. This could be generalized to warning on other types or function return values that shouldn't be ignored (maybe
open
?), similar to__attribute__((warn_unused_result))
in GCC.The text was updated successfully, but these errors were encountered: