-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
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
TypeVar incompatible with constrained union #9424
Comments
from typing import Optional
U = Optional[int]
def f(u: U) -> U:
if u is None:
return None
assert isinstance(u, int)
return u .. though |
@habnabit I'm not trying to create an alias, I'm trying to constrain the return type to match the argument type. |
You can get your code snippet to pass using a value restricted typevar, instead of a bound:
Note a problematic case with bounded typevars to be mindful of in situations similar to this:
(edit: clarifying that bounds aren't central to the issue here, eg #8354 (comment)) |
Interesting. For that snippet, mypy complains about |
@hauntsaninja Could you give some insight into the difference between from typing import Optional, TypeVar
U = TypeVar('U', None, int)
def f(u: U) -> U:
if u is None:
return None
assert isinstance(u, int)
return u
def g(v: Optional[int]) -> Optional[int]:
return f(v) # error: Value of type variable "U" of "f" cannot be "Optional[int]" @gvanrossum Looks like MyPy is the only one doing the right thing? That's great! |
The relevant difference here is how subclasses would get treated. The following links have more info: Callers won't break if they pass in int, None or a similar typevar type. That said, I think your example above is something mypy could support, eg, since the following type checks:
Anyway, for now I think you're probably still best served by overloads or using a typevar and type-ignoring within the function. Note if you type ignore, it's really easy to forgo type safety in the presence of int subclasses, eg, even the following wouldn't be type safe:
But yes, as long as we're careful, I can't see a reason why mypy shouldn't accept:
It should probably also not think the |
@hauntsaninja Thanks, that makes perfect sense. Just a guess, but it seems that in: def f(u: U) -> U:
if u is None:
return None
assert isinstance(u, int)
return u at Thanks for the explanation. |
This problem also happens with class A: pass
class B(A): pass
T = TypeVar("T", bound=A)
def foo(t: T) -> T:
if isinstance(t, B):
return t # this errors
return t # this is fine This will return |
But what if we pass a C and expect a C back? |
Well, in my example you're returning the exact same object def foo(t: T) -> T:
if isinstance(t, B):
return B() I agree that this should error because passing any subclass of But in general, shouldn't it be possible to say "whatever the input class is, the output is of EXACTLY the same class"? Does this comment address your "what if we pass a C" question @gvanrossum ? |
In your first example, after In any case, your example is not the same as the issue being discussed in this issue. If you need more help please consult with the helpful folks on Gitter first. |
Gives
I cannot remove the assertion since it stands in for other code that is effectively constraining
u
.This can be expressed tediously using
overload
:Would it be possible to get the
TypeVar
approach to pass? It is far more succinct, and this quickly blows up in complexity if there are more argument-return type constraints to be enforced.This pattern is analogous to the common class factory pattern:
Since A < B < C, this enforces
This issue argues that since int < Optional[int] and None < Optional[int], we should ideally be able to similarly specify that
using the same notation.
The text was updated successfully, but these errors were encountered: