fixhunt

ruff RSE102

labelled unknown
Runs / 2026-10-17T20-29-39-255Z-1pck-ruff-RSE102ruff 0.17.12026-10-17 20:29 UTC8 programs × 2 round(s)8 programs, 5 fired, 0 fixed, 0 findings, 0 rejectedmodel cost $0.0059

Analysis Nemotron 3 Super

Flags raise statements where parentheses around a single exception argument are syntactically unnecessary.

Fix: Removes the opening and closing parentheses that directly surround the exception expression in a raise statement, leaving the inner expression unchanged.

10 hazards
  • Rewriting `raise f()` to `raise f` changes a function call to a name reference, altering runtime behavior if `f` is a function rather than a class.
  • If the parentheses contain a tuple like `raise (ValueError,)`, removing them yields `raise ValueError,` which is a syntax error due to the trailing comma.
  • When the exception expression is a subscript such as `raise Errors[0]()`, stripping parentheses results in `raise Errors[0]` which may evaluate to a non-callable, breaking implicit instantiation.
  • If the parentheses enclose a named tuple or structured expression like `raise (err := ValueError)()`, the rewrite loses the walrus assignment side-effect.
  • In contexts where the parentheses are required for line continuation or visual grouping (e.g., multi-line expressions), removing them can break parsing or alter interpretation.
  • If the parentheses contain a comment-attached expression like `raise ValueError() # inline`, the fix may drop the comment if it’s attached to the parentheses.
  • When the raised expression is a call to a property that returns a class, e.g., `raise ExcFactory.error`, the parentheses are unnecessary but the rule may not fire; however, if it did fire on a similar pattern, removing them could change binding timing.
  • If the exception is a class defined locally within a function and the parentheses are part of a larger expression (e.g., `raise (LocalError())()`), the rewrite misfires and breaks nesting.
  • In code relying on the call to trigger `__call__` logic in a metaclass or descriptor, removing parentheses prevents that invocation.
  • If the parentheses are the only thing preventing a syntax error due to ambiguous precedence (e.g., `raise a or b()` vs `raise a or b`), their removal can change meaning.

Programs

idresulttitlehazard
r2-p001silent prediction offTrailing Comma Syntax ErrorIf the parentheses contain a tuple like `raise (ValueError,)`, removing them yields `raise ValueError,` which is a syntax error due to the trailing comma.
r2-p002silent prediction offTruthy/Falsy Confusiontruthiness of custom __bool__/__len__, None vs falsy, 'is' vs '=='
r2-p003fired, no fixWalrus Assignment LostIf the parentheses enclose a named tuple or structured expression like `raise (err := ValueError)()`, the rewrite loses the walrus assignment side-effect.
r2-p004fired, no fixFile Truncation Side Effectfile modes and truncation, partial writes, non-str data passed to write
r2-p005fired, no fixEvaluation Order and Side Effectsevaluation order and side effects when expressions are merged, duplicated or deleted
r2-p006silent prediction offWalrus in Comprehension Scopewalrus, comprehensions scoping, late binding in closures and lambdas
r2-p007fired, no fixNon-File Context Manager with Writecontext managers that are not files, objects with write() but no close()
r2-p008fired, no fixAsync Generator Await Orderingasync def/await ordering, async generators

Sources

r2-p001 · Trailing Comma Syntax Error
1import sys
2try:
3 exc = (ValueError,)
4 raise exc
5except SyntaxError as e:
6 print('SyntaxError:', e.msg)
7except ValueError as e:
8 print('Caught:', type(e).__name__)
r2-p002 · Truthy/Falsy Confusion
1class Err:
2 def __init__(self, v): self.v = v
3 def __bool__(self): return self.v is not None
4 def __len__(self): return 0 if self.v else 1
5
6e = Err(None)
7print('Before raise:', bool(e), len(e))
8try:
9 raise (e)()
10except Err as caught:
11 print('After catch:', bool(caught), len(caught))
Before raise: False 1
r2-p003 · Walrus Assignment Lost
1assigned = None
2try:
3 raise ((err := ValueError)())()
4except ValueError as e:
5 assigned = err
6print('Assigned:', assigned is not None)
7print('err is', err if assigned else 'None')
8try:
9 raise (err := ValueError)()
10except ValueError as e:
11 print('Second err:', err)
r2-p004 · File Truncation Side Effect
1with open('test.txt', 'w') as f:
2 f.write('original')
3try:
4 with open('test.txt', 'r+') as f:
5 raise (ValueError('fail'))()
6except ValueError:
7 pass
8with open('test.txt', 'r') as f:
9 print(repr(f.read()))
10import os
11os.remove('test.txt')
r2-p005 · Evaluation Order and Side Effects
1state = []
2def side(x):
3 state.append(x)
4 return ValueError(x)
5try:
6 raise (side(1))()
7except ValueError as e:
8 print('Side effects:', state)
9 print('Exception arg:', e.args[0])
10state.clear()
11try:
12 raise side(2)()
13except ValueError as e:
14 print('Side effects after:', state)
15 print('Exception arg:', e.args[0])
r2-p006 · Walrus in Comprehension Scope
1fns = []
2for i in range(2):
3 if i == 0:
4 fns.append(lambda: (err := ValueError(i)))
5 else:
6 fns.append(lambda: err)
7for f in fns:
8 try:
9 raise f()
10 except ValueError as e:
11 print('Caught:', e.args[0])
12print('Final err:', err if 'err' in locals() else 'undefined')
Caught: 1
r2-p007 · Non-File Context Manager with Write
1class Logger:
2 def __init__(self): self.buf = []
3 def write(self, s): self.buf.append(s)
4 def __enter__(self): return self
5 def __exit__(self, *exc): pass
6
7log = Logger()
8with log as l:
9 try:
10 l.write('before')
11 raise (RuntimeError('bad'))()
12 except RuntimeError:
13 l.write('after')
14print('Logged:', ''.join(log.buf))
r2-p008 · Async Generator Await Ordering
1async def gen():
2 yield 1
3 raise (ValueError('async'))()
4
5async def main():
6 try:
7 async for x in gen():
8 print('Yielded:', x)
9 except ValueError as e:
10 print('Caught async:', e.args[0])
11
12import asyncio
13asyncio.run(main())
Yielded: 1