fixhunt

ruff UP025

labelled unknown
Runs / 2026-10-17T21-19-03-391Z-adcc-ruff-UP025ruff 0.17.12026-10-17 21:19 UTC8 programs × 2 round(s)16 programs, 10 fired, 10 fixed, 0 findings, 4 rejectedmodel cost $0.0031

Analysis Nemotron 3 Super

Flags unnecessary 'u' prefixes on string literals since all strings are Unicode in Python 3.

Fix: Removes the 'u' prefix from string literals, leaving the literal unchanged otherwise.

10 hazards
  • Rewriting ur"..." to r"..." removes the Unicode guarantee, potentially altering behavior if non-ASCII characters rely on implicit decoding in Python 2 contexts (though irrelevant in pure Python 3).
  • Rewriting u"..." to "..." may expose reliance on the 'u' prefix for disambiguation in code generation or eval contexts where the prefix is inspected as source.
  • If the 'u' prefix is part of a larger token due to missing whitespace (e.g., u"x"+y), removing it could change tokenization and break parsing.
  • In bytestring concatenation like u"a" + b"b", removing 'u' does not affect type but could mislead readers about intent, though behavior remains identical.
  • The fix does not adjust surrounding whitespace or formatting, so comments attached via line continuations or inline may become misaligned if the prefix removal alters visual indentation in multi-line edits.
  • In f-strings like uf"{expr}", removing 'u' yields f"{expr}", which is equivalent in Python 3, but if the code is processed by a tool that expects the 'u' prefix for Unicode enforcement, downstream tooling may behave differently.
  • When used in annotations or stringized expressions (e.g., typing annotations), the 'u' prefix has no semantic value, but its removal could affect tools that parse source for metadata.
  • If the string literal is part of a macro or preprocessor-like scheme (e.g., via custom imports or exec), removing 'u' could alter the seen source and break expected transformations.
  • In rare cases involving the tokenizer API or ast.unparse, preserving the exact source form may be required for round-trip fidelity, and removing 'u' breaks that.
  • The rewrite assumes the literal is a string; if mistakenly applied to non-string contexts due to buggy detection, it would corrupt syntax.

Programs

idresulttitlehazard
r1-p001fix okClass Attributes vs Instance AttributesRemoving 'u' prefix changes object identity when used as descriptor __get__ return value affecting attribute access semantics.
r1-p002fix okMutable Default Arguments and AliasingRemoving 'u' prefix causes mutable default list aliasing across function calls due to shared reference preservation.
r1-p003invalidContext Managers Without Close()Removed 'u' prefix alters string returned by __enter__, changing what gets bound in 'as' clause and affecting subsequent method calls.
r1-p004invalidWhitespace and Inline Comments MisalignmentRemoving 'u' prefix shifts visual alignment of inline comment via line continuation, causing misinterpretation though syntax unchanged.
r1-p005fix okWalrus, Comprehensions, Late BindingRemoved 'u' prefix in walrus expression changes captured value in late-binding lambda due to timing of evaluation.
r1-p006silent prediction offTokenizer API and ast.unparse Round-Trip FidelityRemoving 'u' prefix breaks source code round-trip when using ast.unparse, altering exact lexical form expected by tools.
r1-p007invalidAnnotations and Stringized ExpressionsRemoved 'u' prefix in annotation string affects tools that parse source for type metadata, even though semantically identical.
r1-p008fix okExceptions, Finally/Else ClausesRemoved 'u' prefix in string raised as exception changes args, affecting exception matching in except clauses.
r2-p001fix okGenerator exhaustion after u-prefixed string splitgenerators vs lists, iterators consumed once, lazy vs eager evaluation
r2-p002fix okShadowing open then using u-strings in pathsshadowed builtins and names redefined later in the module
r2-p003fix okWalrus in comprehension capturing u-string suffixwalrus, comprehensions scoping, late binding in closures and lambdas
r2-p004silentRound-trip via ast.unparse of module with u-stringIn rare cases involving the tokenizer API or ast.unparse, preserving the exact source form may be required for round-trip fidelity, and removing 'u' breaks that.
r2-p005invalidLine continuation comment misalignment after u-removalThe fix does not adjust surrounding whitespace or formatting, so comments attached via line continuations or inline may become misaligned if the prefix removal alters visual indentation in multi-line
r2-p006fix ok prediction offNon-string target: u prefix on integer (simulated misfire)The rewrite assumes the literal is a string; if mistakenly applied to non-string contexts due to buggy detection, it would corrupt syntax.
r2-p007fix okTruthiness via __len__ of u-string in custom classtruthiness of custom __bool__/__len__, None vs falsy, 'is' vs '=='
r2-p008fix okAsync generator yielding u-strings, awaited onceasync def/await ordering, async generators

Sources

r1-p001 · Class Attributes vs Instance Attributes
1class D:
2 def __init__(self):
3 self.val = None
4 def __get__(self, obj, owner):
5 return getattr(obj, '_d', u'test')
6
7class C:
8 d = D()
9 def __init__(self):
10 self._d = None
11
12c = C()
13print(c.d)
14c._d = 'changed'
15print(c.d)
--- orig.py	2026-10-17 17:19:12.560995538 -0400
+++ fixed.py	2026-10-17 17:19:12.725987319 -0400
@@ -2,7 +2,7 @@
     def __init__(self):
         self.val = None
     def __get__(self, obj, owner):
-        return getattr(obj, '_d', u'test')
+        return getattr(obj, '_d', 'test')
 
 class C:
     d = D()
None
changed
r1-p002 · Mutable Default Arguments and Aliasing
1def f(x=[]):
2 x.append(u'a')
3 return x
4print(f())
5print(f())
6def g(y=[]):
7 y.append('b')
8 return y
9g()
10print(g())
--- orig.py	2026-10-17 17:19:12.832981989 -0400
+++ fixed.py	2026-10-17 17:19:12.987974268 -0400
@@ -1,5 +1,5 @@
 def f(x=[]):
-    x.append(u'a')
+    x.append('a')
     return x
 print(f())
 print(f())
['a']
['a', 'a']
['b', 'b']
r1-p003 · Context Managers Without Close() (generated program does not parse: Traceback (most recent call last):)
1class CM:\n def __enter__(self):\n return u'hello'
2 def __exit__(self, *exc):\n pass\nwith CM() as s:\n print(s.upper())\\n# After fix: returns 'hello' -> still works, but test relies on u-prefix presence for trace\n# To make it fail: need mutation? Not possible. Instead: use s in closure\ndef make_printer():\n with CM() as s:\n return lambda: print(s)\nmake_printer()()
r1-p004 · Whitespace and Inline Comments Misalignment (generated program does not parse: Traceback (most recent call last):)
1msg = u'error: '\\ # This comment should stay aligned\n 'details'
2print(msg)
r1-p005 · Walrus, Comprehensions, Late Binding
1funcs = []
2for i in range(2):
3 if (s := u'value'+str(i)): # u-prefix here
4 funcs.append(lambda: s)
5for f in funcs:
6 print(f())
--- orig.py	2026-10-17 17:19:13.371955173 -0400
+++ fixed.py	2026-10-17 17:19:13.539946820 -0400
@@ -1,6 +1,6 @@
 funcs = []
 for i in range(2):
-    if (s := u'value'+str(i)):  # u-prefix here
+    if (s := 'value'+str(i)):  # u-prefix here
         funcs.append(lambda: s)
 for f in funcs:
     print(f())
