Skip to content

Flag for disallowing Any #3470

Closed
Closed
@ilinum

Description

@ilinum

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.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions