ruff UP025
labelled unknown
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
| id | result | title | hazard |
|---|---|---|---|
| r1-p001 | fix ok | Class Attributes vs Instance Attributes | Removing 'u' prefix changes object identity when used as descriptor __get__ return value affecting attribute access semantics. |
| r1-p002 | fix ok | Mutable Default Arguments and Aliasing | Removing 'u' prefix causes mutable default list aliasing across function calls due to shared reference preservation. |
| r1-p003 | invalid | Context Managers Without Close() | Removed 'u' prefix alters string returned by __enter__, changing what gets bound in 'as' clause and affecting subsequent method calls. |
| r1-p004 | invalid | Whitespace and Inline Comments Misalignment | Removing 'u' prefix shifts visual alignment of inline comment via line continuation, causing misinterpretation though syntax unchanged. |
| r1-p005 | fix ok | Walrus, Comprehensions, Late Binding | Removed 'u' prefix in walrus expression changes captured value in late-binding lambda due to timing of evaluation. |
| r1-p006 | silent prediction off | Tokenizer API and ast.unparse Round-Trip Fidelity | Removing 'u' prefix breaks source code round-trip when using ast.unparse, altering exact lexical form expected by tools. |
| r1-p007 | invalid | Annotations and Stringized Expressions | Removed 'u' prefix in annotation string affects tools that parse source for type metadata, even though semantically identical. |
| r1-p008 | fix ok | Exceptions, Finally/Else Clauses | Removed 'u' prefix in string raised as exception changes args, affecting exception matching in except clauses. |
| r2-p001 | fix ok | Generator exhaustion after u-prefixed string split | generators vs lists, iterators consumed once, lazy vs eager evaluation |
| r2-p002 | fix ok | Shadowing open then using u-strings in paths | shadowed builtins and names redefined later in the module |
| r2-p003 | fix ok | Walrus in comprehension capturing u-string suffix | walrus, comprehensions scoping, late binding in closures and lambdas |
| r2-p004 | silent | Round-trip via ast.unparse of module with u-string | 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. |
| r2-p005 | invalid | Line continuation comment misalignment after u-removal | 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 |
| r2-p006 | fix ok prediction off | Non-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-p007 | fix ok | Truthiness via __len__ of u-string in custom class | truthiness of custom __bool__/__len__, None vs falsy, 'is' vs '==' |
| r2-p008 | fix ok | Async generator yielding u-strings, awaited once | async 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',)
cleanupr2-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