Description
In general, we want to provide the user with a flag to disallow the use of implicit and explicit Any types as well as expressions with type Any, so that they can make sure all of their code is typechecked.
@ddfisher and I did some brainstorming yesterday and this proposal is what we came to.
Here are all the places that Any
can appear in, followed by discussion of the current state of the flags and the proposal for a new flag.
Types
Explicit
A user can explicitly specify an Any
type in variable definition (x: Any = "hello"
), function definition(def foo() → Any: ...
), a cast(cast(Any, x)
), class definition (class C(Any)
), NewType
, a TypeVar
, NamedTuple
, or part of a type(List[Any]
).
Implicit
Unimported
Unimported types happen when we try to use a type from an import from a module we don’t analyze. This could happen if we’re using --follow-imports=skip
or --ignore-missing-imports
.
Consider the following example
# unanalyzed.py
class C:
pass
# test.py
from unanalyzed import C
def foo(c: C) -> str:
return c
If you run mypy test.py --follow-imports=skip
, the code typechecks from mypy’s point of view. Because C
is unresolved, it implicitly becomes an alias for Any
.
Omitted Generics
This case occurs when the user does not explicitly specify a generic parameter for a type. For instance, List
in a type position becomes List[Any]
. It is easy to forget to specify a generic parameter. This can also happen with e.g. lower-case list
from builtins.
We should consider disallowing omitting the generic parameter and maybe forbid using builtin types in favor of types from typing
module. The proposal for it is here.
Omitted Function Signature element
When a user does not specify an explicit type for a parameter or a return type, it becomes Any
.
For example, the function def foo(x) → None: …
implicitly becomes: def foo(x: Any) → None: …
From Error
Any
types can come from an ignored error. For instance:
from typing import Union
T = Union[UhOh] # type: ignore
x: T
reveal_type(x) # error: Revealed type is 'Any'
Expressions
An expression of type Any
can come from any Any
types described above.
For instance, x + y
gets type Any
when x
has type Any
even if y
doesn't.
In real-life code, it would be challenging to achieve no Any
expressions in the entire module. Note that if the user is calling an untyped method from a different module, expression can have an Any
type, even if all Any
types are disallowed in the current module. Still, users may want the ability to disallow expressions of type Any
completely in certain circumstances.
Current State
Unimported types
Currently, there is a PR (#3405) that adds a flag to disallow unimported types.
Omitted Generics
There is currently a PR for a flag that would give a warning on omitted generic types. I believe we should at the very least have it on by default. Ideally, we should forbid omitted generics.
Omitted Function
Flag --disallow-untyped-defs
is already implemented in #1285. This functionality should be merged into the new flag.
From Error
Currently, there is no flag to disable it.
Expressions
Currently, there is no flag to disable it.
Flag
All of these are very similar in their nature and it would make sense to have one flag to rule them all.
It is generally worth it to provide a user with an option to disable each of the types of Anys.
There are several ways to do it.
Have a different flag for each type
Currently, in order to disable a type of Any, you have to use a separate flag for each type. At the very least we should name the flags with similar names because the options are very closely related to each other.
Have several “umbrella” flags
An option would be to have several flags that would cover different types. For instance, --disallow-unimported-any-types
, --disallow-ommited-function-types
, --disallow-implicit-any-types
, --disallow-explicit-any-types
, and --disallow-all-any
.
However, having this many flags is cumbersome, especially when they overlap. The documentation and usage would be confusing.
Have One Flag with List of Options
I think the best option is to have one flag that takes a list of arguments.
This could look something like this:
--disallow-any=...
where ...
can be all
, unimported
, omitted
,error
, expr
We should also be able to combine several into one, like this: --disallow-any=unimported,omitted
. This combination, for example, would disallow imported types and disallow omitted function types.
If the flag parameters are all
+ any other flag, we should give an error saying that it’s redundant and linking to documentation.