Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Abstract Properties & Overloaded method has both abstract and non-abstract variants #4165

Closed
engnadeau opened this issue Oct 26, 2017 · 11 comments · Fixed by #15395
Closed
Labels
bug mypy got something wrong false-positive mypy gave an error on correct code priority-1-normal topic-descriptors Properties, class vs. instance attributes

Comments

@engnadeau
Copy link

Expected Behaviour

  • Ability to type check abstract property getters and setters
  • The intention is to have an interface-like design using properties

Actual Behaviour & Repro Case

Python Code

from abc import abstractmethod


class A:
    @property # error occurs here
    @abstractmethod
    def foo(self) -> int:
        pass

    @foo.setter  # type: ignore
    @abstractmethod
    def foo(self, value: int) -> None:
        pass

Mypy CLI

mypy --strict

Result

error: Overloaded method has both abstract and non-abstract variants

Notes

  • The error message is defined here:

    mypy/mypy/messages.py

    Lines 74 to 75 in a1ace48

    INCONSISTENT_ABSTRACT_OVERLOAD = \
    'Overloaded method has both abstract and non-abstract variants'
  • The error is thrown from here:

    mypy/mypy/checker.py

    Lines 329 to 330 in 936ceac

    if num_abstract not in (0, len(defn.items)):
    self.fail(messages.INCONSISTENT_ABSTRACT_OVERLOAD, defn)
  • Removing # type: ignore from @foo.setter causes mypy to throw error: Decorated property not supported, just as in Decorated property not supported #1362

Related Issues

Current Solution

  • Silence the error with @property # type: ignore
@engnadeau engnadeau changed the title Properties & Overloaded method has both abstract and non-abstract variants Abstract Properties & Overloaded method has both abstract and non-abstract variants Oct 26, 2017
@ilevkivskyi ilevkivskyi added bug mypy got something wrong priority-1-normal labels Oct 26, 2017
@ilevkivskyi ilevkivskyi added the false-positive mypy gave an error on correct code label May 20, 2018
@Netzeband
Copy link
Contributor

It seems that version 0.750 still have this bug. Do you have any ideas how to solve it?

@veox
Copy link

veox commented Dec 10, 2019

py-evm uses this workaround for now.

@pipermerriam
Copy link

@solstice333
Copy link

solstice333 commented Aug 13, 2020

I'm running mypy 0.782 on OSX 10.13.6.

Furthermore, if you use #type:ignore on both the getter and setter abstract properties to suppress the error message, and only implement the getter in the implementing subclass, you never get a mypy error if you never try to use the setter. For instance if I have

from abc import ABC, abstractmethod


class C(ABC):
   @property # type:ignore
   @abstractmethod
   def x(self) -> int:
      pass

   @x.setter # type:ignore
   @abstractmethod
   def x(self, val: int) -> None:
      pass
   

class D(C):
   def __init__(self) -> None:
      self._x = 0

   @property
   def x(self) -> int:
      return self._x


def main() -> None:
   d = D()
   print(d.x)


if __name__ == '__main__':
   main()

I get

$ python3 -m mypy --strict bar.py && python3 bar.py
Success: no issues found in 1 source file
0

I would expect for mypy to say "hey, implement an x.setter in class D". Removing #type:ignore on the getter abstract property results in

bar.py:5: error: Overloaded method has both abstract and non-abstract variants

as the OP already mentioned. Removing the #type:ignore on the setter abstract property results in

bar.py:10: error: Decorated property not supported

The only workaround that seems to make sense is to avoid using properties altogether, and stick with abstract methods:

from abc import ABC, abstractmethod


class C(ABC):
   @abstractmethod
   def get_x(self) -> int:
      pass

   @abstractmethod
   def set_x(self, val: int) -> None:
      pass
   

class D(C):
   def __init__(self) -> None:
      self._x = 0

   def get_x(self) -> int:
      return self._x


def main() -> None:
   d = D()


if __name__ == '__main__':
   main()

which results in:

bar.py:23: error: Cannot instantiate abstract class 'D' with abstract attribute 'set_x'

kunathj added a commit to LLR-ILD/alldecays that referenced this issue Jul 11, 2021
@Property, its setter and @AbstractMethod
do not work well together in mypy.
This fix was suggested in a GitHub issue:
python/mypy#4165
@ktbarrett
Copy link

Seems that using abstractproperty works as intended and avoids the error messages.
@solstice333 This approach errors if only the setter and not the getter are used. It's now properly type checked because it doesn't use type: ignore.

class A(ABC):
    @abstractproperty
    def a(self) -> int:
        ...        
    @a.setter
    def a(self, new_a: int) -> None:
        ...

@aspacca
Copy link

aspacca commented Oct 7, 2021

abstractproperty is deprecated

class abstractproperty(property):
    """A decorator indicating abstract properties.

    Deprecated, use 'property' with 'abstractmethod' instead:

        class C(ABC):
            @property
            @abstractmethod
            def my_abstract_property(self):
                ...

    """

@jgillard
Copy link

jgillard commented May 27, 2022

I hit this today with code similar to the repro, and removing @abstractmethod from the setter fixed it. i.e. only the getter is now decorated with @abstractmethod.

I got this idea from the 2nd code block here in the docs: https://docs.python.org/3.10/library/abc.html#abc.abstractproperty

@ilevkivskyi
Copy link
Member

The original example now works without any type-ignores on master. Probably was fixed by the same PR that fixed #1362. Also the example in the comments now correctly gives Read-only property cannot override read-write property.

@rhshadrach
Copy link

rhshadrach commented Oct 5, 2022

Edit: I misread and thought #4165 (comment) was from long ago; I now see it is only just over a month old. I think there is nothing to do here; sorry for the noise.

We're seeing this issue pop up again with 0.971 and it appears to be subsequently fixed with 0.981. Haven't tried other versions; perhaps a test should be added?

Good behavior (mypy 0.981): pandas-dev/pandas#48943
Bad behavior (mypy 0.971): pandas-dev/pandas#48959

@warsaw
Copy link
Member

warsaw commented Jun 8, 2023

Is this issue actually fixed? I've run into this same problem with mypy 1.3.0. Here's a reproducible example.

% mypy --version
mypy 1.3.0 (compiled: yes)
% mypy /tmp/foo.py
/tmp/foo.py: note: In member "default" of class "Abstract":
/tmp/foo.py:7:6: error: Overloaded method has both abstract and non-abstract variants  [misc]
        @property
         ^
Found 1 error in 1 file (checked 1 source file)

And here is /tmp/foo.py:

from abc import ABC, abstractmethod


class Abstract(ABC):
    """An abstract thing."""

    @property
    @abstractmethod
    def default(self) -> str:
        """The default code."""

    @default.setter
    @abstractmethod
    def default(self, code: str) -> None:
        """Set the code."""

    @default.deleter
    @abstractmethod
    def default(self) -> None:
        """Reset the code."""

@hauntsaninja hauntsaninja reopened this Jun 8, 2023
@hauntsaninja
Copy link
Collaborator

Looks like it got fixed for setters, but not deleters

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong false-positive mypy gave an error on correct code priority-1-normal topic-descriptors Properties, class vs. instance attributes
Projects
None yet
Development

Successfully merging a pull request may close this issue.