fixhunt

ruff FURB161

labelled unknown
Runs / 2026-10-17T21-34-02-887Z-p0ib-ruff-FURB161ruff 0.17.12026-10-17 21:34 UTC8 programs × 2 round(s)16 programs, 13 fired, 1 fixed, 0 findings, 3 rejectedmodel cost $0.0042

Analysis Nemotron 3 Super

Flags calls to bin(x).count('1') where x is an integer expression, recommending replacement with x.bit_count().

Fix: Replaces bin(X).count('1') with X.bit_count(), preserving the inner expression X exactly as it appeared.

10 hazards
  • If X is a function call with side effects, the rewrite changes evaluation timing but not frequency—still one call—but may affect interleaving with other expressions in larger statements.
  • If X raises an exception (e.g., AttributeError, TypeError), the exception type and timing remain identical; however, if X returns a non-int that defines __index__ but not bit_count (e.g., custom numeric type), the rewrite will raise AttributeError where the original would succeed after bin conversion.
  • If X returns a float, both versions raise TypeError, but the original does so during bin() conversion while the rewrite raises AttributeError on bit_count lookup, altering the exception type.
  • If X returns a bool, both work (bool inherits bit_count), but the rewrite may be misleading stylistically though behaviorally equivalent.
  • If X is an expression whose value is used elsewhere in the same statement (e.g., in a tuple or assignment), the rewrite preserves the value and any side effects, but intermediate reuse of X is not possible since it's evaluated once in both forms.
  • If X is a conditional expression like a if cond else b, the rewrite preserves short-circuiting behavior identically.
  • If the code contains a comment between bin( and .count (e.g., bin(/* hint */x).count('1')), the autofix will drop the comment as it only preserves the inner expression.
  • If X is a lambda or comprehension that captures local state, the rewrite preserves capture semantics since evaluation is otherwise identical.
  • If X is a super() call or attribute access on a nested object (e.g., bin(self.obj.x).count('1')), the rewrite preserves the same attribute lookup chain.
  • If the code relies on bin(X) returning a string for further use (impossible here since .count('1') is chained), no issue arises—the intermediate string is never accessible.

Programs

idresulttitlehazard
r1-p001fired, no fixFloat argument changes exception type from TypeError to AttributeErrorIf X returns a float, both versions raise TypeError, but the original does so during bin() conversion while the rewrite raises AttributeError on bit_count lookup, altering the exception type.
r1-p002fired, no fixConditional expression preserves short-circuiting identicallyIf X is a conditional expression like a if cond else b, the rewrite preserves short-circuiting behavior identically.
r1-p003fired, no fix prediction offWalrus operator scoping preserved in comprehensionwalrus, comprehensions scoping, late binding in closures and lambdas
r1-p004fired, no fix prediction offAttribute chain preserved on super() and nested object accessIf X is a super() call or attribute access on a nested object (e.g., bin(self.obj.x).count('1')), the rewrite preserves the same attribute lookup chain.
r1-p005fired, no fix prediction offGenerator vs list: single consumption preservedgenerators vs lists, iterators consumed once, lazy vs eager evaluation
r1-p006fix ok prediction offContext manager with write but no close: no interferencecontext managers that are not files, objects with write() but no close()
r1-p007fired, no fix prediction offStar-imports and module-level side effects: no disruptionstar-imports, module-level side effects, __all__
r1-p008fired, no fix prediction offException inside rewritten code: finally/else and chaining preservedexceptions raised inside the rewritten code, finally/else clauses, exception chaining
r2-p001fired, no fixCustom Truthiness and Identitytruthiness of custom __bool__/__len__, None vs falsy, 'is' vs '=='
r2-p002invalidBoolean Return ValueIf X returns a bool, both work (bool inherits bit_count), but the rewrite may be misleading stylistically though behaviorally equivalent.
r2-p003fired, no fixFunction Call Side Effects Evaluation TimingIf X is a function call with side effects, the rewrite changes evaluation timing but not frequency—still one call—but may affect interleaving with other expressions in larger statements.
r2-p004fired, no fixEvaluation Order and Side Effects in Larger Expressionsevaluation order and side effects when expressions are merged, duplicated or deleted
r2-p005fired, no fixConditional Expression Short-CircuitingIf X is a conditional expression like a if cond else b, the rewrite preserves short-circuiting behavior identically.
r2-p006invalidWalrus and Comprehension Scopingwalrus, comprehensions scoping, late binding in closures and lambdas
r2-p007fired, no fix prediction offRuntime Type Annotations and Typing-Only Importstype annotations evaluated at runtime, typing-only imports
r2-p008invalidPathlib vs os.path Behaviorpathlib vs os.path behavior on relative paths, symlinks, missing files, bytes paths

Sources

r1-p001 · Float argument changes exception type from TypeError to AttributeError
1def f():
2 return 3.14
3try:
4 result = bin(f()).count('1')
5except Exception as e:
6 print(type(e).__name__, str(e))
7try:
8 result = f().bit_count()
9except Exception as e:
10 print(type(e).__name__, str(e))
TypeError 'float' object cannot be interpreted as an integer
AttributeError 'float' object has no attribute 'bit_count'
r1-p002 · Conditional expression preserves short-circuiting identically
1def g():
2 print('g called')
3 return 5
4def h():
5 print('h called')
6 return 3
7cond = False
8try:
9 v = bin(g() if cond else h()).count('1')
10except: pass
11print('g' if not cond else 'h', 'not called' if cond else 'called')
12cond = True
13try:
14 v = bin(g() if cond else h()).count('1')
15except: pass
16print('g' if cond else 'h', 'called' if cond else 'not called')
h called
g called
g called
g called
r1-p003 · Walrus operator scoping preserved in comprehension
1vals = []
2for i in range(2):
3 vals.append((lambda: [x for j in range(1) if (x := i + j) and bin(x).count('1')])())
4print(vals)
5# After fix: same behavior, walrus scoped correctly
[[], [1]]
r1-p004 · Attribute chain preserved on super() and nested object access
1class A:
2 x = 5
3class B(A):
4 def get(self):
5 return super().x
6obj = B()
7print(bin(obj.get()).count('1'))
8obj2 = type('Obj', (), {'x': 10})()
9print(bin(obj2.x).count('1'))
2
2
r1-p005 · Generator vs list: single consumption preserved
1def gen():
2 yield 7
3 yield 3
4print(sum(bin(x).count('1') for x in gen()))
5g = gen()
6print(list(g)) # should be empty after first consumption
5
[7, 3]
r1-p006 · Context manager with write but no close: no interference
1class Writable:
2 def __enter__(self):
3 self.data = []
4 return self
5 def write(self, x):
6 self.data.append(x)
7 def __exit__(self, *args):
8 pass
9with Writable() as w:
10 w.write(str(bin(15).count('1')))
11print(w.data)
--- orig.py	2026-10-17 17:34:12.949814493 -0400
+++ fixed.py	2026-10-17 17:34:13.111812519 -0400
@@ -7,5 +7,5 @@
     def __exit__(self, *args):
         pass
 with Writable() as w:
-    w.write(str(bin(15).count('1')))
+    w.write(str((15).bit_count()))
 print(w.data)
['4']
r1-p007 · Star-imports and module-level side effects: no disruption
1import sys
2mod = type('M', (), {})
3mod.VALUE = 42
4sys.modules['mymod'] = mod
5from mymod import *
6print(VALUE)
7print(bin(VALUE).count('1'))
42
3
r1-p008 · Exception inside rewritten code: finally/else and chaining preserved
1def bad():
2 raise ValueError('bad')
3try:
4 try:
5 bin(bad()).count('1')
6 except ValueError as e:
7 raise RuntimeError('worse') from e
8except RuntimeError as e:
9 print(type(e).__name__, str(e))
10 print(type(e.__cause__).__name__, str(e.__cause__) if e.__cause__ else None)
RuntimeError worse
ValueError bad
r2-p001 · Custom Truthiness and Identity
1class C:
2 def __init__(self, v): self.v = v
3 def __bool__(self): return self.v > 0
4 def __len__(self): return abs(self.v)
5a = C(-1)
6b = C(0)
7c = C(1)
8print(f'a_bool={bool(a)} a_len={len(a)} a_is_none={a is None}')
9print(f'b_bool={bool(b)} b_len={len(b)} b_is_none={b is None}')
10print(f'c_bool={bool(c)} c_len={len(c)} c_is_none={c is None}')
11if bin(a).count('1'): print('a count True')
12else: print('a count False')
13if bin(b).count('1'): print('b count True')
14else: print('b count False')
15if bin(c).count('1'): print('c count True')
16else: print('c count False')
a_bool=False a_len=1 a_is_none=False
b_bool=False b_len=0 b_is_none=False
c_bool=True c_len=1 c_is_none=False
r2-p002 · Boolean Return Value (generated program does not parse: Traceback (most recent call last):)
1def f(): return True
2def g(): return False
3print(f'f()={f()} g()={g()}')
4print(f'bin(f()).count(\'1\')={bin(f()).count(\'1\')} f().bit_count()={f().bit_count()}')
5print(f'bin(g()).count(\'1\')={bin(g()).count(\'1\')} g().bit_count()={g().bit_count()}')
6x = f()
7y = g()
8if bin(x).count('1') == x.bit_count(): print('Match for f()')
9else: print('Mismatch for f()')
10if bin(y).count('1') == y.bit_count(): print('Match for g()')
11else: print('Mismatch for g()')
r2-p003 · Function Call Side Effects Evaluation Timing
1state = []
2def log(n):
3 state.append(f'called {n}')
4 return n * 2
5print('Start:', state)
6try:
7 val = bin(log(3)).count('1')
8except Exception as e:
9 print(f'Error: {type(e).__name__}: {e}')
10print('After bin(log(3)).count(\'1\'):', state)
11state.clear()
12state.append('reset')
13try:
14 val = bin(log(5)).bit_count()
15except Exception as e:
16 print(f'Error: {type(e).__name__}: {e}')
17print('After bin(log(5)).bit_count():', state)
Start: []
After bin(log(3)).count('1'): ['called 3']
Error: AttributeError: 'str' object has no attribute 'bit_count'
After bin(log(5)).bit_count(): ['reset', 'called 5']
r2-p004 · Evaluation Order and Side Effects in Larger Expressions
1side = []
2def f():
3 side.append('f')
4 return 3
5def g():
6 side.append('g')
7 return 5
8side.clear()
9result = f() + bin(g()).count('1')
10print(f'f then g: side={side}, result={result}')
11side.clear()
12result = bin(f()).count('1') + g()
13print(f'bin(f) then g: side={side}, result={result}')
14side.clear()
15result = f() + bin(g()).bit_count()
16print(f'f then bit_count g: side={side}, result={result}')
17side.clear()
18result = bin(f()).bit_count() + g()
19print(f'bit_count f then g: side={side}, result={result}')
f then g: side=['f', 'g'], result=5
bin(f) then g: side=['f', 'g'], result=7
r2-p005 · Conditional Expression Short-Circuiting
1def a(): print('a called'); return 1
2def b(): print('b called'); return 2
3cond = False
4print('Testing bin(a() if cond else b()).count(\'1\'):')
5try:
6 r = bin(a() if cond else b()).count('1')
7except Exception as e:
8 print(f' Error: {type(e).__name__}: {e}')
9cond = True
10print('Testing bin(a() if cond else b()).count(\'1\'):')
11try:
12 r = bin(a() if cond else b()).count('1')
13except Exception as e:
14 print(f' Error: {type(e).__name__}: {e}')
15cond = False
16print('Testing bin(a() if cond else b()).bit_count():')
17try:
18 r = bin(a() if cond else b()).bit_count()
19except Exception as e:
20 print(f' Error: {type(e).__name__}: {e}')
21cond = True
22print('Testing bin(a() if cond else b()).bit_count():')
23try:
24 r = bin(a() if cond else b()).bit_count()
25except Exception as e:
26 print(f' Error: {type(e).__name__}: {e}')
Testing bin(a() if cond else b()).count('1'):
b called
Testing bin(a() if cond else b()).count('1'):
a called
Testing bin(a() if cond else b()).bit_count():
b called
  Error: AttributeError: 'str' object has no attribute 'bit_count'
Testing bin(a() if cond else b()).bit_count():
a called
  Error: AttributeError: 'str' object has no attribute 'bit_count'
r2-p006 · Walrus and Comprehension Scoping (generated program does not parse: Traceback (most recent call last):)
1lst = [5, 6, 7]
2print('List comprehension with walrus:')
3squares = [ (n := x) * n for x in lst if bin(n := x).count('1') == 2 ]
4print(f' Result: {squares}, final n: {n}')
5print('
6Generator expression capture:')
7gen = (bin(n := x).count('1') for x in lst)
8first = next(gen)
9print(f' First from gen: {first}, n after: {n}')
10second = next(gen)
11print(f' Second from gen: {second}, n after: {n}')
12print('
13Lambda late-binding:')
14funcs = [lambda x=x: bin(n := x).count('1') for x in lst]
15for f in funcs:
16 print(f' Lambda result: {f()}')
r2-p007 · Runtime Type Annotations and Typing-Only Imports
1from typing import List
2def process(x: int) -> int:
3 return bin(x).count('1')
4print(f'process(5) = {process(5)}')
5print(f'Annotations: {process.__annotations__}')
6try:
7 if bin(process.__annotations__['return']).count('1'):
8 print('Return annotation has odd popcount')
9 else:
10 print('Return annotation has even popcount')
11except Exception as e:
12 print(f'Error checking annotation: {type(e).__name__}: {e}')
process(5) = 2
Annotations: {'x': <class 'int'>, 'return': <class 'int'>}
Error checking annotation: TypeError: 'type' object cannot be interpreted as an integer
r2-p008 · Pathlib vs os.path Behavior (generated program does not parse: Traceback (most recent call last):)
1import os
2open('test.txt', 'w').write('hello')
3try:
4 p_len = len('test.txt')
5 print(f'Path length: {p_len}')
6 print(f'bin(len(\'test.txt\')).count(\'1\') = {bin(p_len).count(\'1\')}')
7 print(f'bin(len(\'test.txt\')).bit_count() = {p_len.bit_count()}')
8 os.symlink('test.txt', 'link.txt')
9 l_len = len(os.readlink('link.txt'))
10 print(f'Symlink target length: {l_len}')
11 print(f'bin(len(os.readlink(\'link.txt\'))).count(\'1\') = {bin(l_len).count(\'1\')}')
12 print(f'bin(len(os.readlink(\'link.txt\'))).bit_count() = {l_len.bit_count()}')
13except Exception as e:
14 print(f'Error: {type(e).__name__}: {e}')
15finally:
16 try:
17 os.unlink('test.txt')
18 os.unlink('link.txt')
19 except FileNotFoundError:
20 pass