Skip to content

Commit

Permalink
Fix bad-super-call for non-direct parents (#6956)
Browse files Browse the repository at this point in the history
Co-authored-by: Pierre Sassoulas <pierre.sassoulas@gmail.com>
  • Loading branch information
DanielNoord and Pierre-Sassoulas committed Jun 16, 2022
1 parent 54a3d7f commit 600a47e
Show file tree
Hide file tree
Showing 7 changed files with 55 additions and 7 deletions.
7 changes: 6 additions & 1 deletion doc/data/messages/b/bad-super-call/bad.py
Expand Up @@ -2,6 +2,11 @@ class Animal:
pass


class Tree:
pass


class Cat(Animal):
def __init__(self):
super(Animal, self).__init__() # [bad-super-call]
super(Tree, self).__init__() # [bad-super-call]
super(Animal, self).__init__()
3 changes: 3 additions & 0 deletions doc/data/messages/b/bad-super-call/details.rst
Expand Up @@ -2,3 +2,6 @@ In Python 2.7, ``super()`` has to be called with its own class and ``self`` as a
lead to a mix up of parent and child class in the code.

In Python 3 the recommended way is to call ``super()`` without arguments (see also ``super-with-arguments``).

One exception is calling ``super()`` on a non-direct parent class. This can be used to get a method other than the default
method returned by the ``mro()``.
6 changes: 5 additions & 1 deletion doc/data/messages/b/bad-super-call/good.py
Expand Up @@ -2,6 +2,10 @@ class Animal:
pass


class Tree:
pass


class Cat(Animal):
def __init__(self):
super().__init__()
super(Animal, self).__init__()
4 changes: 4 additions & 0 deletions doc/whatsnew/2/2.14/full.rst
Expand Up @@ -5,6 +5,10 @@ What's New in Pylint 2.14.3?
----------------------------
Release date: TBA

* Fixed two false positives for ``bad-super-call`` for calls that refer to a non-direct parent.

Closes #4922, Closes #2903

* Fixed a false positive for ``useless-super-delegation`` for subclasses that specify the number of
of parameters against a parent that uses a variadic argument.

Expand Down
4 changes: 3 additions & 1 deletion pylint/checkers/newstyle.py
Expand Up @@ -108,7 +108,9 @@ def visit_functiondef(self, node: nodes.FunctionDef) -> None:
except astroid.InferenceError:
continue

if klass is not supcls:
# If the supcls is in the ancestors of klass super can be used to skip
# a step in the mro() and get a method from a higher parent
if klass is not supcls and all(i != supcls for i in klass.ancestors()):
name = None
# if supcls is not Uninferable, then supcls was inferred
# and use its name. Otherwise, try to look
Expand Down
35 changes: 33 additions & 2 deletions tests/functional/s/super/super_checks.py
Expand Up @@ -29,7 +29,7 @@ def __init__(self):
class Py3kWrongSuper(Py3kAaaa):
"""new style"""
def __init__(self):
super(NewAaaa, self).__init__() # [bad-super-call]
super(NewAaaa, self).__init__()

class WrongNameRegression(Py3kAaaa):
""" test a regression with the message """
Expand Down Expand Up @@ -59,7 +59,7 @@ class FalsePositive(Empty):
"""The following super is in another scope than `test`."""
def __init__(self, arg):
super(FalsePositive, self).__init__(arg)
super(object, 1).__init__() # [bad-super-call]
super(object, 1).__init__()


class UnknownBases(Missing):
Expand Down Expand Up @@ -123,3 +123,34 @@ class SuperWithSelfClass(object):
"""self.__class__ may lead to recursion loop in derived classes"""
def __init__(self):
super(self.__class__, self).__init__() # [bad-super-call]


# Reported in https://github.com/PyCQA/pylint/issues/2903
class Parent:
def method(self):
print()


class Child(Parent):
def method(self):
print("Child")
super().method()

class Niece(Parent):
def method(self):
print("Niece")
super().method()

class GrandChild(Child):
def method(self):
print("Grandchild")
super(GrandChild, self).method()
super(Child, self).method()
super(Niece, self).method() # [bad-super-call]


# Reported in https://github.com/PyCQA/pylint/issues/4922
class AlabamaCousin(Child, Niece):
def method(self):
print("AlabamaCousin")
super(Child, self).method()
3 changes: 1 addition & 2 deletions tests/functional/s/super/super_checks.txt
@@ -1,10 +1,8 @@
no-member:10:8:10:29:Aaaa.hop:Super of 'Aaaa' has no 'hop' member:INFERENCE
no-member:19:8:19:32:NewAaaa.hop:Super of 'NewAaaa' has no 'hop' member:INFERENCE
bad-super-call:22:8:22:25:NewAaaa.__init__:Bad first argument 'Aaaa' given to super():UNDEFINED
bad-super-call:32:8:32:28:Py3kWrongSuper.__init__:Bad first argument 'NewAaaa' given to super():UNDEFINED
bad-super-call:37:8:37:28:WrongNameRegression.__init__:Bad first argument 'Missing' given to super():UNDEFINED
bad-super-call:46:8:46:33:CrashSuper.__init__:Bad first argument 'NewAaaa' given to super():UNDEFINED
bad-super-call:62:8:62:24:SuperDifferentScope.test:Bad first argument 'object' given to super():UNDEFINED
bad-super-call:70:8:70:28:UnknownBases.__init__:Bad first argument 'Missing' given to super():UNDEFINED
not-callable:89:8:89:54:InvalidSuperChecks.__init__:super(InvalidSuperChecks, self).not_a_method is not callable:UNDEFINED
no-member:90:8:90:55:InvalidSuperChecks.__init__:Super of 'InvalidSuperChecks' has no 'attribute_error' member:INFERENCE
Expand All @@ -15,3 +13,4 @@ unexpected-keyword-arg:95:8:95:57:InvalidSuperChecks.__init__:Unexpected keyword
no-member:98:8:98:55:InvalidSuperChecks.__init__:Super of 'InvalidSuperChecks' has no 'attribute_error' member:INFERENCE
bad-super-call:120:8:120:31:SuperWithType.__init__:Bad first argument 'type' given to super():UNDEFINED
bad-super-call:125:8:125:35:SuperWithSelfClass.__init__:Bad first argument 'self.__class__' given to super():UNDEFINED
bad-super-call:149:8:149:26:GrandChild.method:Bad first argument 'Niece' given to super():UNDEFINED

0 comments on commit 600a47e

Please sign in to comment.