fixhunt

ruff UP004: fix changes program behavior

fix-changes-behaviorlabelled safereal · highrelated
Findings / 2026-10-17T21-10-12-626Z-z2ng-ruff-UP004 / r1-p003ruff 0.17.114 → 9 lines2026-10-17 21:10 UTC

Hypothesis Nemotron 3 Super predicted to fire fired 1×

Rewriting 'class C(object, Mixin)' to 'class C(Mixin)' alters MRO when Mixin and a later base define the same method, changing which method is called via super().

Program

before
1class Mixin:
2 def ping(self):
3 return 'from Mixin'
4class Base:
5 def ping(self):
6 return 'from Base'
7class C(object, Mixin, Base):
8 def ping(self):
9 return super().ping()
after
1class Mixin:
2 def ping(self):
3 return 'from Mixin'
4class Base:
5 def ping(self):
6 return 'from Base'
7class C(Mixin, Base):
8 def ping(self):
9 return super().ping()

Output

stdout beforeexit 1
(no stdout)
TypeError: Cannot create a consistent method resolution
stdout afterexit 0
(no stdout)
fired
1 before · 0 after
runs
each version twice, same file name, no network

Judge Nemotron 3 Ultra advisory real · high

The fix removes 'object' from the base class list of a class that also inherits from Mixin and Base, changing the MRO and resolving a previously unresolvable diamond inheritance conflict. The documentation presents the rewrite as equivalent for all classes, but here the explicit 'object' was required to make the MRO consistent, so the fix silently changes a crashing program into a working one.

Explanation Nemotron 3 Super unverified

The original inheritance order `object, Mixin, Base` creates an inconsistent MRO because `object` precedes both parents but they conflict in their own hierarchy; Python cannot linearize this. Removing `object` yields `Mixin, Base`, which has a consistent MRO (`C → Mixin → Base → object`), allowing `super().ping()` to resolve to `Mixin.ping`. Thus the fix changes behavior by enabling a previously invalid class definition, altering method resolution and eliminating the TypeError. This is not semantics‑preserving because the original program was invalid and raised an error, while the rewritten version executes successfully with a different return value. The safety label assumes validity, but the fix changes the program’s dynamic semantics by correcting an illegitimate inheritance list. (119 words)

Reproduce

file
prog.py (the before program)
minimized program
r1-p003.py
evidence sheet
r1-p003.md
regression fixture
r1-p003.py the minimized program with a header stating the observed change, for the tool's test suite
python3 prog.py
ruff check --preview --isolated --select UP004 --fix prog.py
python3 prog.py

Upstream search GitHub issues Tavily Nemotron 3 Super + Ultra

UP004 fix · UP004 redundant base classes · unnecessary object inheritance Python 3 · super call MRO error removal

  • related astral-sh/ruff/issues/18387 Issue 18387 is another UP-rule (UP050) fix-safety report involving class base manipulation, same rule family but different construct.
  • related astral-sh/ruff/issues/15584 Issue 15584 requests fix-safety documentation for rules including UP004, same rule but a documentation task not a defect report.

Details

program
Method Resolution Order Change
hazard
Rewriting 'class C(object, Mixin)' to 'class C(Mixin)' alters MRO when Mixin and a later base define the same method, changing which method is called via super().
oracle
exit 1 → 0 stderr before: TypeError: Cannot create a consistent method resolution stderr after: (none)
run
2026-10-17T21-10-12-626Z-z2ng-ruff-UP004
generated program (14 lines) and the fix's diff
1class Mixin:
2 def ping(self):
3 return 'from Mixin'
4
5class Base:
6 def ping(self):
7 return 'from Base'
8
9class C(object, Mixin, Base):
10 def ping(self):
11 return super().ping()
12
13c = C()
14print(c.ping())
--- orig.py	2026-10-17 17:10:23.023489820 -0400
+++ fixed.py	2026-10-17 17:10:23.444494188 -0400
@@ -6,7 +6,7 @@
     def ping(self):
         return 'from Base'
 
-class C(object, Mixin, Base):
+class C(Mixin, Base):
     def ping(self):
         return super().ping()
 
rule documentation given to the models
# useless-object-inheritance (UP004)

Derived from the **pyupgrade** linter.

Fix is always available.

## What it does
Checks for classes that inherit from `object`.

## Why is this bad?
Since Python 3, all classes inherit from `object` by default, so `object` can
be omitted from the list of base classes.

## Example

```python
class Foo(object): ...
```

Use instead:

```python
class Foo: ...
```

## Fix safety
This fix is unsafe if it would cause comments to be deleted.

## References
- [PEP 3115 – Metaclasses in Python 3000](https://peps.python.org/pep-3115/)