fixhunt

ruff FURB180

labelled unknown
Runs / 2026-10-17T21-36-55-155Z-smym-ruff-FURB180ruff 0.17.12026-10-17 21:36 UTC8 programs × 2 round(s)16 programs, 7 fired, 5 fixed, 0 findings, 0 rejectedmodel cost $0.0036

Analysis Nemotron 3 Super

Flags explicit metaclass=abc.ABCMeta usage when defining ABCs.

Fix: Replace 'metaclass=abc.ABCMeta' with inheriting from abc.ABC; remove the metaclass keyword argument and add abc.ABC as a base class.

10 hazards
  • If the class inherits from typing.Protocol, adding ABC as a base may interfere with Protocol's structural subtyping checks at runtime.
  • If the class defines __init_subclass__ that inspects bases, inserting ABC could alter subclass creation behavior.
  • If the class relies on the exact tuple of __bases__ for pickling or registration, adding ABC changes that tuple.
  • If the class uses a custom metaclass elsewhere in the hierarchy that expects ABCMeta specifically, replacing it with ABC inheritance may break metaclass resolution.
  • If the class has a docstring or comment immediately after the metaclass keyword that is tied to formatting, the rewrite may reposition or drop it during AST reformatting.
  • If the class is defined inside a function or loop where abc.ABC is not imported in scope, the fix may introduce a NameError unless the import is hoisted.
  • If the class uses starargs (**kwargs) in its definition, the fix may reorder or drop those arguments incorrectly during AST transformation.
  • If the class is part of a dynamically generated class via type() or similar, the static rewrite does not apply and may produce mismatched behavior.
  • If the class defines __classcell__ in a nested scope, adding ABC as a base could interfere with cell binding in Python 3.x.
  • If the class is used in a context where abc.ABC is shadowed by a local variable, the fix may bind to the wrong entity.

Programs

idresulttitlehazard
r1-p001silent prediction offContext manager without close - object with write()context managers that are not files, objects with write() but no close()
r1-p002silent prediction offDynamically generated class via type()If the class is part of a dynamically generated class via type() or similar, the static rewrite does not apply and may produce mismatched behavior.
r1-p003fired, no fixCustom metaclass expecting ABCMetaIf the class uses a custom metaclass elsewhere in the hierarchy that expects ABCMeta specifically, replacing it with ABC inheritance may break metaclass resolution.
r1-p004fix okDocstring after metaclass keywordIf the class has a docstring or comment immediately after the metaclass keyword that is tied to formatting, the rewrite may reposition or drop it during AST reformatting.
r1-p005fix okClass inside function without ABC importIf the class is defined inside a function or loop where abc.ABC is not imported in scope, the fix may introduce a NameError unless the import is hoisted.
r1-p006fix okDuplicate keys and dict orderingdict ordering, duplicate keys, __hash__/__eq__ overrides, sets
r1-p007fix okComments on removed/moved codecomments and docstrings attached to the removed or moved code
r1-p008fix ok__classcell__ in nested scopeIf the class defines __classcell__ in a nested scope, adding ABC as a base could interfere with cell binding in Python 3.x.
r2-p001silent prediction off__bases__ used for pickle registrationClass relies on exact tuple of __bases__ for pickling or registration; adding ABC alters this tuple.
r2-p002fired, no fixRuntime-evaluated type annotations with TYPE_CHECKING importType annotations evaluated at runtime fail if typing-only imports are not available after fix introduces ABC without hoisting import.
r2-p003silent prediction offMutable default argument aliasing across rewriteMutable default arguments in class body cause aliasing issues when ABC is added as base, affecting shared state.
r2-p004silent prediction offClass inside function without ABC import in scopeClass defined in function where abc.ABC not imported; fix adds ABC as base causing NameError unless import hoisted.
r2-p005silent prediction offContext manager with write() but no close()Class is context manager-like (has write, no close); fix must not interfere with protocol relying on __bases__.
r2-p006silent prediction offClass with **kwargs in definition interacting with fixFix may mishandle **kwargs in class definition during AST rewrite, dropping or reordering.
r2-p007silent prediction offDescriptor and property with side effects on class rewriteClass uses descriptor or property with side effects; fix may break binding or evaluation timing.
r2-p008silent prediction offCustom __bool__ and __len__ affecting truthiness checksClass defines __bool__ or __len__; fix must not alter behavior of truthiness or len() after ABC injection.

Sources

r1-p001 · Context manager without close - object with write()
1import abc
2log = []
3def writer():
4 log.append('write')
5 return type('W', (), {'write': writer, '__enter__': lambda s: None, '__exit__': lambda s, *a: None})()
6with writer() as w:
7 w.write()
8print(log)
r1-p002 · Dynamically generated class via type()
1import abc
2bases = ()
3cls = type('Dynamic', bases, {'metaclass': abc.ABCMeta})
4try:
5 cls()
6except TypeError as e:
7 print('TypeError:', type(e).__name__, str(e))
r1-p003 · Custom metaclass expecting ABCMeta
1import abc
2class MetaABC(type):
3 def __new__(mcs, name, bases, ns):
4 if abc.ABCMeta not in [type(b) for b in bases]:
5 raise RuntimeError('ABCMeta required')
6 return super().__new__(mcs, name, bases, ns)
7class Base(metaclass=MetaABC):
8 pass
9class Derived(Base, metaclass=abc.ABCMeta):
10 pass
11print('ok')
r1-p004 · Docstring after metaclass keyword
1import abc
2class C(metaclass=abc.ABCMeta):
3 '"""doc"""'
4 pass
5print(C.__doc__)
--- orig.py	2026-10-17 17:37:04.754922637 -0400
+++ fixed.py	2026-10-17 17:37:04.915921033 -0400
@@ -1,5 +1,5 @@
 import abc
-class C(metaclass=abc.ABCMeta):
+class C(abc.ABC):
   '"""doc"""'
   pass
 print(C.__doc__)
"""doc"""
r1-p005 · Class inside function without ABC import
1def f():
2 import abc
3 class Local(metaclass=abc.ABCMeta):
4 pass
5 return Local()
6print(type(f()))
--- orig.py	2026-10-17 17:37:05.010920086 -0400
+++ fixed.py	2026-10-17 17:37:05.180918395 -0400
@@ -1,6 +1,6 @@
 def f():
   import abc
-  class Local(metaclass=abc.ABCMeta):
+  class Local(abc.ABC):
     pass
   return Local()
 print(type(f()))
<class '__main__.f.<locals>.Local'>
r1-p006 · Duplicate keys and dict ordering
1import abc
2d = {'a': 1, 'b': 2}
3class C(metaclass=abc.ABCMeta):
4 __hash__ = lambda self: id(self)
5 __eq__ = lambda self, other: self is other
6 def __setattr__(self, k, v):
7 d[k] = v
8obj = C()
9obj.x = 99
10print(d.get('x'))
--- orig.py	2026-10-17 17:37:05.288917320 -0400
+++ fixed.py	2026-10-17 17:37:05.455915658 -0400
@@ -1,6 +1,6 @@
 import abc
 d = {'a': 1, 'b': 2}
-class C(metaclass=abc.ABCMeta):
+class C(abc.ABC):
   __hash__ = lambda self: id(self)
   __eq__ = lambda self, other: self is other
   def __setattr__(self, k, v):
99
r1-p007 · Comments on removed/moved code
1import abc
2# comment before
3class C(metaclass=abc.ABCMeta): # inline
4 x = 1 # field
5print(C.x)
--- orig.py	2026-10-17 17:37:05.557914643 -0400
+++ fixed.py	2026-10-17 17:37:05.715913071 -0400
@@ -1,5 +1,5 @@
 import abc
 # comment before
-class C(metaclass=abc.ABCMeta):  # inline
+class C(abc.ABC):  # inline
   x = 1  # field
 print(C.x)
1
r1-p008 · __classcell__ in nested scope
1import abc
2def outer():
3 class Inner(metaclass=abc.ABCMeta):
4 __classcell__ = None
5 def method(self):
6 return 42
7 return Inner()
8print(outer().method())
--- orig.py	2026-10-17 17:37:05.820912026 -0400
+++ fixed.py	2026-10-17 17:37:06.192908326 -0400
@@ -1,6 +1,6 @@
 import abc
 def outer():
-  class Inner(metaclass=abc.ABCMeta):
+  class Inner(abc.ABC):
     __classcell__ = None
     def method(self):
       return 42
r2-p001 · __bases__ used for pickle registration
1import pickle
2REGISTRY = []
3def register(cls):
4 REGISTRY.append((cls.__name__, cls.__bases__))
5 return cls
6@register
7class C(metaclass=abc.ABCMeta):
8 def foo(self): return 42
9print('Before:', [(n, [b.__name__ for b in bs]) for n, bs in REGISTRY])
10data = pickle.dumps(C())
11obj = pickle.loads(data)
12print('After unpickle:', obj.foo())
r2-p002 · Runtime-evaluated type annotations with TYPE_CHECKING import
1from typing import TYPE_CHECKING
2if TYPE_CHECKING:
3 import abc
4class D(metaclass=abc.ABCMeta):
5 x: 'abc.ABC'
6 def f(self): return self.x
7try:
8 d = D()
9 d.x = d
10 print(d.f().__class__.__name__)
11except Exception as e:
12 print(f'{type(e).__name__}: {e}')
r2-p003 · Mutable default argument aliasing across rewrite
1class E(metaclass=abc.ABCMeta):
2 shared = []
3 def __init__(self, value=shared):
4 self.value = value
5 E.shared.append(1)
6e1 = E()
7e2 = E([99])
8print(f'e1.value: {e1.value}, length: {len(e1.value)}')
9print(f'e2.value: {e2.value}, length: {len(e2.value)}')
r2-p004 · Class inside function without ABC import in scope
1def outer():
2 class F(metaclass=abc.ABCMeta):
3 def f(self): return 1
4 return F
5F = outer()
6f = F()
7print(f.f())
r2-p005 · Context manager with write() but no close()
1class Logger(metaclass=abc.ABCMeta):
2 def write(self, msg):
3 self.buf += msg
4 def __enter__(self):
5 self.buf = ''
6 return self
7 def __exit__(self, *exc):
8 print(f'Log: {self.buf}')
9with Logger() as l:
10 l.write('hello')
11 l.write('world')
r2-p006 · Class with **kwargs in definition interacting with fix
1def make_class(**kw):
2 class G(metaclass=abc.ABCMeta, **kw):
3 pass
4 return G
5Klass = make_class(debug=True, verbose=False)
6k = Klass()
7print(f'Klass.__bases__: {[b.__name__ for b in Klass.__bases__]}')
8print(f'Klass.debug: {getattr(Klass, "debug", "missing")}')
r2-p007 · Descriptor and property with side effects on class rewrite
1class VerboseProperty:
2 def __get__(self, obj, owner):
3 print(f'Accessed by {obj}')
4 return 42
5class H(metaclass=abc.ABCMeta):
6 x = VerboseProperty()
7 @property
8 def y(self):
9 print('in y getter')
10 return self._y
11 def __init__(self):
12 self._y = 99
13print('Accessing h.x:')
14h = H()
15val = h.x
16print(f'Got: {val}')
17print('Accessing h.y:')
18val2 = h.y
19print(f'Got: {val2}')
r2-p008 · Custom __bool__ and __len__ affecting truthiness checks
1class Truthy(metaclass=abc.ABCMeta):
2 def __bool__(self):
3 return False
4 def __len__(self):
5 return 5
6 def __init__(self, val):
7 self.val = val
8obj = Truthy(10)
9print(f'bool(obj): {bool(obj)}')
10print(f'len(obj): {len(obj)}')
11print(f'obj is None: {obj is None}')
12print(f'obj == 0: {obj == 0}')
13if not obj:
14 print('obj is falsy')
15else:
16 print('obj is truthy')