-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Should we remove abstract-method
?
#10054
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
Comments
Thanks for the engagement. As I understand your comments, the answer to the question - Should we remove abstract-method - would be no if Pylint were to define abstract methods strictly as those that are decorated by What I would say is, my description on this issue probably wasn't clear enough. The main takeaway of point 1. is that the Tiger class is the concrete one which implements the abstract method, and therefore, Note the example doesn't use from abc import abstractmethod
class Base:
@abstractmethod
def meow(self):
print("MEOW")
class Cat(Base):
def scratch(self):
"""Scratch my back""" While this does not: from abc import abstractmethod, ABC
class Base(ABC):
@abstractmethod
def meow(self):
print("MEOW")
class Cat(Base):
def scratch(self):
"""Scratch my back""" Even if we fix the bug in point 2., I feel that |
That makes perfect sense and pylint more precise. But please also check my post in #5793, this could be relevant for abstract-class-instantiated (analyzing) too. In my opinion a deriving abstract class definition (child class) must consist of the exact same definition (including parameter / count) as the base class definition. If not, then it can no longer call itself abstract. For #5793 it would make sense to only check "arguments-differ" for abstract methods in child classes (or also remove). Wrong: import abc
class Base(metaclass=abc.ABCMeta):
@abc.abstractmethod
def _testmethod(self, arg1):
pass
class Child1(Base):
@abc.abstractmethod
def _testmethod(self, arg1, arg2):
print(arg1*arg2)
class Child2(Base):
@abc.abstractmethod
def _testmethod(self, arg1, arg2):
print(arg1*arg2*2) Correct (two real abstract definitions in the base must be overloaded in both child classes) import abc
class Base(metaclass=abc.ABCMeta):
@abc.abstractmethod
def _testmethod(self, arg1):
pass
@abc.abstractmethod
def _testmethod(self, arg1, arg2):
pass
class Child1(Base):
@abc.abstractmethod
def _testmethod(self, arg1):
print(arg1*1)
def _testmethod(self, arg1, arg2):
print(arg1*arg2)
class Child2(Base):
@abc.abstractmethod
def _testmethod(self, arg1):
print(arg1*2)
def _testmethod(self, arg1, arg2):
print(arg1*arg2*2) |
Thanks for the clarification. I think there is an issue with your example, because this isn't overloading. In other words, |
Understand. My assumption of Python class method overloading in combination with positional parameters was wrong, which Pylint corrected now 👍. |
But returning to the current problem. Consider: An abstract class method is only abstract if it is decorated with the abc.ABCMeta |
I guess we should keep discussion about that on the #10192 issue. |
Sorry to bring up an old thread, but I feel that this hasn't been settled, at least not the particular issue I'm having with this warning. Going back to basics, if we stick with the case where everything is explicit i.e. we use
In case you still think
So my question is this: can pylint not just use |
The short answer is No unfortunately. We don't have access to to the objects at runtime but only the AST. Therefore, calling |
Thanks Daniel, I thought that might be the case. Do you have any idea how hard it is to implement def isabstract(object):
"""Return true if the object is an abstract base class (ABC)."""
if not isinstance(object, type):
return False
if object.__flags__ & TPFLAGS_IS_ABSTRACT:
return True
if not issubclass(type(object), abc.ABCMeta):
return False
if hasattr(object, '__abstractmethods__'):
# It looks like ABCMeta.__new__ has finished running;
# TPFLAGS_IS_ABSTRACT should have been accurate.
return False
# It looks like ABCMeta.__new__ has not finished running yet; we're
# probably in __init_subclass__. We'll look for abstractmethods manually.
for name, value in object.__dict__.items():
if getattr(value, "__isabstractmethod__", False):
return True
for base in object.__bases__:
for name in getattr(base, "__abstractmethods__", ()):
value = getattr(object, name, None)
if getattr(value, "__isabstractmethod__", False):
return True
return False My example above already returns on the |
We might be able to, but there is also the issue of this being a breaking change and (until now) nobody feeling strong enough about any provided solution to argue that we should potentially break all current CIs for this change. |
@DanielNoord i know you are talking about implementing (changing) the abstract method inspection code into pylint. On my to-do list is still the point to inform Python about the faulty documentation. If successful this could inspire the pylint developer community a bit. |
Thanks @DanielNoord , I agree that it's key that someone is bothered enough to do something about it. I'm sadly overloaded so (even if it were to be accepted by pylint), it's not something I can tackle at the moment. I would say though, that while it is true that this is a breaking change, from what I can see it would only be 'breaking' in the sense that it would no longer warn in situations that it used to warn in. It wouldn't warn in any new situations. Furthermore, the current implementation gives false-positives and so a change now would essentially restore the 'correct' behaviour. Given that pylint is used to write 'correct' (or at least less error prone code), this would seem to be a good thing. |
Even not warning will mean that people might need to remove ignore comments from their code. This all contributes to having to do manual work when you bump the version of |
Good point, I hadn't thought of that! |
Current problem
abstract-method documentation.
The documentation states that this message is emitted when an abstract method (i.e. raise NotImplementedError) is not overridden in concrete class.
A concrete class is one where all the abstract methods have been implemented.
The problem with
abstract-method
is that we don't know for sure if a class is intended to be a concrete class or not. The only way we would know for sure is if it does contain all of the implemented abstract methods - but by that point the check is not useful.The following scenario (modified from one of the examples in the documentation) is valid Python but it would emit
abstract-method
:The other example from the documentation emits
abstract-method
; however the base class does not inherit fromabc.ABC
, so this example emits a warning even though the class, which is not implementing the abstract method, instantiates without error:Desired solution
Deprecate and remove
abstract-method
.Additional context
#9979
#7950
#3098
Related message https://pylint.readthedocs.io/en/latest/user_guide/messages/error/abstract-class-instantiated.html
The text was updated successfully, but these errors were encountered: