fixhunt

ruff RUF041

labelled unknown
Runs / 2026-10-17T21-44-59-800Z-hkcn-ruff-RUF041ruff 0.17.12026-10-17 21:44 UTC8 programs × 2 round(s)16 programs, 15 fired, 11 fixed, 0 findings, 1 rejectedmodel cost $0.0033

Analysis Nemotron 3 Super

Flags unnecessarily nested Literal types where inner Literals can be flattened.

Fix: Replaces nested Literal expressions with a single Literal containing all unique arguments from the inner Literals, preserving order of first occurrence and removing duplicates.

10 hazards
  • Rewriting Literal[Literal[expr]] where expr has side effects (e.g., function calls) changes evaluation from once per inner Literal to once in the flattened version, altering call count.
  • If inner Literals contain duplicate values, deduplication in the fix may break code relying on duplicate literal parameters for overload resolution or type narrowing.
  • Moving inline comments attached to inner Literal brackets (e.g., Literal["a" # comment ]) to the flattened Literal may detach them from their original syntactic context, changing comment association.
  • When nested Literals span multiple lines with trailing comments on some lines, the fix may reorder or lose those comments due to reformatting, violating the rule's noted unsafety condition.
  • If the outer Literal is used in a forward reference or string annotation, flattening may break evaluation if the inner Literals were relied upon for delayed processing.
  • In code that inspects the AST structure of Literal (e.g., via typing.get_args), the fix alters the nested structure expected by introspection tools or decorators.
  • If an inner Literal contains a mutable expression (e.g., Literal[[]]), though invalid, the fix would still flatten it, potentially exposing differences in how type checkers handle malformed literals.
  • When the nested Literal appears in a class body or function default, flattening could affect the timing of when the literal value is computed relative to other statements in the same block.
  • If the inner Literals are defined via imports or aliases that are later rebound, the fix captures their values at rewrite time, breaking dynamic behavior.
  • In code that uses Literal in conjunction with Final or ClassVar, flattening may interfere with how tools detect constantness if they rely on the Literal node structure.

Programs

idresulttitlehazard
r1-p001fix okShadowed Builtins - Literal[int] Flatteningshadowed builtins and names redefined later in the module
r1-p002fired, no fixComments and Docstrings - Preserved During Rewritecomments and docstrings attached to the removed or moved code
r1-p003invalidAsync Generators - Await Ordering Dependencyasync def/await ordering, async generators
r1-p004fix okClass Body Default - Timing of EvaluationWhen the nested Literal appears in a class body or function default, flattening could affect the timing of when the literal value is computed relative to other statements in the same block.
r1-p005fix okAST Inspection - get_args Structure ChangeIn code that inspects the AST structure of Literal (e.g., via typing.get_args), the fix alters the nested structure expected by introspection tools or decorators.
r1-p006fix ok prediction offContext Manager - Non-File Write Objectcontext managers that are not files, objects with write() but no close()
r1-p007fix okFinal/ClassVar - Constantness DetectionIn code that uses Literal in conjunction with Final or ClassVar, flattening may interfere with how tools detect constantness if they rely on the Literal node structure.
r1-p008fix okStar Imports - Module-Level Side Effectsstar-imports, module-level side effects, __all__
r2-p001fix okMutable Default Argument Aliasingmutable default arguments and aliasing after a rewrite
r2-p002fix okString Formatting Edge Casesstring formatting edge cases: %, format, f-strings, repr vs str, nested quotes
r2-p003fired, no fixInline Comment DetachmentMoving inline comments attached to inner Literal brackets
r2-p004fix okNon-File Context Manager with write()context managers that are not files, objects with write() but no close()
r2-p005fired, no fixMulti-line Literal with Trailing CommentsWhen nested Literals span multiple lines with trailing comments on some lines
r2-p006fix okClass Body Timing of EvaluationWhen the nested Literal appears in a class body or function default
r2-p007fix okGenerators vs Lists Consumptiongenerators vs lists, iterators consumed once, lazy vs eager evaluation
r2-p008fired, no fixComments and Docstrings on Removed Codecomments and docstrings attached to the removed or moved code

Sources

r1-p001 · Shadowed Builtins - Literal[int] Flattening
1from typing import Literal
2int = lambda: print('custom_int')
3x = Literal[Literal['a', 'b']]
4print(int(), getattr(x, '__args__', x))
5del int
6int = 42
7try:
8 print(int)
9except Exception as e:
10 print(type(e).__name__, str(e))
--- orig.py	2026-10-17 17:45:09.538052614 -0400
+++ fixed.py	2026-10-17 17:45:09.722051411 -0400
@@ -1,6 +1,6 @@
 from typing import Literal
 int = lambda: print('custom_int')
-x = Literal[Literal['a', 'b']]
+x = Literal['a', 'b']
 print(int(), getattr(x, '__args__', x))
 del int
 int = 42
custom_int
None ('a', 'b')
42
r1-p002 · Comments and Docstrings - Preserved During Rewrite
1from typing import Literal
2# This comment should stay after rewrite
3x = Literal[
4 Literal['hello'], # inline comment
5 Literal['world']
6] # trailing comment
7print(x.__args__)
8# Another comment to ensure attachment matters
('hello', 'world')
r1-p003 · Async Generators - Await Ordering Dependency (generated program is not deterministic)
1from typing import Literal
2async def gen():
3 yield 1
4 await gen()
5async def main():
6 x = Literal[Literal[gen()], Literal[2]]
7 async for v in gen():
8 print(v, x.__args__)
9import asyncio
10asyncio.run(main())
r1-p004 · Class Body Default - Timing of Evaluation
1from typing import Literal
2counter = 0
3def inc():
4 global counter
5 counter += 1
6 return counter
7class C:
8 x = Literal[Literal[inc()], Literal[inc()]]
9 y = counter
10print(C.x.__args__, C.y)
--- orig.py	2026-10-17 17:45:10.379047116 -0400
+++ fixed.py	2026-10-17 17:45:10.566045895 -0400
@@ -5,6 +5,6 @@
     counter += 1
     return counter
 class C:
-    x = Literal[Literal[inc()], Literal[inc()]]
+    x = Literal[inc(), inc()]
     y = counter
 print(C.x.__args__, C.y)
(1, 2) 2
r1-p005 · AST Inspection - get_args Structure Change
1from typing import Literal, get_args
2x = Literal[Literal['a', 'b'], Literal['c']]
3args = get_args(x)
4print(len(args), [get_args(a) if hasattr(a, '__args__') else a for a in args])
--- orig.py	2026-10-17 17:45:10.670045215 -0400
+++ fixed.py	2026-10-17 17:45:10.858043986 -0400
@@ -1,4 +1,4 @@
 from typing import Literal, get_args
-x = Literal[Literal['a', 'b'], Literal['c']]
+x = Literal['a', 'b', 'c']
 args = get_args(x)
 print(len(args), [get_args(a) if hasattr(a, '__args__') else a for a in args])
3 ['a', 'b', 'c']
r1-p006 · Context Manager - Non-File Write Object
1from typing import Literal
2class Writer:
3 def __init__(self):
4 self.data = []
5 def write(self, s):
6 self.data.append(s)
7 def __enter__(self):
8 return self
9 def __exit__(self, *args):
10 pass
11w = Writer()
12with w as f:
13 f.write('test')
14x = Literal[Literal['a'], Literal['b']]
15print(w.data, x.__args__)
--- orig.py	2026-10-17 17:45:10.968043267 -0400
+++ fixed.py	2026-10-17 17:45:11.149042085 -0400
@@ -11,5 +11,5 @@
 w = Writer()
 with w as f:
     f.write('test')
-x = Literal[Literal['a'], Literal['b']]
+x = Literal['a', 'b']
 print(w.data, x.__args__)
['test'] ('a', 'b')
r1-p007 · Final/ClassVar - Constantness Detection
1from typing import Literal, Final, ClassVar
2NESTED = Literal[Literal['x'], Literal['y']]
3FLAT: Final = Literal['x', 'y']
4CLASS_VAR: ClassVar = Literal['x', 'y']
5print(NESTED.__args__, FLAT.__args__, CLASS_VAR.__args__)
--- orig.py	2026-10-17 17:45:11.258041373 -0400
+++ fixed.py	2026-10-17 17:45:11.434040224 -0400
@@ -1,5 +1,5 @@
 from typing import Literal, Final, ClassVar
-NESTED = Literal[Literal['x'], Literal['y']]
+NESTED = Literal['x', 'y']
 FLAT: Final = Literal['x', 'y']
 CLASS_VAR: ClassVar = Literal['x', 'y']
 print(NESTED.__args__, FLAT.__args__, CLASS_VAR.__args__)
('x', 'y') ('x', 'y') ('x', 'y')
r1-p008 · Star Imports - Module-Level Side Effects
1from typing import Literal
2x = Literal[Literal['a', 'b'], Literal['c']]
3print(x.__args__)
4def side_effect():
5 global x
6 x = Literal['d']
7side_effect()
8print(x.__args__)
--- orig.py	2026-10-17 17:45:11.549039473 -0400
+++ fixed.py	2026-10-17 17:45:11.725038323 -0400
@@ -1,5 +1,5 @@
 from typing import Literal
-x = Literal[Literal['a', 'b'], Literal['c']]
+x = Literal['a', 'b', 'c']
 print(x.__args__)
 def side_effect():
     global x
('a', 'b', 'c')
('d',)
r2-p001 · Mutable Default Argument Aliasing
1from typing import Literal
2
3def func(a=Literal[Literal['x'], Literal['y']]):
4 a.append('z') if hasattr(a, 'append') else None
5 return a
6
7print(func())
8print(func())
--- orig.py	2026-10-17 17:45:16.442007569 -0400
+++ fixed.py	2026-10-17 17:45:16.631006339 -0400
@@ -1,6 +1,6 @@
 from typing import Literal
 
-def func(a=Literal[Literal['x'], Literal['y']]):
+def func(a=Literal['x', 'y']):
     a.append('z') if hasattr(a, 'append') else None
     return a
 
typing.Literal['x', 'y']
typing.Literal['x', 'y']
r2-p002 · String Formatting Edge Cases
1from typing import Literal
2
3x = Literal[Literal['hello'], Literal['world']]
4s = '%s %s' % (x.__args__[0][0], x.__args__[1][0])
5print(s)
6print(f'{x.__args__[0][0]} {x.__args__[1][0]}')
7print(str(x))
8print(repr(x))
--- orig.py	2026-10-17 17:45:16.747005584 -0400
+++ fixed.py	2026-10-17 17:45:16.943004308 -0400
@@ -1,6 +1,6 @@
 from typing import Literal
 
-x = Literal[Literal['hello'], Literal['world']]
+x = Literal['hello', 'world']
 s = '%s %s' % (x.__args__[0][0], x.__args__[1][0])
 print(s)
 print(f'{x.__args__[0][0]} {x.__args__[1][0]}')
h w
h w
typing.Literal['hello', 'world']
typing.Literal['hello', 'world']
r2-p003 · Inline Comment Detachment
1from typing import Literal
2
3# Comment before
4x = Literal[
5 Literal['a' # inline comment for a
6 ],
7 Literal['b'] # inline comment for b
8]
9print(x.__args__)
('a', 'b')
r2-p004 · Non-File Context Manager with write()
1from typing import Literal
2from io import StringIO
3
4class WriteOnly:
5 def __enter__(self):
6 return self
7 def __exit__(self, *args):
8 pass
9 def write(self, s):
10 self.data = s
11
12x = Literal[Literal['stdout'], Literal['stderr']]
13with WriteOnly() as w:
14 w.write(str(x))
15print(w.data)
--- orig.py	2026-10-17 17:45:17.247002330 -0400
+++ fixed.py	2026-10-17 17:45:17.446001035 -0400
@@ -9,7 +9,7 @@
     def write(self, s):
         self.data = s
 
-x = Literal[Literal['stdout'], Literal['stderr']]
+x = Literal['stdout', 'stderr']
 with WriteOnly() as w:
     w.write(str(x))
 print(w.data)
typing.Literal['stdout', 'stderr']
r2-p005 · Multi-line Literal with Trailing Comments
1from typing import Literal
2
3x = Literal[
4 Literal['a'], # first
5 Literal['b'], # second
6 Literal['c'] # third
7]
8print([arg for sub in x.__args__ for arg in sub.__args__])
r2-p006 · Class Body Timing of Evaluation
1from typing import Literal
2
3LIT = Literal[Literal['init']]
4
5class C:
6 attr = LIT # Evaluated at class definition time
7 def __init__(self):
8 self.val = self.attr
9
10print(C().val)
--- orig.py	2026-10-17 17:45:17.944997789 -0400
+++ fixed.py	2026-10-17 17:45:18.141996508 -0400
@@ -1,6 +1,6 @@
 from typing import Literal
 
-LIT = Literal[Literal['init']]
+LIT = Literal['init']
 
 class C:
     attr = LIT  # Evaluated at class definition time
typing.Literal['init']
r2-p007 · Generators vs Lists Consumption
1from typing import Literal
2
3def gen():
4 for item in Literal[Literal['a'], Literal['b']]:
5 yield item
6
7g = gen()
8print(list(g)) # First consumption
9print(list(g)) # Second consumption (should be empty)
--- orig.py	2026-10-17 17:45:18.261995728 -0400
+++ fixed.py	2026-10-17 17:45:18.448994512 -0400
@@ -1,7 +1,7 @@
 from typing import Literal
 
 def gen():
-    for item in Literal[Literal['a'], Literal['b']]:
+    for item in Literal['a', 'b']:
         yield item
 
 g = gen()
[typing.Unpack[typing.Literal['a', 'b']]]
[]
r2-p008 · Comments and Docstrings on Removed Code
1from typing import Literal
2
3"""Module docstring."""
4
5x = Literal[
6 Literal['alpha'], # Keep this
7 Literal['beta'] # Remove this line? No, we keep both.
8]
9"""This is a docstring for x"""
10print(x.__args__)
('alpha', 'beta')