fixhunt

ruff RUF027

labelled unknown
Runs / 2026-10-17T21-42-53-372Z-3lu1-ruff-RUF027ruff 0.17.12026-10-17 21:42 UTC8 programs × 2 round(s)16 programs, 4 fired, 0 fixed, 0 findings, 1 rejectedmodel cost $0.0060

Analysis Nemotron 3 Super

Flags string literals containing curly-brace expressions without an 'f' prefix when they appear to be intended as f-strings.

Fix: Adds an 'f' prefix to the string literal, converting it into an f-string so that expressions within curly braces are evaluated.

12 hazards
  • The string may be used in a context expecting a literal string with braces (e.g., regex, SQL, or templating), where adding 'f' would cause runtime errors due to undefined variable names or syntax issues.
  • If the string contains backslashes, the f-string prefix changes how escape sequences are interpreted (e.g., '\n' vs. actual newline), potentially altering the string value.
  • Comments placed inside the string literal (e.g., via concatenation or multi-line strings) may be misinterpreted as code or removed during parsing after the fix.
  • In triple-quoted strings, adding 'f' may interfere with indentation processing if the string relies on dedent behavior that differs between raw and f-string handling.
  • If the string is part of a larger expression (e.g., concatenation with other strings or variables), the fix may break operator precedence or introduce unintended grouping.
  • When the string appears in a dictionary key or set literal, the rewritten f-string may raise a TypeError at runtime if the expression evaluates to an unhashable type.
  • If the string is used in a byte string context (e.g., b"{x}"), the fix is invalid and would produce a SyntaxError, though Ruff should avoid suggesting it in such cases.
  • The fix may inadvertently capture variables from an outer scope that were not intended to be used, especially in nested functions or comprehensions, changing closure behavior.
  • In f-strings, expressions are evaluated left-to-right; if the original string relied on brace literals for formatting (e.g., in a loop generating templates), evaluation order side effects could emerge.
  • If the string contains a '#' inside braces (e.g., "{x#comment}"), the f-string parser may interpret it as a comment, truncating the expression and causing a SyntaxError or incorrect evaluation.
  • When the string is passed to a function like gettext or logging that expects a format string with named placeholders, the f-string will evaluate the expressions immediately, bypassing the intended deferred formatting mechanism.
  • If the string is defined but never used (dead code), the fix still applies and may introduce unnecessary runtime overhead or NameErrors if variables are undefined.

Programs

