Skip to content

UP008 false positive in nested class when the first super arg is not a full class path #24001

@generalmimon

Description

@generalmimon

Summary

This is a follow-up to issue #22597. I'd like to thank @leandrobbraga for the initial implementation of the PR #22677 that fixed the bug and @charliermarsh for finishing it!

I'm glad to see that my review comments on that PR have been incorporated. However, after reviewing this part in the final version of the PR, I realized that there is still one edge case that isn't handled correctly:

class Base:
    def __init__(self, foo):
        print(f"Base.__init__({foo}) called")
        self.foo = foo


class Inner(Base):
    def __init__(self, foo):
        print(f"Inner.__init__({foo}) called")
        super().__init__(foo)


class Outer:
    class Inner(Inner):
        def __init__(self, foo):
            super(Inner, self).__init__(foo)  # Should NOT trigger UP008


i = Outer.Inner(5)

Playground link: https://play.ruff.rs/b19c84be-2da6-46e9-93f7-87f5ee770f54

As you can see in the playground, super(Inner, self) in Outer.Inner.__init__() triggers the UP008 rule, which is incorrect. UP008 should only be triggered if it were super(Outer.Inner, self) instead - only then would super() be equivalent.

Let me demonstrate that super(Inner, self) cannot be changed to super() (as the incorrectly triggered UP008 rule claims), because doing so would alter the program's runtime behavior:

$ python3 --version
Python 3.14.3
$ cat test.py
class Base:
    def __init__(self, foo):
        print(f"Base.__init__({foo}) called")
        self.foo = foo


class Inner(Base):
    def __init__(self, foo):
        print(f"Inner.__init__({foo}) called")
        super().__init__(foo)


class Outer:
    class Inner(Inner):
        def __init__(self, foo):
            super(Inner, self).__init__(foo)  # Should NOT trigger UP008


i = Outer.Inner(5)
$ python3 test.py
Base.__init__(5) called
$ ruff --version
ruff 0.15.6
$ ruff check --select UP --isolated --no-cache test.py
UP008 [*] Use `super()` instead of `super(__class__, self)`
  --> test.py:16:18
   |
14 |     class Inner(Inner):
15 |         def __init__(self, foo):
16 |             super(Inner, self).__init__(foo)  # Should NOT trigger UP008
   |                  ^^^^^^^^^^^^^
   |
help: Remove `super()` parameters

Found 1 error.
[*] 1 fixable with the `--fix` option.
$ ruff check --select UP --isolated --no-cache test.py --diff
--- test.py
+++ test.py
@@ -13,7 +13,7 @@
 class Outer:
     class Inner(Inner):
         def __init__(self, foo):
-            super(Inner, self).__init__(foo)  # Should NOT trigger UP008
+            super().__init__(foo)  # Should NOT trigger UP008


 i = Outer.Inner(5)

Would fix 1 error.
$ ruff check --select UP --isolated --no-cache test.py --fix
Found 1 error (1 fixed, 0 remaining).
$ python3 test.py
Inner.__init__(5) called
Base.__init__(5) called

Note that the Inner.__init__(5) called message was not printed by the original code, but is printed after applying the safe fix offered by Ruff for the mistakenly reported violation of rule UP008.

Version

ruff 0.15.6

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions