fixhunt

ruff FURB105

labelled unknown
Runs / 2026-10-17T21-28-10-065Z-wj9b-ruff-FURB105ruff 0.17.12026-10-17 21:28 UTC8 programs × 2 round(s)16 programs, 9 fired, 8 fixed, 0 findings, 2 rejectedmodel cost $0.0027

Analysis Nemotron 3 Super

Flags print() calls with empty string literals as positional arguments or redundant sep=keyword arguments.

Fix: Removes all empty string literal positional arguments and the sep keyword argument when present, preserving other arguments and keyword arguments like end, file, flush.

10 hazards
  • The sep argument might be a variable whose removal avoids evaluating it, changing behavior if it has side effects.
  • The sep argument might be a variable whose removal hides a TypeError if it is not a string or None.
  • An empty string argument might be intended to produce a specific output format (e.g., CSV with empty fields) and its removal alters output.
  • Multiple empty strings might be used for alignment or spacing in output, and removing them changes formatting.
  • If sep is a keyword argument passed via **kwargs, the autofix cannot safely remove it and may leave invalid syntax.
  • Comments attached to the empty string arguments or sep keyword may be lost during rewriting.
  • If the print statement is part of a larger expression (e.g., in a list comprehension), removing arguments could alter syntax validity.
  • The sep argument might be conditionally assigned and its removal could skip error checking in debugging contexts.
  • In code relying on exact argument count for introspection or decorators, removing args may break assumptions.
  • If sep is a built-in overridden locally, removing it may avoid using the local override and use the built-in instead.

Programs

idresulttitlehazard
r1-p001silent prediction offArgument Count IntrospectionIn code relying on exact argument count for introspection or decorators, removing args may break assumptions.
r1-p002fix okBytes vs Str Encodingbytes vs str, encoding and newline handling, text vs binary modes
r1-p003fix okWalrus and Comprehension Scopingwalrus, comprehensions scoping, late binding in closures and lambdas
r1-p004silentSep via **kwargsIf sep is a keyword argument passed via **kwargs, the autofix cannot safely remove it and may leave invalid syntax.
r1-p005fix okPrint in List ComprehensionIf the print statement is part of a larger expression (e.g., in a list comprehension), removing arguments could alter syntax validity.
r1-p006fix okRuntime Type Annotation Evaluationtype annotations evaluated at runtime, typing-only imports
r1-p007fix okDict Ordering and Hashdict ordering, duplicate keys, __hash__/__eq__ overrides, sets
r1-p008invalidStar Imports and Module Side Effectsstar-imports, module-level side effects, __all__
r2-p001silent prediction offDictionary Key Override Behaviordict ordering, duplicate keys, __hash__/__eq__ overrides, sets
r2-p002silent prediction offMutable Default Aliasingmutable default arguments and aliasing after a rewrite
r2-p003silent prediction offShadowed Built-in Redefinitionshadowed builtins and names redefined later in the module
r2-p004fix okFile Truncation and Partial Writefile modes and truncation, partial writes, non-str data passed to write
r2-p005fix okPathlib Relative Path Symlinkpathlib vs os.path behavior on relative paths, symlinks, missing files, bytes paths
r2-p006invalidBytes vs Str Encoding Newlinebytes vs str, encoding and newline handling, text vs binary modes
r2-p007fix okLocal Sep Built-in OverrideIf sep is a built-in overridden locally, removing it may avoid using the local override and use the built-in instead.
r2-p008fired, no fixEvaluation Order Side Effects Mergedevaluation order and side effects when expressions are merged, duplicated or deleted

Sources

r1-p001 · Argument Count Introspection
1def inspect_args(func):
2 import inspect
3 sig = inspect.signature(func)
4 return len(sig.parameters)
5@inspect_args
6def wrapper(*args, **kwargs):
7 print(*args, sep='')
8 return args
9print(wrapper(1, 2))
r1-p002 · Bytes vs Str Encoding
1import io
2buf = io.BytesIO()
3try:
4 print(b''.decode(), file=buf, sep='')
5except Exception as e:
6 print(type(e).__name__, str(e))
7else:
8 print(buf.getvalue())
9buf.close()
--- orig.py	2026-10-17 17:28:16.858393384 -0400
+++ fixed.py	2026-10-17 17:28:17.017390195 -0400
@@ -1,7 +1,7 @@
 import io
 buf = io.BytesIO()
 try:
-    print(b''.decode(), file=buf, sep='')
+    print(b''.decode(), file=buf)
 except Exception as e:
     print(type(e).__name__, str(e))
 else:
TypeError a bytes-like object is required, not 'str'
r1-p003 · Walrus and Comprehension Scoping
1funcs = []
2for i in range(2):
3 if (x := '') == '':
4 funcs.append(lambda: print(x, sep=''))
5for f in funcs:
6 f()
--- orig.py	2026-10-17 17:28:17.138387771 -0400
+++ fixed.py	2026-10-17 17:28:17.302384486 -0400
@@ -1,6 +1,6 @@
 funcs = []
 for i in range(2):
     if (x := '') == '':
-        funcs.append(lambda: print(x, sep=''))
+        funcs.append(lambda: print(x))
 for f in funcs:
     f()
