mypy
mypy copied to clipboard
super(cls, self) seemingly has no attribute it actually has
Bug Report
I wanted to have a quick way of concatenating results of several mixin-like implementations of similar algorithms. Below is a MWE of what I wanted to implement
To Reproduce
MWE:
from abc import abstractmethod
from typing import List
class A:
@abstractmethod
def foo(self, arg: str) -> str:
raise NotImplementedError
def bar(self, arg: str) -> List[str]:
ret: List[str] = []
for c in self.__class__.mro():
c_super = super(c, self)
if hasattr(c_super, 'foo'):
try:
ret.append(c_super.foo(arg))
except NotImplementedError:
pass
try:
ret.append(self.foo(arg))
except NotImplementedError:
pass
return ret
class B(A):
def foo(self, arg: str) -> str:
return f'B.foo[{arg}]'
class C(B):
def foo(self, arg: str) -> str:
return f'C.foo[{arg}]'
hello = 'hello'
c = C()
print(f'foo: {c.foo(hello)}')
print(f'bar: {c.bar(hello)}')
Expected Behavior
No warning
Actual Behavior
mypy recognizes
mwe.py:15: error: "super" has no attribute "foo"
even though it is executed after if hasattr(...)
What's even more strange, if you construct a similar pattern but with the method foo
with no arguments, then the same example just passes mypy checks.
Your Environment
- Mypy version used: 0.812, 0.900, 0.971
- Python version used: 3.8
- Operating system and version: Ubuntu 22.04
Mypy has to implement quite a lot of special-casing in order to support super()
at all. I think it's pretty unlikely that mypy is going to add the necessary complexity in order to support highly dynamic (and quite unusual) uses of super()
like this, unfortunately.
This
class A():
def foo(self) -> None:
pass
class A1000(A):
def foo(self) -> None:
parent = super()
for _ in range(1000):
parent.foo()
already fails.