Add an optional warning for returning Any from typed functions#2854
Add an optional warning for returning Any from typed functions#2854ddfisher merged 7 commits intopython:masterfrom
Conversation
…turn a more specific type (and the warning is enabled).
mypy/messages.py
Outdated
| MISSING_RETURN_STATEMENT = 'Missing return statement' | ||
| INVALID_IMPLICIT_RETURN = 'Implicit return in function which does not return' | ||
| INCOMPATIBLE_RETURN_VALUE_TYPE = 'Incompatible return value type' | ||
| RETURN_ANY = 'Returning Any from function declared with return type {}' |
There was a problem hiding this comment.
Normally, types and type names are put in quotes, e.g. like this: '... with return type "{}"'
ddfisher
left a comment
There was a problem hiding this comment.
Good idea! The implementation looks good, too -- just have a few small nits and then will merge!
Thinking out loud here: this helps catch accidental Anys in one particular circumstance, but it might be nice to have something a bit more comprehensive as well. Perhaps we should consider adding a flag which disallows use of Any-typed values except as the right hand side of an assignment with a type comment (i.e. forcing all Anys to be immediately typed).
mypy/main.py
Outdated
| add_invertible_flag('--no-warn-no-return', dest='warn_no_return', default=True, | ||
| help="do not warn about functions that end without returning") | ||
| add_invertible_flag('--warn-return-any', default=False, strict_flag=True, | ||
| help="warn about returning objects of type Any") |
There was a problem hiding this comment.
Maybe add "from non-Any typed functions".
There was a problem hiding this comment.
Also, "objects" -> "values".
mypy/messages.py
Outdated
| MISSING_RETURN_STATEMENT = 'Missing return statement' | ||
| INVALID_IMPLICIT_RETURN = 'Implicit return in function which does not return' | ||
| INCOMPATIBLE_RETURN_VALUE_TYPE = 'Incompatible return value type' | ||
| RETURN_ANY = 'Returning Any from function declared with return type "{}"' |
There was a problem hiding this comment.
Tiny nitpick: I think this reads better as "...with declared return type...".
| from typing import Any | ||
| def g() -> Any: pass | ||
| def f(): return g() | ||
| [out] |
There was a problem hiding this comment.
It would also be good to have a test for returning Any from a function declared to return Any.
|
Good feedback. Addressed all of the above. |
|
(And regarding a more broad check around use of Any, I agree that seems potentially very useful. I may look into that at some point if I get some more time to work on stuff!) |
|
Thanks! |
I noticed that I often wanted to write "safe" wrappers around untyped library code. There's an example in the mypy source,
mypy/git.py:Note that
subprocess.check_outputreturns an Any for convenience (its actual type depends on the encoding parameter). If this author had accidentally written .split() instead of .strip(), this error would pass silently but the calling code would fail at runtime, since a caller would receive aList[bytes]when they expected a strippedbytes. I realize that use of Any invites runtime type errors, but it would be nice to at least know when the possibility of runtime errors is "leaking" out of a function definition.This PR adds an optional warning which lets you know when you're returning Any from a function which is documented to return a non-Any type. The warning flags the above code. The user could then silence the warning by changing their code to:
For the curious: the output from turning on this warning and running it on the mypy source is reasonable in size, and indeed flags things worth looking-at:
The stubutil, nodes and types ones are doing something very dynamic (accessing
__dict__.) The git ones are calling subprocess.check_output. I don't understand what fastparse.py is doing yet to comment on those instances.However, parse.py is extremely interesting! The
exprvariable's type is declared to be Expression at the beginning of that function. Addingreveal_type(expr)at various places indicates that after the block of if/elif/... where it dispatches toself.parse_*and assigns the result toexpr, the type ofexprhas been magically changed to Any. I am mystified by what mypy is doing here; I tried to go source-spelunking but am a bit too new to the project to figure out what exactly is going on. If I do manage to reproduce it in a smaller test case I'll file a separate issue.