value1
value1
r1-p006 · Tokenizer API and ast.unparse Round-Trip Fidelity
1import ast
2tree = ast.parse("u'test'")
3unparsed = ast.unparse(tree)
4print(unparsed)\
5# Before: u'"test"' -> after fix: "test"
6# So unparsed loses u-prefix, breaking fidelity
u"""test"""
r1-p007 · Annotations and Stringized Expressions (generated program does not parse: Traceback (most recent call last):)
1def f(x: 'u'int') -> None:
2 pass
3import typing
4hints = typing.get_type_hints(f)
5print(hints.get('x')) # Depends on string content; 'u'int'' vs 'int' may be parsed differently by some tools
r1-p008 · Exceptions, Finally/Else Clauses
1try:
2 raise ValueError(u'error')
3except ValueError as e:
4 print(e.args)
5finally:
6 print('cleanup')
--- orig.py	2026-10-17 17:19:13.941926831 -0400
+++ fixed.py	2026-10-17 17:19:14.104918735 -0400
@@ -1,5 +1,5 @@
 try:
-    raise ValueError(u'error')
+    raise ValueError('error')
 except ValueError as e:
     print(e.args)
 finally:
('error',)
cleanup
r2-p001 · Generator exhaustion after u-prefixed string split
1def gen_from_u(s):
2 yield from s.split()
3g = gen_from_u(u'one two three')
4first = list(g)
5second = list(g) # exhausted
6print('first:', first)
7print('second:', second)
--- orig.py	2026-10-17 17:19:19.673643497 -0400
+++ fixed.py	2026-10-17 17:19:19.832635676 -0400
@@ -1,6 +1,6 @@
 def gen_from_u(s):
     yield from s.split()
-g = gen_from_u(u'one two three')
+g = gen_from_u('one two three')
 first = list(g)
 second = list(g)  # exhausted
 print('first:', first)
first: ['one', 'two', 'three']
second: []
r2-p002 · Shadowing open then using u-strings in paths
1open = lambda x: f'MOCK:{x}'
2def load(p): return open(p)
3print(load(u'/tmp/file'))
4del open # restore built-in
5try:
6 open(u'/tmp/file')
7except Exception as e:
8 print(f'{type(e).__name__}: {e}')
--- orig.py	2026-10-17 17:19:19.939630412 -0400
+++ fixed.py	2026-10-17 17:19:20.113621862 -0400
@@ -1,8 +1,8 @@
 open = lambda x: f'MOCK:{x}'
 def load(p): return open(p)
-print(load(u'/tmp/file'))
+print(load('/tmp/file'))
 del open  # restore built-in
 try:
-    open(u'/tmp/file')
+    open('/tmp/file')
 except Exception as e:
     print(f'{type(e).__name__}: {e}')
MOCK:/tmp/file
FileNotFoundError: [Errno 2] No such file or directory: '/tmp/file'
r2-p003 · Walrus in comprehension capturing u-string suffix
1suffix = u'_end'
2results = [ (n := i) + len(suffix) for i in range(3) ]
3print('results:', results)
4print('final n:', n)
5sufix = 'changed' # typo intentional: suffix not rebound
6lamb = lambda: len(suffix)
7print('lambda result:', lamb())
--- orig.py	2026-10-17 17:19:20.213616951 -0400
+++ fixed.py	2026-10-17 17:19:20.381608701 -0400
@@ -1,4 +1,4 @@
-suffix = u'_end'
+suffix = '_end'
 results = [ (n := i) + len(suffix) for i in range(3) ]
 print('results:', results)
 print('final n:', n)
results: [4, 5, 6]
final n: 2
lambda result: 4
r2-p004 · Round-trip via ast.unparse of module with u-string
1import ast
2code = """
3u_msg = u'Hello\nWorld'
4print(u_msg)
5"""
6tree = ast.parse(code)
7unparsed = ast.unparse(tree)
8print('=== ORIGINAL ===')
9print(code)
10print('=== UNPARSED ===')
11print(unparsed)
12print('Same?', code == unparsed)
13try:
14 exec(unparsed)
15except Exception as e:
16 print(f'{type(e).__name__}: {e}')
r2-p005 · Line continuation comment misalignment after u-removal (generated program does not parse: Traceback (most recent call last):)
1msg = u'first\' # x=1
2 + u'second\' # y=2
3 + u'third' # z=3
4print(repr(msg))
5# After fix, visual alignment of comments may shift relative to code
r2-p006 · Non-string target: u prefix on integer (simulated misfire)
1# This tests detection robustness: ensure u-only triggers on strings
2val = 42
3u_val = u'42' # should trigger
4print('val == int(u_val)?', val == int(u_val))
5print('u_val type:', type(u_val))
6# If rule misfires on 'u_val = u42', SyntaxError would occur - we avoid that.
--- orig.py	2026-10-17 17:19:20.996578500 -0400
+++ fixed.py	2026-10-17 17:19:21.156570657 -0400
@@ -1,6 +1,6 @@
 # This tests detection robustness: ensure u-only triggers on strings
 val = 42
-u_val = u'42'  # should trigger
+u_val = '42'  # should trigger
 print('val == int(u_val)?', val == int(u_val))
 print('u_val type:', type(u_val))
 # If rule misfires on 'u_val = u42', SyntaxError would occur - we avoid that.
val == int(u_val)? True
u_val type: <class 'str'>
r2-p007 · Truthiness via __len__ of u-string in custom class
1class StrLen:
2 def __init__(self, s): self.s = s
3 def __len__(self): return len(self.s)
4 def __bool__(self): return bool(len(self.s))
5x = StrLen(u'')
6y = StrLen(u'nonempty')
7print('x is False:', x is False)
8print('x == False:', x == False)
9print('bool(x):', bool(x))
10print('bool(y):', bool(y))
--- orig.py	2026-10-17 17:19:21.256565755 -0400
+++ fixed.py	2026-10-17 17:19:21.425557471 -0400
@@ -2,8 +2,8 @@
     def __init__(self, s): self.s = s
     def __len__(self): return len(self.s)
     def __bool__(self): return bool(len(self.s))
-x = StrLen(u'')
-y = StrLen(u'nonempty')
+x = StrLen('')
+y = StrLen('nonempty')
 print('x is False:', x is False)
 print('x == False:', x == False)
 print('bool(x):', bool(x))
x is False: False
x == False: False
bool(x): False
bool(y): True
r2-p008 · Async generator yielding u-strings, awaited once
1async def aiter():
2 yield u'hello'
3 yield u'world'
4async def consume():
5 result = []
6 async for item in aiter():
7 result.append(item)
8 return result
9import asyncio
10out = asyncio.run(consume())
11print('result:', out)
12print('length:', len(out))
--- orig.py	2026-10-17 17:19:21.526552520 -0400
+++ fixed.py	2026-10-17 17:19:21.819538157 -0400
@@ -1,6 +1,6 @@
 async def aiter():
-    yield u'hello'
-    yield u'world'
+    yield 'hello'
+    yield 'world'
 async def consume():
     result = []
     async for item in aiter():
result: ['hello', 'world']
length: 2