ruff RUF031
labelled unknown
Analysis Nemotron 3 Super
Flags non-empty tuple literals used as subscription indices when parentheses are present but disallowed by configuration.
Fix: Removes the outer parentheses around the tuple literal inside the subscription brackets, preserving inner elements and formatting exactly as written.
10 hazards
- Rewriting `(expr,)` as `expr,` changes a parenthesized expression into a tuple, altering meaning when `expr` is not a tuple-forming construct.
- If the tuple contains a starred expression like `*(xs,)`, removing parentheses may invalidate syntax or change unpacking behavior.
- In delayed evaluation contexts (e.g., lambdas or comprehensions inside the subscript), stripping parentheses could bind commas differently, altering structure.
- If the tuple appears in an annotation-like context not recognized by Ruff’s typing heuristic (e.g., string annotations or conditional imports), the fix may be applied inappropriately.
- When the tuple is part of a larger expression due to line continuation (e.g., backslash), removing parentheses may break syntactic grouping.
- If the tuple contains a walrus operator (`:=`), removing parentheses may change the scope or binding behavior of the assigned variable.
- Comments inside the parentheses (e.g., `(# x , 1)`) may be lost or misplaced if the rewrite does not preserve comment attachment.
- In nested subscriptions like `d[(1, 2)][(3, 4)]`, fixing only the outer tuple may produce invalid or misleading intermediate expressions if inner tuples are also problematic.
- If the subscript is part of a slice object construction via `__getitem__`, altering the tuple form may change how the slice is interpreted by the object’s implementation.
- When the tuple is generated via a macro, decorator, or codegen tool that relies on the parentheses for parsing, removal may break tooling even if runtime behavior is preserved.
Programs
| id | result | title | hazard |
|---|---|---|---|
| r1-p001 | fix ok | String Formatting Edge Case with Parentheses Removal | string formatting edge cases: %, format, f-strings, repr vs str, nested quotes |
| r1-p002 | fix ok | Comment Preservation Hazard | comments and docstrings attached to the removed or moved code |
| r1-p003 | silent prediction off | Walrus Operator Scope Change | If the tuple contains a walrus operator (`:=`), removing parentheses may change the scope or binding behavior of the assigned variable. |
| r1-p004 | fix ok | Context Manager Without Close() | context managers that are not files, objects with write() but no close() |
| r1-p005 | silent | Pathlib vs os.path Behavior on Paths | pathlib vs os.path behavior on relative paths, symlinks, missing files, bytes paths |
| r1-p006 | fix ok prediction off | Runtime Type Annotation Evaluation | type annotations evaluated at runtime, typing-only imports |
| r1-p007 | fix ok | Custom Truthiness and Identity Checks | truthiness of custom __bool__/__len__, None vs falsy, 'is' vs '==' |
| r1-p008 | silent prediction off | Walrus in Comprehension Late Binding | walrus, comprehensions scoping, late binding in closures and lambdas |
| r2-p001 | silent prediction off | Keyword-only and positional-only argument forwarding | keyword-only, positional-only, *args/**kwargs forwarding, defaults evaluated once |
| r2-p002 | silent prediction off | Walrus operator scope change | If the tuple contains a walrus operator (`:=`), removing parentheses may change the scope or binding behavior of the assigned variable. |
| r2-p003 | silent prediction off | Parenthesized expression vs tuple formation | Rewriting `(expr,)` as `expr,` changes a parenthesized expression into a tuple, altering meaning when `expr` is not a tuple-forming construct. |
| r2-p004 | fix ok | Line continuation grouping hazard | When the tuple is part of a larger expression due to line continuation (e.g., backslash), removing parentheses may break syntactic grouping. |
| r2-p005 | fix ok | Generator consumption difference | generators vs lists, iterators consumed once, lazy vs eager evaluation |
| r2-p006 | fix ok | Slice object interpretation in __getitem__ | If the subscript is part of a slice object construction via `__getitem__`, altering the tuple form may change how the slice is interpreted by the object’s implementation. |
| r2-p007 | silent prediction off | Context manager without close() | context managers that are not files, objects with write() but no close() |
| r2-p008 | silent prediction off | Comment preservation hazard | comments and docstrings attached to the removed or moved code |
Sources
r1-p001 · String Formatting Edge Case with Parentheses Removal
1x = [(1, 2)]
2print(str(x[(1, 2)]))
--- orig.py 2026-10-17 17:44:23.691356993 -0400 +++ fixed.py 2026-10-17 17:44:24.089354310 -0400 @@ -1,2 +1,2 @@ x = [(1, 2)] -print(str(x[(1, 2)])) +print(str(x[1, 2]))
r1-p002 · Comment Preservation Hazard
1# comment before
2x = [10, 20]
3# important comment here
4val = x[(1, # inline comment
5 2)] # trailing
6print(val)
--- orig.py 2026-10-17 17:44:24.316352781 -0400 +++ fixed.py 2026-10-17 17:44:24.693350240 -0400 @@ -1,6 +1,6 @@ # comment before x = [10, 20] # important comment here -val = x[(1, # inline comment - 2)] # trailing +val = x[1, # inline comment + 2] # trailing print(val)
r1-p003 · Walrus Operator Scope Change
1def f():
2 x = []
3 if (x := [(1, 2)])[0]:
4 pass
5 return x
6print(f())
[(1, 2)]
r1-p004 · Context Manager Without Close()
1class Writer:
2 def __enter__(self):
3 self.data = []
4 return self
5 def __exit__(self, *args):
6 pass
7 def write(self, v):
8 self.data.append(v)
9with Writer() as w:
10 w.write([(1, 2)] [(1, 2)])
11print(w.data[0][0])
--- orig.py 2026-10-17 17:44:25.030347970 -0400 +++ fixed.py 2026-10-17 17:44:25.412345397 -0400 @@ -7,5 +7,5 @@ def write(self, v): self.data.append(v) with Writer() as w: - w.write([(1, 2)] [(1, 2)]) + w.write([(1, 2)] [1, 2]) print(w.data[0][0])
r1-p005 · Pathlib vs os.path Behavior on Paths
1import os
2import tempfile
3with tempfile.TemporaryDirectory() as d:
4 p = os.path.join(d, 'test.txt')
5 open(p, 'w').write('hello')
6 print(os.path.exists(p))
7 from pathlib import Path
8 print(Path(p).exists())
True True
r1-p006 · Runtime Type Annotation Evaluation
1from typing import Tuple, List
2T: Tuple[int, int] = (1, 2)
3x = [T]
4print(type(x[(1, 2)]))
--- orig.py 2026-10-17 17:44:25.803342764 -0400 +++ fixed.py 2026-10-17 17:44:26.195340126 -0400 @@ -1,4 +1,4 @@ from typing import Tuple, List T: Tuple[int, int] = (1, 2) x = [T] -print(type(x[(1, 2)])) +print(type(x[1, 2]))
r1-p007 · Custom Truthiness and Identity Checks
1class CustomBool:
2 def __init__(self, val):
3 self.val = val
4 def __bool__(self):
5 return self.val > 0
6 def __len__(self):
7 return 1 if self.val else 0
8x = [CustomBool(1), CustomBool(0)]
9print(bool(x[(1, 0)]))
--- orig.py 2026-10-17 17:44:26.422338598 -0400 +++ fixed.py 2026-10-17 17:44:26.811335981 -0400 @@ -6,4 +6,4 @@ def __len__(self): return 1 if self.val else 0 x = [CustomBool(1), CustomBool(0)] -print(bool(x[(1, 0)])) +print(bool(x[1, 0]))
r1-p008 · Walrus in Comprehension Late Binding
1funcs = []
2for i in range(2):
3 if (t := [(i, i+1)])[0]:
4 funcs.append(lambda: t)
5print([f() for f in funcs])
[[(1, 2)], [(1, 2)]]
r2-p001 · Keyword-only and positional-only argument forwarding
1def f(a, /, b, *, c=0): return (a, b, c)
2val = [1, 2]
3d = {(f(*val))[():]}
4print('before:', list(d))
5def g(a, /, b, *, c=0): return (a, b, c)
6val = [1, 2]
7d = {g(*val)[()]}
8print('after:', list(d))
r2-p002 · Walrus operator scope change
1d = {}
2if True:
3 d[(walrus := 1)] = 'inner'
4print('walrus in outer?', 'walrus' in locals())
5d = {}
6if True:
7 d[walrus := 1] = 'inner'
8print('walrus in outer?', 'walrus' in locals())
walrus in outer? True walrus in outer? True
r2-p003 · Parenthesized expression vs tuple formation
1x = (42,)
2print('x is tuple:', isinstance(x, tuple), 'value:', x)
3x = 42,
4print('x is tuple:', isinstance(x, tuple), 'value:', x)
x is tuple: True value: (42,) x is tuple: True value: (42,)
r2-p004 · Line continuation grouping hazard
1import sys
2idx = (
3 1,
4 2
5)
6d = {}
7d[idx] = 'value'
8print('dict value:', d[(1,
9 2)])
10idx = (
11 1,
12 2
13)
14d = {}
15d[idx] = 'value'
16print('dict value:', d[1,
17 2])
--- orig.py 2026-10-17 17:44:34.635283484 -0400 +++ fixed.py 2026-10-17 17:44:34.800282380 -0400 @@ -5,8 +5,8 @@ ) d = {} d[idx] = 'value' -print('dict value:', d[(1, - 2)]) +print('dict value:', d[1, + 2]) idx = ( 1, 2
dict value: value dict value: value
r2-p005 · Generator consumption difference
1def gen():
2 yield 1
3 yield 2
4g = gen()
5d = {}
6d[(next(g), next(g))] = 'got'
7print('first access:', d[(1, 2)])
8g = gen()
9try:
10 print('second access:', d[(next(g), next(g))])
11except StopIteration as e:
12 print('StopIteration:', e)
13g = gen()
14d = {}
15d[(next(g), next(g))] = 'got'
16print('first access:', d[(next(g), next(g))])
17g = gen()
18try:
19 print('second access:', d[(next(g), next(g))])
20except StopIteration as e:
21 print('StopIteration:', e)
--- orig.py 2026-10-17 17:44:34.907281664 -0400 +++ fixed.py 2026-10-17 17:44:35.285279135 -0400 @@ -3,19 +3,19 @@ yield 2 g = gen() d = {} -d[(next(g), next(g))] = 'got' -print('first access:', d[(1, 2)]) +d[next(g), next(g)] = 'got' +print('first access:', d[1, 2]) g = gen() try: - print('second access:', d[(next(g), next(g))]) + print('second access:', d[next(g), next(g)]) except StopIteration as e: print('StopIteration:', e) g = gen() d = {} -d[(next(g), next(g))] = 'got' -print('first access:', d[(next(g), next(g))]) +d[next(g), next(g)] = 'got' +print('first access:', d[next(g), next(g)]) g = gen() try: - print('second access:', d[(next(g), next(g))]) + print('second access:', d[next(g), next(g)]) except StopIteration as e: print('StopIteration:', e)
first access: got second access: got
r2-p006 · Slice object interpretation in __getitem__
1class C:
2 def __getitem__(self, key):
3 if isinstance(key, tuple) and len(key) == 2:
4 return f'tuple-slice: {key}'
5 return f'other: {key}'
6c = C()
7print(c[(1, 2)])
8c = C()
9print(c[1, 2])
--- orig.py 2026-10-17 17:44:35.522277550 -0400 +++ fixed.py 2026-10-17 17:44:35.684276467 -0400 @@ -4,6 +4,6 @@ return f'tuple-slice: {key}' return f'other: {key}' c = C() -print(c[(1, 2)]) +print(c[1, 2]) c = C() print(c[1, 2])
tuple-slice: (1, 2) tuple-slice: (1, 2)
r2-p007 · Context manager without close()
1class Writer:
2 def __enter__(self):
3 self.data = []
4 return self
5 def __exit__(self, *exc):
6 pass
7 def write(self, s):
8 self.data.append(s)
9with Writer() as w:
10 w.write('hello')
11 idx = (0,)
12 lst = ['world']
13 lst[idx[0]] = w.data[0]
14print('lst:', lst)
15with Writer() as w:
16 w.write('hello')
17 idx = 0,
18 lst = ['world']
19 lst[idx[0]] = w.data[0]
20print('lst:', lst)
lst: ['hello'] lst: ['hello']
r2-p008 · Comment preservation hazard
1# comment before
2x = [10, 20]
3# important index
4i = [(0, 1)] # inline comment
5print(x[i[0][0]], x[i[0][1]])
6# comment before
7x = [10, 20]
8# important index
9i = [(0, 1)] # inline comment
10print(x[i[0][0]], x[i[0][1]])
10 20 10 20