-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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 OOP class / method inheritance / analyzing misbehaving #10192
Comments
I forgot to mention: a base abstract class must be "empty" (no code). This also should be analyzed by pylint. @abc.abstractmethod
def methodname(self):
pass |
It looks like a duplicate of #9275. |
That would contradict the Python docs for abstractmethod: Note Unlike Java abstract methods, these abstract methods may have an implementation. This implementation can be called via the super() mechanism from the class that overrides it. This could be useful as an end-point for a super-call in a framework that uses cooperative multiple-inheritance. |
This is 100% true, i forgot the super() call possibility to add some abstract global-ness. This even makes sense for non-multi-inheritance where you have a global routine for every overloaded method. |
No, if i would declare |
Sorry I think I was too vague! I didn't mean to suggest mixing NotImplementedError with abc.abstractmethod in the same method definition. What I said was:
Let me restate that another way. Approach 1 class Base(abc.ABC):
@abc.abstractmethod
def say_hello(self):
print("hello") Approach 2 class Base:
def say_hello(self):
raise NotImplementedError My feeling of your expected behaviour for Approach 2 (No Exception should be raised by pylint.) is that this would be a contradiction to the Python docs. I think the NotImplementedError approach pre-dates the abc.abstractmethod approach, but they are both still documented and in use in codebases. Maybe it's a good idea to ask on Python Discourse Help to pitch the idea if you think it should be changed? For reference, the Python docs have this: |
We are getting closer, i will write some detailed comments later on. Coming tonight ... |
Code Example 1 (correct implementation)import abc
class DecoratorClass():
def global_func(self, arg1):
print(arg1)
class Base(DecoratorClass, metaclass=abc.ABCMeta):
def __init__(self):
print('init some math')
@abc.abstractmethod
def calc_shape(self, x, y, z):
pass
def calc_more(self, data):
raise NotImplementedError
class Shape1(Base):
def __init__(self):
super().__init__()
def calc_shape(self, x, y, z=None):
print('x-cord:{} y-cord:{}'.format(x, y))
class Shape2(Base):
def __init__(self):
super().__init__()
def calc_shape(self, x, y, z):
print('x-cord:{} y-cord:{} z-cord:{}'.format(x, y, z))
s1 = Shape1()
s1.calc_shape(10, 20)
s1.global_func('global1')
try:
s1.calc_more(100)
except NotImplementedError as e:
print('Correct: NotImplementedError raised.')
s2 = Shape2()
s2.calc_shape(20, 30, 40)
s2.global_func('global2')
try:
s2.calc_more(200)
except NotImplementedError as e:
print('Correct: NotImplementedError raised.') Python 3.11.2 executes the code (working like expected). python3 test-abstract.py
init some math
x-cord:10 y-cord:20
global1
Correct: NotImplementedError raised.
init some math
x-cord:20 y-cord:30 z-cord:40
global2
Correct: NotImplementedError raised. Code Example 2 (incorrect implementation)I omit the class Shape2(Base):
def __init__(self):
super().__init__() Python prints out the correct Exception. s2 = Shape2()
^^^^^^^^
TypeError: Can't instantiate abstract class Shape2 with abstract method calc_shape AssumptionI assume there is a slight misunderstanding of how abstract class methods work. An abstract class must be overloaded in every child class method to guarantee a different implementation. An abstract class inheriting from abc.ABCMeta gets base functionality Wrong pylint Output / Processingpylint 2.16.2 It would be only valid for Code Example 2 (where the calc_shape() method is flagged abstract in the base and no calc_shape() method exists in the child). |
I agree that the My point is that both these approaches are documented Python behaviour for creating abstract methods. Therefore Pylint is complete in the sense that it analyses both approaches. We can leave this open to see if anyone wants to voice their thoughts. |
Yes, the Python documentation is wrong!
should be
The following example code can be used to clarify this to Python staff (not working like described in the current NotImplementedError documentation). Until progress, this ticket can be closed (and i have to disable the W0223 in my pylint config). Example 1 (abstract method not declared)import abc
class Base(metaclass=abc.ABCMeta):
def test_method(self):
pass
class Child1(Base):
def test_method(self):
print('overloaded in Child1')
class Child2(Base):
def some_other_method(self):
pass
c1 = Child1()
c2 = Child2() Python behavior: No Exception ✅ Pylint: No message raised ✅ Example 2 (abstract method not overloaded, implementation error)import abc
class Base(metaclass=abc.ABCMeta):
@abc.abstractmethod
def test_method(self):
pass
class Child1(Base):
def test_method(self):
print('overloaded in Child1')
class Child2(Base):
def some_other_method(self):
pass
c1 = Child1()
c2 = Child2() Python behavior: TypeError: Can't instantiate ... Exception ✅ Pylint: W0223: Method 'test_method' is abstract ✅ Example 3 (NotImplementedError raising method declared abstract)import abc
class Base(metaclass=abc.ABCMeta):
@abc.abstractmethod
def test_method(self):
raise NotImplementedError
class Child1(Base):
def test_method(self):
print('overloaded in Child1')
class Child2(Base):
def no_test_method(self):
print('another method')
c1 = Child1()
c2 = Child2() Python behavior: TypeError: Can't instantiate ... Exception ✅ Pylint: W0223: Method 'test_method' is abstract ✅ A Warning Implementation of Example 4 (NotImplementedError declared non-abstract)import abc
class Base(metaclass=abc.ABCMeta):
@abc.abstractmethod
def some_abstract_method(self):
print('i am abstract')
def ni_method(self):
raise NotImplementedError
class Child1(Base):
def some_abstract_method(self):
print('overloaded in Child1')
class Child2(Base):
def some_abstract_method(self):
print('overloaded in Child2')
c1 = Child1()
c2 = Child2()
c2.ni_method() Python behavior: NotImplementedError Exception raised ✅ Pylint: 2x W0223: Method 'test_method' is abstract ❌ |
@mbyrnepr2 Hi, is it possible to reference you / this conversation for a public discussion on https://der-it-pruefer.de? |
Yes feel free to do that. It’s very considerate of you to ask. |
Bug description
get_value_by_property_id()
is not defined as abstract method (@abc.abstractmethod
decorator) but pylint treats like.Configuration
name: Pylint on: [push] jobs: build: runs-on: ubuntu-latest strategy: matrix: python-version: [ "3.10" ] steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v3 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip pip install pylint pip install microesb
Command used
pylint $(git ls-files 'src/microesb.py')
Pylint output
Expected behavior
No Exception should be raised by pylint.
Pylint version
OS / Environment
GitHub Actions / Docker
Additional dependencies
The text was updated successfully, but these errors were encountered: