Skip to content

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

Closed

Description

@uriyyo

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

Activity

  1. hauntsaninja commented on Feb 7, 2023

    @hauntsaninja
    Collaborator

    I guess the issue is that the bounds are different?

  2. uriyyo commented on Feb 7, 2023

    @uriyyo
    MemberAuthor

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

  3. srittau commented on Oct 13, 2024

    @srittau
    Contributor

    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.

  4. sterliakov commented on Jan 16, 2025

    @sterliakov
    Collaborator

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

  5. hauntsaninja commented on Jan 16, 2025

    @hauntsaninja
    Collaborator

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugmypy got something wrongtopic-inheritanceInheritance and incompatible overridestopic-self-typesTypes for self

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions