This bug report is coming from https://github.com/python/mypy/issues/8946 and https://github.com/ltworf/typedload/issues/132
Basically the pattern
def f(t: Type[T]) -> T: ...
Works for some types but not others, and I can't see a pattern. For example it will work for a NamedTuple but not for a Tuple.
See this example:
from typing import *
from dataclasses import dataclass
T = TypeVar('T')
@dataclass
class P:
a: int
class Q(NamedTuple):
a: int
def create(t: Type[T]) -> T: ...
# These ones work
reveal_type(create(int))
reveal_type(create(str))
reveal_type(create(List[int]))
reveal_type(create(Dict[int, int]))
reveal_type(create(Q))
reveal_type(create(P))
# These do not
reveal_type(create(Tuple[int]))
reveal_type(create(Union[int,str]))
reveal_type(create(Optional[int]))
Now in the older mypy, the ones that do not work would just be understood as Any, which is absolutely not the intended behaviour. In the latest release instead they fail with error: Argument 1 to "create" has incompatible type "object"; expected "Type[<nothing>]" (and I have no idea of what this means).
Anyway in my view, all of the examples provided should work.
In case I'm wrong, then none of them should work, and probably a way to achieve this is needed.
Background: I am working on a library to load json-like things into more typed classes, and it promises to either return an object of the wanted type, or fail with an exception.
This is similar also to #8992.
Ah yes, sorry for not linking that one too. But that is about the change that happened in mypy. The change is possibly good since the type isn't being inferred. I'm thinking it should either be or not be in all cases.
List[int] and Dict[int, int] should have the inferred type object in an expression context, since they can't be used as type objects at runtime.
The error messages given are pretty confusing, but it can be tricky to generate a significantly better error message here.
Why can't they be used at runtime?
This works
from typedload import load
from typing import *
a = load(('1', 1, 1.1), List[int])
assert a == [1, 1, 1]
Just now I encountered another strange failure caused by this issue:
from attr import attrs, attrib
from typing import Optional
def validate(*args, **kwargs) -> None:
...
@attrs
class Qwe:
v = attrib(type=Optional[int], default=None, validator=lambda i, _, v: validate(i.address, v))
@attrs
class Asd:
v: Optional[int] = attrib(default=None, validator=lambda i, _, v: validate(i.address, v))
Both classes do the same thing, however the 1st one fails and the 2nd one is ok with mypy.
The error only seem to happen if the validator parameter is a lambda, just passing a funciton doesn't trigger it.
Is there a new, better way to say "thing that mypy treats as a type, at runtime" that isn't Type[]? I understand why Type is misleading given that it implies a specific run-time protocol, and it would be cool to get safety around that, but if one has lots of code that can already deal with Unions et. al., it's not quite clear how to describe its signatures any more.
For now, I'm resorting to this:
from typing import TYPE_CHECKING, Generic, TypeVar
T = TypeVar("T")
if TYPE_CHECKING:
class MyType(Generic[T]):
"A MyPy type object."
else:
class _ProtoMyType(object):
def __getitem__(self, t):
return lambda: t
MyType = _ProtoMyType()
...
def create(t: Union[Type[T], MyType[T]) -> T:
"runtime magic lives here"
This seems pretty hacky though.
I've another issue that seems to be related.
from typing import Type, TypeVar, Union
T = TypeVar("T")
def foo(obj: Union[Type[T], T]) -> T:
...
class Bar:
baz: int
foo(Bar).baz
# Mypy: <nothing> has no attribute "baz"
# Mypy: Argument 1 to "foo" has incompatible type "Type[Bar]"; expected "Type[<nothing>]"
My current workaround is to use overload with
@overload
def foo(obj: Type[T]) -> T:
...
@overload
def foo(obj: T) -> T:
...
and error disappear.
Hope it help.
Background: I am working on a library to load json-like things into more typed classes, and it promises to either return an object of the wanted type, or fail with an exception.
I'm working on the same kind of library (see my implementation here!) and running into this same issue.
For example I want to be able to have a function with signature:
def trycast(type: Type[T], value: object) -> Optional[T]: ...
And then call it like this:
response_json: object
int_or_str = trycast(Union[int, str], response_json)
if int_or_str is not None:
print('Got a Union[int, str]!') # int_or_str should be narrowed to Union[int, str] here
else:
print('Got something else!')
I've even implemented such a function that works at runtime, but mypy doesn't like me passing a Union[int, str] object to trycast as a Type[T]. It fails on the trycast(Union[int, str], response_json) call with:
error: Argument 1 to "trycast_union" has incompatible type "object"; expected "Type[<nothing>]"
Having the following items recognized by mypy as a Type[T] would be especially helpful in type-annotating functions that can manipulate generic type objects at runtime:
The "MyType" workaround given earlier in this thread doesn't seem to work to recognize a Union[...] type despite apparently working to recognize an Optional[...] type.
Most helpful comment
Why can't they be used at runtime?
This works