fixhunt

ruff FURB189

labelled unknown
Runs / 2026-10-17T21-37-47-548Z-ck53-ruff-FURB189ruff 0.17.12026-10-17 21:37 UTC8 programs × 2 round(s)16 programs, 13 fired, 0 fixed, 0 findings, 0 rejectedmodel cost $0.0053

Analysis Nemotron 3 Super

Flags classes that inherit directly from built-in dict, list, or str due to inconsistent dunder method usage.

Fix: Replaces direct inheritance from dict, list, or str with the corresponding collections.UserDict, UserList, or UserString base class, preserving all other bases and class body content.

12 hazards
  • The rewrite changes the MRO, potentially altering method resolution order in multiple inheritance scenarios involving the replaced base.
  • isinstance(obj, dict/list/str) checks will return False for instances after the rewrite, breaking type tests that rely on the original built-in type.
  • Code relying on the exact type (type(obj) is dict/list/str) will fail after the rewrite, as the type becomes the User* subclass.
  • If the class defines __slots__, the rewrite may conflict with User*'s __slots__ or cause layout incompatibilities, leading to attribute errors.
  • If the class overrides __new__ or __init__ and depends on the built-in's memory layout or initialization behavior, the User* alternative may not replicate it exactly.
  • If the class is used in C extensions or via CPython's internal APIs that expect the exact built-in type, the rewrite may cause segmentation faults or undefined behavior.
  • If the class relies on the built-in's hash implementation (e.g., for use in sets or as dict keys), the User* version may have different hashing behavior.
  • If the class is pickled/unpickled, the rewrite changes the serialized form and may break compatibility with existing pickles expecting the built-in type.
  • If the class is documented or inspected via __mro__, __bases__, or similar introspection, the output will differ after the rewrite.
  • Any decorators or metaclasses that inspect or modify the base classes at definition time may behave differently due to the changed base list.
  • If the code imports dict/list/str from elsewhere (e.g., via aliasing or shadowing), the fix may incorrectly replace the wrong base or fail to detect the inheritance.
  • Comments placed on the same line as the class declaration or within the parentheses of the base class list may be lost or misplaced during the rewrite.

Programs

idresulttitlehazard
r1-p001fired, no fixExact type check fails after rewriteCode relying on type(obj) is dict/list/str breaks when type becomes User* subclass.
r1-p002fired, no fix__mro__ inspection differs after rewriteIntrospection via __mro__, __bases__ etc. shows different inheritance chain post-fix.
r1-p003silentAliased built-in causes misfire or missed detectionIf dict/list/str imported/aliased/shadowed, fix may replace wrong identifier or miss trigger.
r1-p004fired, no fixDescriptor/properties with side effects behave differently__get__/__set__ logic assumes dict storage; UserDict stores data in .data attribute.
r1-p005fired, no fixBytes vs str confusion in UserString rewriteUserString expects str input; passing bytes breaks or encodes unexpectedly post-fix.
r1-p006fired, no fix*args/**kwargs forwarding broken by signature mismatchUserList.__init__ signature differs from list; forwarding fails or misbehaves.
r1-p007silent prediction offMRO changed in multiple inheritance alters method resolutionReplacing list with UserList shifts MRO, calling different parent method in diamond inheritance.
r1-p008fired, no fixContext manager with write() but no close() misbehavesUserDict/UserList/UserString lack file-like semantics; write() may exist but context protocol fails.
r2-p001fired, no fixMRO and Base InspectionIf the class is documented or inspected via __mro__, __bases__, or similar introspection, the output will differ after the rewrite.
r2-p002fired, no fix__slots__ ConflictIf the class defines __slots__, the rewrite may conflict with User*'s __slots__ or cause layout incompatibilities, leading to attribute errors.
r2-p003fired, no fixdict Ordering and Duplicate Keysdict ordering, duplicate keys, __hash__/__eq__ overrides, sets
r2-p004fired, no fixInline Comments LostComments placed on the same line as the class declaration or within the parentheses of the base class list may be lost or misplaced during the rewrite.
r2-p005fired, no fixC Extension ExpectationIf the class is used in C extensions or via CPython's internal APIs that expect the exact built-in type, the rewrite may cause segmentation faults or undefined behavior.
r2-p006silent prediction offEvaluation Order Side Effectsevaluation order and side effects when expressions are merged, duplicated or deleted
r2-p007fired, no fixAttached Comments/Docstringscomments and docstrings attached to the removed or moved code
r2-p008fired, no fixMutable Default Arguments Aliasingmutable default arguments and aliasing after a rewrite

Sources

r1-p001 · Exact type check fails after rewrite
1import sys
2
3def f():
4 d = {} # baseline dict
5 c = MyDict() # user-defined dict-like
6 print(type(d) is dict)
7 print(type(c) is dict)
8
9class MyDict(dict):
10 pass
11
12f()
True
False
r1-p002 · __mro__ inspection differs after rewrite
1class A(dict):
2 pass
3
4print(A.__mro__)
5print(A.__bases__)
(<class '__main__.A'>, <class 'dict'>, <class 'object'>)
(<class 'dict'>,)
r1-p003 · Aliased built-in causes misfire or missed detection
1d = dict
2class A(d):
3 pass
4print(type(A()))
<class '__main__.A'>
r1-p004 · Descriptor/properties with side effects behave differently
1class Desc:
2 def __get__(self, obj, objtype=None):
3 if obj is None:
4 return self
5 return obj['x'] # assumes dict-like storage
6
7 def __set__(self, obj, value):
8 obj['x'] = value # writes to internal dict
9
10class MyDict(dict):
11 attr = Desc()
12
13d = MyDict()
14d.attr = 42
15print(d.attr)
16print(d['x'])
42
42
r1-p005 · Bytes vs str confusion in UserString rewrite
1class MyStr(str):
2 def __new__(cls, s):
3 return super().__new__(cls, s)
4
5s = MyStr(b'hello'.decode())
6print(type(s))
7print(repr(s))
<class '__main__.MyStr'>
'hello'
r1-p006 · *args/**kwargs forwarding broken by signature mismatch
1class MyList(list):
2 def __init__(self, *args, **kwargs):
3 super().__init__(*args, **kwargs)
4 self.extra = len(args)
5
6l = MyList([1, 2], extra=3)
7print(l)
8print(l.extra)
r1-p007 · MRO changed in multiple inheritance alters method resolution
1class A:
2 def extend(self, x):
3 print('A.extend')
4
5class B(list, A):
6 pass
7
8b = B()
9b.extend([1]) # which extend is called?
r1-p008 · Context manager with write() but no close() misbehaves
1class MyDict(dict):
2 def write(self, s):
3 self['log'] = self.get('log', '') + s
4
5d = MyDict()
6d.write('hello')
7print(d['log'])
8try:
9 with d:
10 pass
11except Exception as e:
12 print(type(e).__name__, e)
hello
TypeError 'MyDict' object does not support the context manager protocol (missed __exit__ method)
r2-p001 · MRO and Base Inspection
1class A(dict):
2 pass
3print('bases:', A.__bases__)
4print('mro:', [c.__name__ for c in A.__mro__])
5x = A()
6print('type(x).__name__:', type(x).__name__)
bases: (<class 'dict'>,)
mro: ['A', 'dict', 'object']
type(x).__name__: A
r2-p002 · __slots__ Conflict
1class A(dict):
2 __slots__ = ('x',)
3 def __init__(self):
4 super().__init__()
5 self.x = 1
6try:
7 a = A()
8 print('a.x:', a.x)
9except Exception as e:
10 print(type(e).__name__, ':', e)
a.x: 1
r2-p003 · dict Ordering and Duplicate Keys
1class A(dict):
2 def __setitem__(self, k, v):
3 super().__setitem__(k, v * 2)
4 def __hash__(self):
5 return id(self)
6a = A()
7a[1] = 2
8a[1] = 3
9print('a[1]:', a[1])
10print('len(a):', len(a))
11print('dict(a):', dict(a))
a[1]: 6
len(a): 1
dict(a): {1: 6}
r2-p004 · Inline Comments Lost
1class A(dict): # comment on decl
2 """doc"""
3 x = 1
4class B(list): # another
5 y = 2
6print('A.x:', A.x)
7print('B.y:', B.y)
8print('A.__doc__:', A.__doc__)
9print('B.__doc__:', B.__doc__)
A.x: 1
B.y: 2
A.__doc__: doc
B.__doc__: None
r2-p005 · C Extension Expectation
1import sys
2class A(dict):
3 pass
4def check(obj):
5 return isinstance(obj, dict) and type(obj) is dict # strict built-in check
6a = A()
7print('isinstance(a, dict):', isinstance(a, dict))
8print('check(a):', check(a))
9print('type(a) is dict:', type(a) is dict)
isinstance(a, dict): True
check(a): False
type(a) is dict: False
r2-p006 · Evaluation Order Side Effects
1def f():
2 print('f called')
3 return dict
4class A(f()): # side effect in base
5 pass
6print('A.__bases__[0].__name__:', A.__bases__[0].__name__)
7a = A()
8print('type(a).__name__:', type(a).__name__)
f called
A.__bases__[0].__name__: dict
type(a).__name__: A
r2-p007 · Attached Comments/Docstrings
1class A(dict): # inline comment
2 """This is a docstring."""
3 def method(self):
4 return 'ok'
5print('A.__doc__:', A.__doc__)
6# Simulate checking comment attachment via AST-like behavior
7import ast
8tree = ast.parse('''class A(dict): # inline comment
9 \"\"\"This is a docstring.\"\"\"
10 def method(self):
11 return 'ok' ''')
12cls = tree.body[0]
13print('has inline comment?', hasattr(cls, 'comment') if hasattr(cls, 'comment') else 'N/A')
14print('method result:', A().method())
A.__doc__: This is a docstring.
has inline comment? N/A
method result: ok
r2-p008 · Mutable Default Arguments Aliasing
1def f(x=None):
2 if x is None:
3 x = {}
4 if not hasattr(f, 'counter'):
5 f.counter = 0
6 f.counter += 1
7 x['key'] = f.counter
8 return x
9class A(dict):
10 pass
11# Use class in default arg
12call_log = []
13def g(val=A()): # mutable default: class instance
14 call_log.append(val.copy())
15 return val
16g()
17g()
18g()
19print('call log lengths:', [len(d) for d in call_log])
20print('call log contents:', call_log)
call log lengths: [0, 0, 0]
call log contents: [{}, {}, {}]