Skip to content

super(cls, self) seemingly has no attribute it actually has #13497

Closed

Description

@pjoziak

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

Activity

  1. added and removed
    bugmypy got something wrong
    on Aug 24, 2022
  2. AlexWaygood commented on Aug 24, 2022

    @AlexWaygood
    Member

    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.

  3. n1ngu commented on Sep 23, 2022

    @n1ngu

    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.

  4. erictraut commented on Aug 13, 2023

    @erictraut

    This appears to have been fixed in mypy 0.990. It does not repro since that release.

  5. AlexWaygood commented on Aug 13, 2023

    @AlexWaygood
    Member

    Wow, I never expected to see this one fixed

  6. hauntsaninja commented on Aug 13, 2023

    @hauntsaninja
    Collaborator

    Fixed by #13544. mypy will now get out of your way because of the hasattr

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

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions