python / mypy

Optional static typing for Python
https://www.mypy-lang.org/
Other
18.5k stars 2.83k forks source link

no error when value of type `type` (aka `type[Any]`) passed to function with generic with constraint, generic inferred as the first constraint #12300

Open DetachHead opened 2 years ago

DetachHead commented 2 years ago
from typing import TypeVar

T = TypeVar("T", int, str)

def foo(cls: type[T]) -> T:
    ...

asdf: type

reveal_type(type) # error: Expression type contains "Any"

a = foo(asdf) # no error

reveal_type(a) # int

https://mypy-play.net/?mypy=master&python=3.10&flags=show-error-codes%2Callow-redefinition%2Cstrict%2Ccheck-untyped-defs%2Cdisallow-any-decorated%2Cdisallow-any-expr%2Cdisallow-any-explicit%2Cdisallow-any-generics%2Cdisallow-any-unimported%2Cdisallow-incomplete-defs%2Cdisallow-subclassing-any%2Cdisallow-untyped-calls%2Cdisallow-untyped-decorators%2Cdisallow-untyped-defs%2Cno-implicit-optional%2Cno-implicit-reexport%2Cstrict-equality%2Cwarn-incomplete-stub%2Cwarn-redundant-casts%2Cwarn-return-any%2Cwarn-unreachable%2Cwarn-unused-configs%2Cwarn-unused-ignores&gist=66b8feecd9c40d2c3fab75ff146b43b3

KotlinIsland commented 2 years ago

I think this is three issues in one 😳

  1. mypy doesn't care about type without type parameter and hides Any errors #12301
  2. type[T] generics default to <nothing> when they are any
    
    from typing import _T

def foo(t: type[_T]) -> _T: ...

t: type reveal_type(foo(t)) #

3. Constrained `TypeVars` default to their first constraint when they are `any`ified example in OP, and:
```py
from typing import TypeVar

T = TypeVar("T", int, str)

def foo(cls: type[T]) -> T: ...

a = []

reveal_type(foo(a)) # int

This seems quite broken.

flisboac commented 2 years ago

Regarding item 2, PEP-483 says the following:

If a generic type appears in a type annotation with a type variable omitted, it is assumed to be Any.

So, it is a bug. Curiously, with Type the correct behaviour is shown:

from typing import TypeVar, Any, Type

T = TypeVar('T')

t: Type

def test(cls: Type[T]) -> T: ...

reveal_type(t)  # note: Revealed type is "Type[Any]"

reveal_type(test(t))  # note: Revealed type is "Any"