Repository navigation
Inheritance broken when testing isinstance? #6386
Copy link
Copy link
Closed
Labels
Description
I don't know if I'm running into a bug or I'm just not doing things right.
I was able to minimize my problem to this using Python 3.5 and mypy 0.660:
from abc import ABC, abstractmethod
from typing import Any, Optional, Type, Union
class A(ABC):
pass
class AA(A):
pass
class AB(A):
pass
class M(object):
pass
class MA(M, AA):
pass
class MB(M, AB):
pass
MYTYPES = Union[MA, MB]
class T(object):
def __init__(self, newtype:Type[MYTYPES]=MA) -> None:
assert isinstance(newtype, type(M))
self._new_type = newtype # type: Type[MYTYPES]
def test(self) -> A:
ret = self._new_type()
print(type(ret))
return ret
x = T()
print(isinstance(x.test(), A)) # True
y = T(MB)
print(isinstance(y.test(), A)) # True
My results are:
$ python3 -m mypy mixin_test.py
mixin_test.py:27: error: Incompatible types in assignment (expression has type "Type[M]", variable has type "Union[Type[MA], Type[MB]]")
$ python3 mixin_test.py
<class '__main__.MA'>
True
<class '__main__.MB'>
True
It seems that the assert is causing mypy to not realize that the object is still of type 'A'. I just want to make sure that the item passed in has the MixIn 'M' so that it can support the functions that I need. It is quite possible that I'm approaching this whole thing wrong.
Reactions are currently unavailable
Activity
ilevkivskyi commentedon Feb 13, 2019
Mypy has special treatment of
assert isinstance(obj, ...), they work as runtime-checked casts, and are normally used to narrow types. In your case it actually widens the type. I would say, we should not widen type after an assert (unless I am missing some use cases).Anyway, IIUC you should just do this:
(Another question is that we use runtime expression -- function call -- for static checks, which is probably bad.)
rldleblanc commentedon Feb 13, 2019
I don't know that I'm widening the type. My class
T(is actually an implementation of an ABC) that requires MixInMto work properly. The ABC has base functionally and requiresAat a minimum. ClassMAhas bothMandAwhich satisfies the specific needs of classTand has the generic stuff to satisfy the ABC of classT. So, I just want to make sure thatMis only accepted for classTwhich returns objects of classAwhich could be anMAobject. What you proposed looks like an acceptable solution. Too bad types aren't enforced at run time, it would make all this simpler.I'm not sure what you mean by this. Are you talking about the
newtype:Type[MYTYPES]=MAindef __init__(self, newtype:Type[MYTYPES]=MA) -> None:? I'm still pretty new at all of this.ilevkivskyi commentedon Feb 17, 2019
Using the call
type(M)as a source of static type information. Anyway, this is gray area, becausetype()is a "special" function.Btw I just noticed that logic in your code is wrong
This is because type of
Cis justtype. What you want is probablyassert issubclass(newtype, M).Fix interaction of isinstance() with Type[...] (#6419)