-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
TypeVar bound with Optional checking does not work any more #12622
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
Comments
I just realised that Btw, if someone has a better idea how to tell mypy "this function can take an optional or a non-optional and it will return exactly that again what you passed into the function", I'd be happy to hear suggestions. |
@darkclouder It seems you need typing.overload. |
from typing import Optional, TypeVar, overload
class Parent:
def copy(self):
return self
class Child(Parent):
pass
ParentT = TypeVar("ParentT", bound=Parent)
@overload
def try_copy_works(parent: None) -> None: ...
@overload
def try_copy_works(parent: ParentT) -> ParentT: ...
def try_copy_works(parent: Optional[ParentT]) -> Optional[ParentT]:
if parent is None:
return None
return parent.copy()
reveal_type(try_copy_works(None)) # None
reveal_type(try_copy_works(Parent())) # Parent
reveal_type(try_copy_works(Child())) # Child Looks like #9424 is a very similar issue as this one, and also suggests using overload as a workaround. |
Yes, thanks. Overload seems to be the thing I need. Coming back to the issue though: The error by mypy is still wrong, right? After type narrowing with the None check, |
I think you're right, and is what #9424 is trying to capture. Comparing Pylance type narrowing to mypy in your example: def try_copy_doesnt_work(parent: MaybeParent) -> MaybeParent:
if parent is None:
reveal_type(parent) # Pylance: Type of "parent" is "None"
return None # mypy: statement is unreachable
reveal_type(parent) # Pylance: Type of "parent" is "Parent"
# mypy: Type of "parent" is "MaybeParent`-1"
return parent.copy() # mypy: Item "object" of the upper bound "Optional[Parent]" of type variable "MaybeParent" has no attribute "copy" Clearly mypy is failing to narrow the typevar with the Your example is nearly identical to #9424 (comment): U = TypeVar('U', bound=Optional[int])
def sleep(x: int) -> None: ...
def f(u: U) -> U:
if u is None:
return None
sleep(u)
return u Shall we close as duplicate? |
Yup, seems to be a duplicate of that. Thanks for finding that. |
Bug Report
I use a pattern where I bind a TypeVar to an optional so I can use a function to return either an optional or non-optional depending on what is passed to the function:
MaybeParent = TypeVar("MaybeParent", bound=Optional[Parent])
.So with a function with this signature
try_copy(parent: MaybeParent) -> MaybeParent
, the function returns aParent
if I pass aParent
and returns aOptional[Parent]
if a pass aOptional[Parent]
.So I can have a function which can handle optionals but explicitly returns a non-optional if I pass a non-optional.
This used to pass mypy with version 0.812 but now with 0.942, it yields an error as it does not manage to extract the optional type (any more) if you check for it first.
And IMO the typing is correct and it should therefore pass.
To Reproduce
pip install mypy==0.942
Expected Behavior
Should pass type checking as
MaybeParent
is bound withNone | Parent
.Since I check
None
in the beginning, the variable should be bound withParent
.Actual Behavior
Does not pass type checking:
Item "object" of the upper bound "Optional[Parent]" of type variable "MaybeParent" has no attribute "copy"
Incompatible types in assignment (expression has type "MaybeParent", variable has type "Parent")
Your Environment
The text was updated successfully, but these errors were encountered: