mypy icon indicating copy to clipboard operation
mypy copied to clipboard

`Self` as return type annotation in base class method breaks multi-inheritance

Open uriyyo opened this issue 2 years ago • 3 comments

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

uriyyo avatar Feb 07 '23 22:02 uriyyo

I guess the issue is that the bounds are different?

hauntsaninja avatar Feb 07 '23 22:02 hauntsaninja

@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.

uriyyo avatar Feb 07 '23 22:02 uriyyo

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.

srittau avatar Oct 13 '24 13:10 srittau

@hauntsaninja this is one more ticket resolved by #18465 - sorry again, I should have searched for them more thoroughly:(

sterliakov avatar Jan 16 '25 02:01 sterliakov

No need to apologise, thank you for fixing so many things!

hauntsaninja avatar Jan 16 '25 02:01 hauntsaninja