`Self` as return type annotation in base class method breaks multi-inheritance
Bug Report
Self as return type annotation in base class method breaks multi-inheritance.
(A clear and concise description of what the bug is.)
To Reproduce
import copy
from typing import Self
class Cloneable:
def clone(self) -> Self:
return copy.copy(self)
class Immutable:
def clone(self) -> Self:
return self
class Collection(Cloneable):
pass
class Tuple(Immutable, Collection):
pass
Expected Behavior No errors
Actual Behavior
error: Definition of "clone" in base class "Immutable" is incompatible with definition in base class "Cloneable" [misc]
Same code using old approach woks as expected:
import copy
from typing import TypeVar
_Self = TypeVar("_Self")
class Cloneable:
def clone(self: _Self) -> _Self:
return copy.copy(self)
class Immutable:
def clone(self: _Self) -> _Self:
return self
class Collection(Cloneable):
pass
class Tuple(Immutable, Collection):
pass
pyright doesn't raise an error.
Your Environment
- Mypy version used: 1.0.0
- Mypy command-line flags:
- Mypy configuration options from
mypy.ini(and other config files): - Python version used: 3.11
I guess the issue is that the bounds are different?
@hauntsaninja I guess code with Self should cover all the cases when TypeVar workaround was used.
So if this code works as expected:
import copy
from typing import TypeVar
_Self = TypeVar("_Self")
class Cloneable:
def clone(self: _Self) -> _Self:
return copy.copy(self)
class Immutable:
def clone(self: _Self) -> _Self:
return self
class Collection(Cloneable):
pass
class Tuple(Immutable, Collection):
pass
Then the code below should work too:
import copy
from typing import Self
class Cloneable:
def clone(self) -> Self:
return copy.copy(self)
class Immutable:
def clone(self) -> Self:
return self
class Collection(Cloneable):
pass
class Tuple(Immutable, Collection):
pass
I didn't find anything related to this in PEP 673, but pyright handle such case and works as I expected.
Please, correct me if I missed smth.
For the record, the Collection class is not necessary to expose the problem:
from typing import Self
class Base1:
def foo(self) -> Self:
return self
class Base2:
def foo(self) -> Self:
return self
class Sub(Base1, Base2): # error: Definition of "foo" in base class "Base1" is incompatible with definition in base class "Base2" [misc]
pass
(mypy 1.11.2)
Real-life example: python/typeshed#12788, where peewee makes use of that pattern.
@hauntsaninja this is one more ticket resolved by #18465 - sorry again, I should have searched for them more thoroughly:(
No need to apologise, thank you for fixing so many things!