r1-p004 · Sep via **kwargs
1kwargs = {'sep': ''}
2print('a', 'b', **kwargs)
3print('done')
ab
done
r1-p005 · Print in List Comprehension
1results = [print('', sep='') or 1 for _ in range(2)]
2print('results:', results)
--- orig.py	2026-10-17 17:28:17.528379959 -0400
+++ fixed.py	2026-10-17 17:28:17.689376734 -0400
@@ -1,2 +1,2 @@
-results = [print('', sep='') or 1 for _ in range(2)]
+results = [print() or 1 for _ in range(2)]
 print('results:', results)

results: [1, 1]
r1-p006 · Runtime Type Annotation Evaluation
1T = str
2def foo(x: T) -> None:
3 print(x, '', sep='')
4foo('test')
--- orig.py	2026-10-17 17:28:17.795374611 -0400
+++ fixed.py	2026-10-17 17:28:17.956371386 -0400
@@ -1,4 +1,4 @@
 T = str
 def foo(x: T) -> None:
-    print(x, '', sep='')
+    print(x)
 foo('test')
test
r1-p007 · Dict Ordering and Hash
1class Key:
2 def __init__(self, v): self.v = v
3 def __hash__(self): return hash(self.v)
4 def __eq__(self, other): return self.v == other.v
5d = {Key(''): 1, Key(''): 2}
6print(len(d))
7print('', sep='')
--- orig.py	2026-10-17 17:28:18.060369305 -0400
+++ fixed.py	2026-10-17 17:28:18.226365985 -0400
@@ -4,4 +4,4 @@
     def __eq__(self, other): return self.v == other.v
 d = {Key(''): 1, Key(''): 2}
 print(len(d))
-print('', sep='')
+print()
1
r1-p008 · Star Imports and Module Side Effects (generated program does not parse: Traceback (most recent call last):)
1import tempfile
2import os
3with tempfile.NamedTemporaryFile(mode='w', delete=False) as f:
4 f.write('print(__name__, sep=\\'\\'\')
5')
6 fname = f.name
7try:
8 exec(open(fname).read(), {'__name__': '__main__'})
9except Exception as e:
10 print(type(e).__name__, str(e))
11finally:
12 os.unlink(fname)
r2-p001 · Dictionary Key Override Behavior
1class K:
2 def __init__(self, v):
3 self.v = v
4 def __hash__(self):
5 return hash(self.v)
6 def __eq__(self, other):
7 return isinstance(other, K) and self.v == other.v
8k1 = K(1)
9k2 = K(2)
10d = {}
11d[k1] = 'first'
12d[''] = 'empty'
13d[k2] = 'second'
14print(len(d))
15for k, v in d.items():
16 print(f'{k.v if isinstance(k, K) else repr(k)}:{v}', end=' ') if hasattr(k, 'v') else None
3
1:first 2:second
r2-p002 · Mutable Default Aliasing
1def f(a=[], b=''):
2 a.append(1)
3 print(f'a={len(a)}, b={repr(b)}')
4f()
5f()
6f([])
a=1, b=''
a=2, b=''
a=1, b=''
r2-p003 · Shadowed Built-in Redefinition
1print = lambda *args, **kwargs: None # shadow
2def g():
3 print('', sep='')
4g()
5del print
6print('restored:', end=' ')
7print('hello')
restored: hello
r2-p004 · File Truncation and Partial Write
1with open('test.txt', 'w') as f:
2 f.write('hello')
3 print('', file=f)
4 f.seek(0)
5 f.write('world')
6with open('test.txt') as f:
7 print(repr(f.read()))
8import os
9os.remove('test.txt')
--- orig.py	2026-10-17 17:28:23.093268966 -0400
+++ fixed.py	2026-10-17 17:28:23.265265552 -0400
@@ -1,6 +1,6 @@
 with open('test.txt', 'w') as f:
     f.write('hello')
-    print('', file=f)
+    print(file=f)
     f.seek(0)
     f.write('world')
 with open('test.txt') as f:
'world\n'
r2-p005 · Pathlib Relative Path Symlink
1import os
2os.symlink('target', 'link')
3try:
4 with open('link') as f:
5 print('opened link')
6except Exception as e:
7 print(type(e).__name__, e)
8print('', sep='')
9os.unlink('link')
--- orig.py	2026-10-17 17:28:23.370263468 -0400
+++ fixed.py	2026-10-17 17:28:23.531260272 -0400
@@ -5,5 +5,5 @@
         print('opened link')
 except Exception as e:
     print(type(e).__name__, e)
-print('', sep='')
+print()
 os.unlink('link')
FileNotFoundError [Errno 2] No such file or directory: 'link'
r2-p006 · Bytes vs Str Encoding Newline (generated program does not parse: Traceback (most recent call last):)
1data = b'hello
2world'
3print(data.decode())
4with open('out.bin', 'wb') as f:
5 f.write(data)
6with open('out.bin', 'rb') as f:
7 print(f.read())
8import os
9os.remove('out.bin')
r2-p007 · Local Sep Built-in Override
1def test():
2 sep = '|' # local override
3 print('a', '', sep=sep)
4 print('b', '', sep='') # triggers FURB105
5test()
--- orig.py	2026-10-17 17:28:23.793255072 -0400
+++ fixed.py	2026-10-17 17:28:23.954251877 -0400
@@ -1,5 +1,5 @@
 def test():
     sep = '|'  # local override
     print('a', '', sep=sep)
-    print('b', '', sep='')  # triggers FURB105
+    print('b')  # triggers FURB105
 test()
a|
b
r2-p008 · Evaluation Order Side Effects Merged
1i = 0
2def inc():
3 global i
4 i += 1
5 return i
6print('', sep=inc(), end='')
7print(f' i={i}')