Open samuelstevens opened 2 years ago
This behavior is correct because isinstance(obj, int)
does not guarantee that obj
is an int
; it simply guarantees that it's a subtype of int
. However, the __add__
method for int
always returns an int
, not a subtype of int
.
class IntSubclass(int): ...
i1 = IntSubclass(0)
i2 = increment(i1)
# At runtime, i2 is an instance of 'int', not 'IntSubclass'.
print(type(i2)) # <class 'int'>
That makes sense. Thank you for explainging!
My actual use case is more complicated, however. Instead of int
, I have some objects of a class that call some methods. Something like:
from typing import TypeVar
T = TypeVar("T")
class Dummy:
def __init__(self):
self.state: int = 0
def mutate_state(self):
self.state += 1
def handle(obj: T) -> T:
if isinstance(obj, Dummy):
obj.mutate_state()
return obj
else:
return obj
I get the same error:
$ mypy --strict scratch.py
scratch.py:17: error: Incompatible return value type (got "Dummy", expected "T")
Found 1 error in 1 file (checked 1 source file)
Even though obj
might just be a subclass of Dummy
, I am still returning obj
directly which is still of type T
. So shouldn't this be error-free?
Yes, your revised sample is type safe. I don't think mypy should emit an error in this case. By comparison, pyright does not.
Here's a workaround for mypy:
@overload
def handle(obj: Dummy) -> Dummy: ...
@overload
def handle(obj: T) -> T: ...
def handle(obj: T | Dummy) -> T | Dummy:
if isinstance(obj, Dummy):
obj.mutate_state()
return obj
else:
return obj
Thanks for the suggestion; I will try it in my full case but I was hoping to avoid all the extra boilerplate! (I have half a dozen classes that have special isinstance
cases.)
I'll leave this open as a bug in mypy. If I have time, maybe I will be able to look into why this happens (probably not).
Bug Report
When checking the type of a generic, mypy reports an error when (as far as I understand), I am still following the type contract I set.
To Reproduce
Expected Behavior
I expect there to be no error because if
obj
is anint
, then returning anint
is the correct behavior.Actual Behavior
Mypy reports an error.
Your Environment
--strict
mypy.ini
(and other config files): no other config