Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Allow disabling implicit Any for type aliases #2696

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

Closed
mohabusama opened this issue Jan 16, 2017 · 10 comments
Closed

Allow disabling implicit Any for type aliases #2696

mohabusama opened this issue Jan 16, 2017 · 10 comments

Comments

@mohabusama
Copy link

The issue is somehow discussed here #2690

Here is a sample code that mypy allows and eventually fails in runtime

from typing import *
T = TypeVar('T', List[int], Dict)
Processor = Callable[[T], None]
def output(inp: T, processor: Processor) -> None:
    processor(inp)
def process_dict(inp: Dict) -> None:
    print(inp.keys())
output([1, 2, 3], process_dict)

As described in #606 discussions and https://www.python.org/dev/peps/pep-0484/#type-aliases this is not a problem with mypy, but an overlooked mistake in non-parameterizing generic type aliasProcessor in output.

reveal_type for output:
Revealed type is 'def [T in (builtins.list[builtins.int], builtins.dict)] (inp: T-1, processor: def (Any))'`

Processor is implicitly treated as Processor[Any] in this case.

Changing output to def output(inp: T, processor: Processor[T]) -> None: resolves the issue and lets mypy catch the bug.

The problem is that implicit Any for type aliases can easily go unnoticed.

Suggestions:

  1. As suggested by @roganov & @ilevkivskyi : Add --disable-implicit-any flag to mypy, which will disable this behavior and forces the user to be explicit. Drawback, users who do not know about the flag might learn about the bug the hard way (in production 😉 )

  2. Disable implicit Any by default for type aliases, and force users to be explicit (either by parameterizing [Any] or [T]). Drawback, might introduce a lot of mypy errors for existing, "error free" code base (confusing/annoying to users). Also, not entirely sure about how this impacts other scenarios.

  3. Produce warning when used type alias has no explicit parameterization. This will make mypy exit without failure, but just point the user to potential bugs. Drawback, warnings might be annoying for users who are ok with their codebase (i.e. might require --suppress-implicit-any-warning)

  4. Any other suggestion ... 😄

Thanks,

@JukkaL
Copy link
Collaborator

JukkaL commented Jan 16, 2017

One option would be to use deprecate --disallow-untyped-defs and use --disallow-implicit-any as a superset of that option + checking that there no implicit Any types for other reasons, such the example above.

@ilevkivskyi
Copy link
Member

+1 to Jukka's idea. We have at least three places where an absence of type is equivalent to Any:

  • Untyped definitions: def f(x, ...): ... is equivalent to def f(x: Any, ...): ...
  • Normal generics x: Iterable is equivalent to x: Iterable[Any]
  • Generic aliases

Maybe there others that I don't remember. It would be difficult to remember what is the option to disable each usage.

@gvanrossum
Copy link
Member

Hm, I expect that the "x: Iterable" case is very common. Would be worth doing a little experiment with that: find the code that implements this, make it raise an error instead, and see what it catches in some real-world codebases, starting with mypy. FWIW the pattern '\[AnyType\(\)\]\s*\*' seems to find many of these (I count 11).

@pkch
Copy link
Contributor

pkch commented Feb 24, 2017

I'm very much in favor of --disallow-implicit-any.

However, this isn't currently allowed (not sure why):

from typing import *
T = TypeVar('T')
x: List[T] # Invalid type
ListAlias = List[T]
y: ListAlias[T] # Invalid type
def f() -> None:
    x: ListAlias[T] # Invalid type

So when we need to define variables that belong to a generic container type, but without the item type being part of the type of the function call arguments, we have to rely on implicit Any like this:

from typing import *
T = TypeVar('T')
x: List
ListAlias = List[T]
y: ListAlias
def f() -> None:
    x: ListAlias

Therefore I think either the proposed --disallow-implicit-any flag should not create warnings in the second code snippet; or the first code snippet should be made legal.

@ilevkivskyi
Copy link
Member

or the first code snippet should be made legal.

But what you expect to be the meaning of this? Type variables are only used for generic functions, and generic types and aliases. What would be the meaning of an unbound type variable in an annotation?

@pkch
Copy link
Contributor

pkch commented Feb 24, 2017

I agree, x: SomeGenericContainerAlias[T] outside a function signature leaves mypy no chance to solve for T; however, it can still be used to define a generic container if someone prefers to completely eliminate reliance on implicit Any. I didn't mean to propose that, just listing it as an option.

My main point was that currently we often rely on implicit Any with a normal generic or a generic alias to specify a container type (as in my second code snippet). So --disallow-implicit-any shouldn't complain in those cases.

@gvanrossum
Copy link
Member

It feels like a special case is needed for aliases.

When C is a generic class (defined with a class statement that uses a type variable in a base class), I think we should still allow using x: C as a shorthand for x: C[Any].

But when A is a generic alias (defined with an assignment statement using a type variable on the right-hand side) the experience from #2690 and the discussion here suggest that it may be better to outright reject using x: A, instead requiring x: A[Any] if that's what is meant, or (in the parameter list of a generic function or in the body of a generic class) x: A[T].

@ddfisher
Copy link
Collaborator

I'm not sure I understand why we'd want to make that distinction. It seems like a good idea to keep the behavior of aliases and classes as similar as possible.

@gvanrossum
Copy link
Member

gvanrossum commented Feb 28, 2017 via email

@ilevkivskyi
Copy link
Member

I think this is now fixed since we have --disallow-any=generics in 0.540 that also catches implicit Any in generic aliases. (IIRC it was replaced by a dedicated flag in a recent PR.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants