Inferred TypeVar information lost through function calls
When a typevar is used in a function call, sub-types of the typevar are lost. e.g. def foo(T) -> T, when given List[int] as the input will return List. This only seems to happen when the generic'd type is inferred.
from typing import TypeVar, List
T = TypeVar('T')
def accept(x: T) -> T:
return x
a: List[int] = [1, 2]
b = [1, 2]
reveal_type(a) # outputs List[int]
a_out = accept(a)
reveal_type(a_out) # outputs List[int]
reveal_type(b) # outputs List[int]
b_out = accept(b)
reveal_type(b_out) # outputs list (unexpected)
Expected: the last reveal_type should output List[int] Actual: the last reveal_type outputs list
Weird. We have a bug for losing information about functions when they're passed through annotated decorators, but I'm surprised that the same happens for other types.
I ran into the same for decorators specifically. If I add @pytypes.typechecked to a class to add runtime type checking, that sadly hobbles the static type inference and aliases the type to Any.
@rchen152 was that bug about decorators in some other tracker? In GitHub I only found this bug about decorators.
Ah yes, the decorators bug was in our internal tracker (and will be fixed in the next release, actually). The bug there was that things like
def decorator(func: T) -> T: ...
@decorator
def f(): ...
didn't work properly when the decorator and the decorator function were in the same file.
The @pytypes.typechecked issue is probably caused by us having no type information at all for pytypes. To fix that, someone would have to add stubs for pytypes to https://github.com/python/typeshed.
I also took another look at the original issue that @rickeylev opened. The problem there is that for (x: T) -> T, we get the return type from the .cls attribute of the input value (https://github.com/google/pytype/blob/9c6e5f1bdbb037f25cb84475a7ccafa5b2b14988/pytype/convert.py#L338), which for a list instance with an inferred type is List[Any] (which makes sense in most cases, since the contained type might change as things are appended to the list).
Ah thanks @rchen152! I had tried several workarounds like defining my own decorator wrappers with explicit type annotations and using the more literal MyType = typechecked(MyType) syntax instead, but definitely wouldn't have thought to try defining them in a separate file.
I filed python/typeshed#3991 to add pytypes to typeshed.
Oh, defining your own wrapper should work. (Support for that in pytype is quite recent, so it might not have been working when you originally tried it.) Something like:
from typing import TypeVar
import pytypes
T = TypeVar('T')
def typechecked(x: T) -> T:
return pytypes.typechecked(x)
should do it.