You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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:
fromtypingimport*fromdataclassesimportdataclassT=TypeVar('T')
@dataclassclassP:
a: intclassQ(NamedTuple):
a: intdefcreate(t: Type[T]) ->T: ...
# These ones workreveal_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 notreveal_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.
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.
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.
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.
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:
Union[T1, T2, ...]
Optional[T]
List[T], list[T]
Dict[K, V], dict[K, V]
Literal['foo', 'bar', ...]
T extends TypedDict, for some T
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.
Reacted by Fabian Keller, Gary Yendell and Tobias Pfeiffer
from __future__ importannotationsfromtypingimportTypeVar, Any, TypeTargetType=TypeVar("TargetType", int, float, str)
defconvert_type(input_var: Any, target_type: Type[TargetType]) ->TargetType:
iftarget_type==str:
returnstr(input_var)
iftarget_type==int:
returnint(input_var)
iftarget_type==float:
returnfloat(input_var)
raiseNotImplementedError("This Target Type is not supported")
convert_type("1", int) +convert_type("1.2", float)
resulting in
mypy_test_variable_returns2.py:8: error: Incompatible return value type (got "str", expected "int")
mypy_test_variable_returns2.py:8: error: Incompatible return value type (got "str", expected "float")
mypy_test_variable_returns2.py:10: error: Incompatible return value type (got "int", expected "str")
mypy_test_variable_returns2.py:12: error: Incompatible return value type (got "float", expected "int")
mypy_test_variable_returns2.py:12: error: Incompatible return value type (got "float", expected "str")
@ltworf Thanks for pointing out.
But still I am not sure if that is the issue here.
I would expect the code above to be more or less equivalent to the code below:
from __future__ importannotationsfromtypingimportTypeVar, Any, Type, overload, UnionTargetType=TypeVar("TargetType", int, float, str)
@overloaddefconvert_type(input_var: Any, target_type: Type[int]) ->int:
...
@overloaddefconvert_type(input_var: Any, target_type: Type[float]) ->float:
...
@overloaddefconvert_type(input_var: Any, target_type: Type[str]) ->str:
...
defconvert_type(input_var: Any, target_type):
iftarget_type==str:
returnstr(input_var)
iftarget_type==int:
returnint(input_var)
iftarget_type==float:
returnstr(input_var) # should raise error but doesn'traiseNotImplementedError("This Target Type is not supported")
convert_type("1", int) +convert_type("1.2", float)
But as you can see, mypy is not checking that properly either...
And as far I understood in #9773, Type[] should work with everything that you can input in isinstance
So even the basic example doesn't work... :-/
Actually it seems that overloading even basic types also doesn't work as expected:
@overloaddefcombine(a: str, b: str) ->str:
...
@overloaddefcombine(a: str, b: int) ->int:
...
defcombine(a: str, b: Union[str, int]) ->Union[str, int]:
ifisinstance(b, str):
returnstr(int(a)+int(b))
ifisinstance(b, int):
returnstr(int(a) +b) # Also should raise an error but doesn'traiseNotImplementedError
Or am I off track here?
PS: I know that I could use Single Dispatch for the second example, but they are only minimal working examples for much more complex functions, with much more possible variants (include None, etc...)
@CarliJoy In your first example, you are using target_type == ..., which won't trigger MyPy to do any type narrowing. The only proper way to narrow down the types right now, is with isinstance usage.
In your second example, you are correctly using isinstance, meaning that b's type of Union[str, int] is narrowed down to just int. Since int(a) is also of type int, and int + int => int, there is no error returned - it is working as expected in this case.
The underlaying issue is that Type[...] usage right now, is only valid for things that can work as a second argument to isinstance - from the documentation:
The only legal parameters for Type are classes, Any, type variables, and unions of any of these types.
While this is fair and valid for most use-cases, Type[T] won't accept things like:
Singletons: None, NotImplemented and Ellipsis
Union or Literal
List[int], or any parametrized base class from the typing module really
In your second example, you are correctly using isinstance, meaning that b's type of Union[str, int] is narrowed down to just int. Since int(a) is also of type int, and int + int => int, there is no error returned - it is working as expected in this case.
Stupid me. Your are right. Thanks for pointing out.
@CarliJoy In your first example, you are using target_type == ..., which won't trigger MyPy to do any type narrowing. The only proper way to narrow down the types right now, is with isinstance usage.
Yes which is actually the problem here writing a typed converter function that expects a target type that should be returned.
Is there an issue for this already?
Because cast or isinstance won't work in this case.
The underlaying issue is that Type[...] usage right now, is only valid for things that can work as a second argument to isinstance - from the documentation:
The only legal parameters for Type are classes, Any, type variables, and unions of any of these types.
I really hope that #9773 will be implemented in the near future.
On May 3, 2021, at 10:26 AM, Kound ***@***.***> wrote:
In your second example, you are correctly using isinstance, meaning that b's type of Union[str, int] is narrowed down to just int. Since int(a) is also of type int, and int + int => int, there is no error returned - it is working as expected in this case.
Stupid me. Your are right. Thanks for pointing out.
@CarliJoy In your first example, you are using target_type == ..., which won't trigger MyPy to do any type narrowing. The only proper way to narrow down the types right now, is with isinstance usage.
Yes which is actually the problem here writing a typed converter function that expects a target type that should be returned.
Is there an issue for this already?
Because cast or isinstance won't work in this case.
The underlaying issue is that Type[...] usage right now, is only valid for things that can work as a second argument to isinstance - from the documentation:
The only legal parameters for Type are classes, Any, type variables, and unions of any of these types.
I really hope that #9773 will be implemented in the near future.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or unsubscribe.
I tried to use the workaround in #9003 (comment) but I wasn't able to get it working correctly. It did help me to arrive at a similar workaround I felt like sharing for feedback or in case it helps anyone else.
In my case, I was trying to wrap cattrs.structure() method, so conceptually a signature like my_func(return_type: Type[T]) -> T.
This workaround requires callers to wrap the non-type types from typing with a wrapper called Returns. It also allows the use of None as the parameter, in a similar way that None is allowed in type signatures.
fromtypingimportTYPE_CHECKING, Generic, Optional, Type, TypeVar, Union, overloadT=TypeVar("T")
classReturns(Generic[T]):
ifnotTYPE_CHECKING:
def__class_getitem__(cls, item: object) ->object:
"""Called when Returns is used as an Annotation at runtime. We just return the type we're given"""returnitem@overloaddefmy_func(return_type: Type[Returns[T]]) ->T:
pass@overloaddefmy_func(return_type: Type[T]) ->T:
pass@overloaddefmy_func(return_type: None) ->None:
passdefmy_func(return_type: Union[Type[T], Type[Returns[T]], None]) ->Optional[T]:
# Actually returns a T using cattrreturnNoneclassSomeClass:
passfoo: int=my_func(int) # Infered as returning intbar: SomeClass=my_func(SomeClass) # Infered as returning SomeClassbaz: Optional[int] =my_func(Returns[Optional[int]]) # Infered as returning Optional[int]# Error AS INTENDED# error: Incompatible types in assignment (expression has type "Optional[int]", variable has type "str")qux: str=my_func(Returns[Optional[int]])
Activity
JelleZijlstra commentedon Jun 15, 2020
This is similar also to #8992.
ltworf commentedon Jun 15, 2020
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.
JukkaL commentedon Jun 19, 2020
List[int]andDict[int, int]should have the inferred typeobjectin 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.
ltworf commentedon Jun 21, 2020
Why can't they be used at runtime?
This works
Make mypy happy
ltworf commentedon Jul 1, 2020
Just now I encountered another strange failure caused by this issue:
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.
glyph commentedon Jul 3, 2020
Is there a new, better way to say "thing that mypy treats as a type, at runtime" that isn't
Type[]? I understand whyTypeis 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 withUnions et. al., it's not quite clear how to describe its signatures any more.glyph commentedon Jul 3, 2020
For now, I'm resorting to this:
This seems pretty hacky though.
wyfo commentedon Jul 31, 2020
I've another issue that seems to be related.
My current workaround is to use overload with
and error disappear.
Hope it help.
Usual mypy workaround
davidfstr commentedon Nov 27, 2020
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:
And then call it like this:
I've even implemented such a function that works at runtime, but mypy doesn't like me passing a
Union[int, str]object totrycastas aType[T]. It fails on thetrycast(Union[int, str], response_json)call with: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 anOptional[...]type.CarliJoy commentedon Feb 12, 2021
It seems that even very basic usage of
Type[T] -> Tdoes not work.Similiar to the function of @davidfstr I have:
resulting in
a fix would be very much appreciated
ltworf commentedon Feb 12, 2021
Check the PEP that @davidfstr is working on…
CarliJoy commentedon Feb 13, 2021
@ltworf Thanks for pointing out.
But still I am not sure if that is the issue here.
I would expect the code above to be more or less equivalent to the code below:
But as you can see, mypy is not checking that properly either...
And as far I understood in #9773, Type[] should work with everything that you can input in
isinstanceSo even the basic example doesn't work... :-/
Actually it seems that overloading even basic types also doesn't work as expected:
Or am I off track here?
PS: I know that I could use Single Dispatch for the second example, but they are only minimal working examples for much more complex functions, with much more possible variants (include None, etc...)
DevilXD commentedon May 3, 2021
@CarliJoy In your first example, you are using
target_type == ..., which won't trigger MyPy to do any type narrowing. The only proper way to narrow down the types right now, is withisinstanceusage.In your second example, you are correctly using
isinstance, meaning thatb's type ofUnion[str, int]is narrowed down to justint. Sinceint(a)is also of typeint, andint + int => int, there is no error returned - it is working as expected in this case.The underlaying issue is that
Type[...]usage right now, is only valid for things that can work as a second argument toisinstance- from the documentation:While this is fair and valid for most use-cases,
Type[T]won't accept things like:None,NotImplementedandEllipsisUnionorLiteralList[int], or any parametrized base class from the typing module reallySee also: #9773
PS: The official documentation actually does a pretty poor job at wording - "unions" are not valid when assigned to
Type:The correct word there, would be "tuples" of any of these types. I guess I could make a PR that fixes this.
CarliJoy commentedon May 3, 2021
Stupid me. Your are right. Thanks for pointing out.
Yes which is actually the problem here writing a typed converter function that expects a target type that should be returned.
Is there an issue for this already?
Because
castor isinstance won't work in this case.I really hope that #9773 will be implemented in the near future.
davidfstr commentedon May 4, 2021
efokschaner commentedon Jan 2, 2022
I tried to use the workaround in #9003 (comment) but I wasn't able to get it working correctly. It did help me to arrive at a similar workaround I felt like sharing for feedback or in case it helps anyone else.
In my case, I was trying to wrap
cattrs.structure()method, so conceptually a signature likemy_func(return_type: Type[T]) -> T.This workaround requires callers to wrap the non-
typetypes fromtypingwith a wrapper calledReturns. It also allows the use ofNoneas the parameter, in a similar way thatNoneis allowed in type signatures.Interactive version at: https://mypy-play.net/?mypy=latest&python=3.8&flags=strict%2Cno-implicit-optional&gist=b4862022f1c7429b603b23c2939cc71e
Improve reachability in narrowing logic (#20660)
Better narrowing for enums and other types with known equality (#21281)