idresulttitlehazard
r1-p001silent prediction offString comment inside triple-quoted literal misparsed as code after f-string conversionComments placed inside the string literal (e.g., via concatenation or multi-line strings) may be misinterpreted as code or removed during parsing after the fix.
r1-p002silent prediction offLeft-to-right evaluation in f-string exposes side effects in original templateIn f-strings, expressions are evaluated left-to-right; if the original string relied on brace literals for formatting (e.g., in a loop generating templates), evaluation order side effects could emerge
r1-p003silent prediction offMutable default argument captures f-string expression state across callsmutable default arguments and aliasing after a rewrite
r1-p004silentNon-file context manager with write() but no close() misused in f-string loggingcontext managers that are not files, objects with write() but no close()
r1-p005silent prediction offGenerator expression in list comprehension consumed once after f-string rewritegenerators vs lists, iterators consumed once, lazy vs eager evaluation
r1-p006silent prediction offKeyword-only argument default evaluated once, f-string captures stale valuekeyword-only, positional-only, *args/**kwargs forwarding, defaults evaluated once
r1-p007fired, no fixFloat division rounding differs when f-string evaluates expression eagerlyint/float/bool coercion, Decimal, division and rounding
r1-p008silent prediction offClass attribute shadowed by instance attribute, f-string binds to wrong scopeclass attributes vs instance attributes, descriptors, properties with side effects
r2-p001fired, no fixDictionary key with unhashable expressionWhen the string appears in a dictionary key or set literal, the rewritten f-string may raise a TypeError at runtime if the expression evaluates to an unhashable type.
r2-p002silent prediction offGenerator consumed once in list comprehensiongenerators vs lists, iterators consumed once, lazy vs eager evaluation
r2-p003fired, no fixgettext bypasses deferred formattingWhen the string is passed to a function like gettext or logging that expects a format string with named placeholders, the f-string will evaluate the expressions immediately, bypassing the intended def
r2-p004silent prediction offClosure captures outer variable incorrectlyThe fix may inadvertently capture variables from an outer scope that were not intended to be used, especially in nested functions or comprehensions, changing closure behavior.
r2-p005silent prediction offBackslash escape sequence changedIf the string contains backslashes, the f-string prefix changes how escape sequences are interpreted (e.g., '\n' vs. actual newline), potentially altering the string value.
r2-p006silent prediction offDead code introduces NameErrorIf the string is defined but never used (dead code), the fix still applies and may introduce unnecessary runtime overhead or NameErrors if variables are undefined.
r2-p007invalidTriple-quoted string dedent interferenceIn triple-quoted strings, adding 'f' may interfere with indentation processing if the string relies on dedent behavior that differs between raw and f-string handling.
r2-p008fired, no fixRegex pattern with braces treated as f-stringThe string may be used in a context expecting a literal string with braces (e.g., regex, SQL, or templating), where adding 'f' would cause runtime errors due to undefined variable names or syntax issu

Sources

r1-p001 · String comment inside triple-quoted literal misparsed as code after f-string conversion
1s = '''
2# This is a comment
3{x}
4'''
5print(eval('f' + repr(s)))
r1-p002 · Left-to-right evaluation in f-string exposes side effects in original template
1counter = 0
2def inc():
3 global counter
4 counter += 1
5 return counter
6s = '{inc()} {inc()}'
7print('Before fix:', s.format(inc=inc))
8print('After fix :', eval('f"' + s[1:-1] + '"'))
r1-p003 · Mutable default argument captures f-string expression state across calls
1def f(s='{x}'):
2 return eval('f"' + s[1:-1] + '"')
3x = 1
4print(f())
5x = 2
6print(f())
x
x
r1-p004 · Non-file context manager with write() but no close() misused in f-string logging
1class LogSink:
2 def __enter__(self): return self
3 def __exit__(self, *a): pass
4 def write(self, msg): print('LOG:', msg.strip())
5with LogSink() as sink:
6 sink.write('Error: {code}'.format(code=500))
LOG: Error: 500
r1-p005 · Generator expression in list comprehension consumed once after f-string rewrite
1vals = (i for i in [1,2])
2s = ['Item {v}' for v in vals]
3print('First use :', s)
4print('Second use:', [x for x in s])
First use : ['Item {v}', 'Item {v}']
Second use: ['Item {v}', 'Item {v}']
r1-p006 · Keyword-only argument default evaluated once, f-string captures stale value
1def f(*, template='Value: {x}'):
2 return eval('f"' + template[1:-1] + '"')
3x = 1
4print(f())
5x = 2
6print(f())
r1-p007 · Float division rounding differs when f-string evaluates expression eagerly
1x = 1
2s = 'Result: {x}/3'
3print('Before fix:', s.format(x=x))
4print('After fix :', eval('f"' + s[1:-1] + '"'))
5x = 2
6print('Before fix:', s.format(x=x))
7print('After fix :', eval('f"' + s[1:-1] + '"'))
Before fix: Result: 1/3
After fix : esult: 1/
Before fix: Result: 2/3
After fix : esult: 2/
r1-p008 · Class attribute shadowed by instance attribute, f-string binds to wrong scope
1class C:
2 x = 'class'
3 def __init__(self):
4 self.x = 'instance'
5 def show(self):
6 s = 'Value: {x}'
7 return eval('f"' + s[1:-1] + '"')
8c = C()
9print(c.show())
r2-p001 · Dictionary key with unhashable expression
1x = [1, 2]
2d = {"Key {x}": "value"} # Rule triggers: adds f-prefix -> f"Key {[1, 2]}"
3try:
4 print(d)
5except TypeError as e:
6 print(f"TypeError: {type(e).__name__}: {e}")
{'Key {x}': 'value'}
r2-p002 · Generator consumed once in list comprehension
1def gen():
2 for i in range(2):
3 yield i
4g = gen()
5result = [s for s in [f"Item {next(g)}"] for _ in range(2)] # Rule triggers on f"Item {next(g)}"
6print(f"Result: {result}") # Second iteration sees exhausted generator
Result: ['Item 0', 'Item 0']
r2-p003 · gettext bypasses deferred formatting
1import gettext
2name = 'Alice'
3def translate(msg):
4 return msg # Simulate gettext: returns raw msg
5translated = translate("Hello {name}") # Rule triggers: becomes f"Hello {name}"
6print(f"Translated: '{translated}'") # Now contains evaluated 'Alice', not placeholder
Translated: 'Hello {name}'
r2-p004 · Closure captures outer variable incorrectly
1x = 10
2def make_printer():
3 return lambda: print(f"Value: {x}") # Rule triggers: string was "Value: {x}"
4printer = make_printer()
5x = 99 # Rebinding after closure creation
6printer() # Prints 99, but if original was meant to capture x=10 at def-time, this is wrong
Value: 99
r2-p005 · Backslash escape sequence changed
1s = "Path: \\u{code}" # Rule triggers: becomes f"Path: \\u{code}"
2code = 65
3print(repr(s)) # In raw string: '\\\\u{65}' -> after f: '\\\\uA' (because \uXX is interpreted)
4# Actual output: 'Path: \\uA' -> but wait: in f-string, \u65 is 'A', so becomes 'Path: A'
5# Let's make it clearer:
'Path: \\u{code}'
r2-p006 · Dead code introduces NameError
1def unused():
2 msg = "Undefined {var}" # Rule triggers: becomes f"Undefined {var}" -> NameError if called
3 # Never called, so no error... but let's make it so definition alone causes issue? Can't.
4 # Instead: define in global scope, but if var not defined, even defining f-string errors.
5# So move to global:
6UNUSED_MSG = "Undefined {var}" # Rule triggers: f-string -> NameError at module load if var not defined
7# But we want to catch it:
8try:
9 UNUSED_MSG = "Undefined {var}"
10except NameError as e:
11 print(f"NameError at definition: {type(e).__name__}: {e}")
12# Actually, f-string definition does NOT evaluate expressions until execution.
13# So this won't trigger. Need different approach.
14# Hazard: even if unused, if later used in eval or exec, it may error.
15# Let's do:
16UNUSED_MSG = "Undefined {var}" # Not triggered yet
17if False: # Dead code block
18 print(UNUSED_MSG) # Never runs
19# But defining UNUSED_MSG as f-string doesn't error.
20# Real issue: if someone does eval(UNUSED_MSG) later, and var not defined.
21# But that's not dead code.
22# Reconsider: the fix introduces runtime overhead (always evaluating) even if string never used.
23# We can't observe overhead easily.
24# Instead: make it so that the act of defining the f-string in dead code causes error because var is missing in enclosing scope?
25# No.
26# Let's skip and use: the string is defined but not used, and we later delete the variable it depends on.
27# But if never used, deletion doesn't matter.
28# Better: the string is built but not used, and we rely on it being a literal for later reconstruction.
29# Too vague.
30# Accept: we show that if the dead code were to be executed, it would error.
31# So we define it in a function that's never called, but if called would error.
32# And we note that the fix makes it so that IF called, it errors earlier/lazily same.
33# Not showing difference.
34# New idea: the string is in a class body, unused, but class creation evaluates f-string? No.
35# I think the hazard is weak for dead code, but we must comply.
36# Let's do:
37if False:
38 dead = f"{undef}" # Rule would have triggered on "{undef}" -> now f-string
39# But since in dead block, not executed.
40# No output.
41# Instead, let's make the dead code affect something else via side effect in definition? Impossible.
42# Give up and use: the string is assigned but not used, and we later inspect its value via globals() and see it's f-string.
43# But that doesn't change behavior.
44# We'll output that the dead code string evaluates to something when forced.
45# So:
46deaf = "Value {x}" # Trigger: becomes f"Value {x}"
47x = 5
48if False:
49 print(deaf) # Dead
50else:
51 # Force evaluation by using in a context that doesn't use value? Hard.
52 # Just show that if we were to use it, it would be f'ed.
53 print(f"Dead code string repr: {repr(deaf)}") # Shows it's f-string value
Dead code string repr: 'Value {x}'
r2-p007 · Triple-quoted string dedent interference (generated program does not parse: Traceback (most recent call last):)
1text = """ Line1
2 {x}
3 Line2""" # Rule triggers: becomes f""" Line1
4 {x}
5 Line2"""
6x = 42
7print(repr(text)) # Shows indentation preserved; f-string doesn't change dedent by itself.
8# But if combined with textwrap.dedent:
9import textwdedent = textwrap.dedent
10normal = textdedent(""" Line1
11 {x}
12 Line2""") # Not triggered
13fix_dedent = textdedent(f""" Line1
14 {x}
15 Line2""") # Triggered: f-string first, then dedent
16print(f"Normal dedent: {repr(normal)}")
17print(f"Fix dedent: {repr(fix_dedent)}")
r2-p008 · Regex pattern with braces treated as f-string
1import re
2pattern = "a{b}c{d}" # Rule triggers: becomes f"a{b}c{d}"
3try:
4 re.compile(pattern) # Tries to replace {b} and {d} with vars
5except NameError as e:
6 print(f"NameError: {type(e).__name__}: {e}")
7# If we define b,d:
8b, d = 1, 2
9pattern2 = "a{b}c{d}"
10print(f"Regex matches: {bool(re.fullmatch(pattern2, 'a1c2'))}")
Regex matches: False