From 5393abe61c1f860e90422858c9caf709ea7c4fee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20Noord?= <13665637+DanielNoord@users.noreply.github.com> Date: Wed, 15 Jun 2022 13:33:00 +0200 Subject: [PATCH 1/6] Fix ``bad-super-call`` for non-direct parents --- doc/whatsnew/2/2.14/full.rst | 2 ++ pylint/checkers/newstyle.py | 4 ++- tests/functional/s/super/super_checks.py | 35 +++++++++++++++++++++-- tests/functional/s/super/super_checks.txt | 3 +- 4 files changed, 39 insertions(+), 5 deletions(-) diff --git a/doc/whatsnew/2/2.14/full.rst b/doc/whatsnew/2/2.14/full.rst index 0cbfdaf88a..52f33670d6 100644 --- a/doc/whatsnew/2/2.14/full.rst +++ b/doc/whatsnew/2/2.14/full.rst @@ -5,7 +5,9 @@ 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 What's New in Pylint 2.14.2? diff --git a/pylint/checkers/newstyle.py b/pylint/checkers/newstyle.py index 6b0aa50956..0aed971836 100644 --- a/pylint/checkers/newstyle.py +++ b/pylint/checkers/newstyle.py @@ -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 diff --git a/tests/functional/s/super/super_checks.py b/tests/functional/s/super/super_checks.py index 277feae7ee..ca35d52aec 100644 --- a/tests/functional/s/super/super_checks.py +++ b/tests/functional/s/super/super_checks.py @@ -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 """ @@ -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): @@ -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() diff --git a/tests/functional/s/super/super_checks.txt b/tests/functional/s/super/super_checks.txt index d18c28327f..f33d9ec637 100644 --- a/tests/functional/s/super/super_checks.txt +++ b/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 @@ -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 From 1156c3499017c88ea8402505cce53a37d1631395 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20Noord?= <13665637+DanielNoord@users.noreply.github.com> Date: Wed, 15 Jun 2022 13:38:16 +0200 Subject: [PATCH 2/6] Bump cache --- .github/workflows/primer-test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/primer-test.yaml b/.github/workflows/primer-test.yaml index a20f6a3e08..f3ad747f63 100644 --- a/.github/workflows/primer-test.yaml +++ b/.github/workflows/primer-test.yaml @@ -14,7 +14,7 @@ on: env: # Also change CACHE_VERSION in the CI workflow - CACHE_VERSION: 6 + CACHE_VERSION: 7 concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} From b686b4019e4a2c7b3f24382e323fdde448461a24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20Noord?= <13665637+DanielNoord@users.noreply.github.com> Date: Wed, 15 Jun 2022 14:00:39 +0200 Subject: [PATCH 3/6] Update examples --- doc/data/messages/b/bad-super-call/bad.py | 7 ++++++- doc/data/messages/b/bad-super-call/details.rst | 3 +++ doc/data/messages/b/bad-super-call/good.py | 6 +++++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/doc/data/messages/b/bad-super-call/bad.py b/doc/data/messages/b/bad-super-call/bad.py index 1f13df5ede..01c9ac6cc4 100644 --- a/doc/data/messages/b/bad-super-call/bad.py +++ b/doc/data/messages/b/bad-super-call/bad.py @@ -2,6 +2,11 @@ class Animal: pass +class Fish: + pass + + class Cat(Animal): def __init__(self): - super(Animal, self).__init__() # [bad-super-call] + super(Fish, self).__init__() # [bad-super-call] + super(Animal, self).__init__() diff --git a/doc/data/messages/b/bad-super-call/details.rst b/doc/data/messages/b/bad-super-call/details.rst index 73f41942c1..e5b0ab5c7d 100644 --- a/doc/data/messages/b/bad-super-call/details.rst +++ b/doc/data/messages/b/bad-super-call/details.rst @@ -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()``. diff --git a/doc/data/messages/b/bad-super-call/good.py b/doc/data/messages/b/bad-super-call/good.py index 720b2eda91..c2b395076c 100644 --- a/doc/data/messages/b/bad-super-call/good.py +++ b/doc/data/messages/b/bad-super-call/good.py @@ -2,6 +2,10 @@ class Animal: pass +class Fish: + pass + + class Cat(Animal): def __init__(self): - super().__init__() + super(Animal, self).__init__() From 780b77862ca9354ea78468c4687e3194ae6c7f0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20Noord?= <13665637+DanielNoord@users.noreply.github.com> Date: Wed, 15 Jun 2022 14:01:17 +0200 Subject: [PATCH 4/6] Down Cache --- .github/workflows/primer-test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/primer-test.yaml b/.github/workflows/primer-test.yaml index f3ad747f63..a20f6a3e08 100644 --- a/.github/workflows/primer-test.yaml +++ b/.github/workflows/primer-test.yaml @@ -14,7 +14,7 @@ on: env: # Also change CACHE_VERSION in the CI workflow - CACHE_VERSION: 7 + CACHE_VERSION: 6 concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} From 94d8ecd9b7e0e621595c431445c85d38fa8cd916 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20Noord?= <13665637+DanielNoord@users.noreply.github.com> Date: Wed, 15 Jun 2022 23:55:16 +0200 Subject: [PATCH 5/6] Apply suggestions from code review Co-authored-by: Pierre Sassoulas --- doc/data/messages/b/bad-super-call/bad.py | 2 +- doc/data/messages/b/bad-super-call/good.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/data/messages/b/bad-super-call/bad.py b/doc/data/messages/b/bad-super-call/bad.py index 01c9ac6cc4..149306d73e 100644 --- a/doc/data/messages/b/bad-super-call/bad.py +++ b/doc/data/messages/b/bad-super-call/bad.py @@ -2,7 +2,7 @@ class Animal: pass -class Fish: +class Tree: pass diff --git a/doc/data/messages/b/bad-super-call/good.py b/doc/data/messages/b/bad-super-call/good.py index c2b395076c..66152bc73e 100644 --- a/doc/data/messages/b/bad-super-call/good.py +++ b/doc/data/messages/b/bad-super-call/good.py @@ -2,7 +2,7 @@ class Animal: pass -class Fish: +class Tree: pass From 39c46f5bce9733a25dd1c4cc8f1df21786cc473c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20Noord?= <13665637+DanielNoord@users.noreply.github.com> Date: Wed, 15 Jun 2022 23:58:08 +0200 Subject: [PATCH 6/6] Update doc/data/messages/b/bad-super-call/bad.py --- doc/data/messages/b/bad-super-call/bad.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/data/messages/b/bad-super-call/bad.py b/doc/data/messages/b/bad-super-call/bad.py index 149306d73e..625a70e6f9 100644 --- a/doc/data/messages/b/bad-super-call/bad.py +++ b/doc/data/messages/b/bad-super-call/bad.py @@ -8,5 +8,5 @@ class Tree: class Cat(Animal): def __init__(self): - super(Fish, self).__init__() # [bad-super-call] + super(Tree, self).__init__() # [bad-super-call] super(Animal, self).__init__()