fixhunt

ruff TRY201

labelled unknown
Runs / 2026-10-17T21-52-48-892Z-gia8-ruff-TRY201ruff 0.17.12026-10-17 21:52 UTC8 programs × 2 round(s)16 programs, 14 fired, 0 fixed, 0 findings, 0 rejectedmodel cost $0.0042

Analysis Nemotron 3 Super

Flags unnecessary repetition of caught exception variable when immediately re-raising the same exception.

Fix: Removes the exception variable name from both the except clause and the raise statement, changing 'except X as var: raise var' to 'except X: raise'.

10 hazards
  • The exception variable is used after the raise in the same block, so removing it causes an undefined variable error.
  • The exception variable is reassigned between the except and raise, so the fix alters which object is raised.
  • The exception variable is referenced in a nested function defined between except and raise, capturing the original binding which the fix removes.
  • The except clause contains multiple exception types (e.g., 'except (ValueError, TypeError) as exc') and the fix incorrectly drops the variable.
  • A comment attached to the 'as exc' token is lost during the rewrite.
  • The raise statement is inside a finally block where the variable is referenced from an outer scope, and the fix breaks that reference.
  • The exception variable name shadows a global or nonlocal variable, and removing the binding changes later accesses to that variable.
  • The except block is empty except for the raise, and removing the variable leaves a pointless 'except:' that may confuse readers but is syntactically valid.
  • The code relies on the exception variable being deleted after the block (via implicit cleanup), and altering the binding scope changes deletion timing.
  • The raise statement is modified by a decorator or metaclass that inspects the source, and the rewrite changes its behavior.

Programs

idresulttitlehazard
r1-p001fired, no fixMultiple exception types - variable droppedWhen catching multiple exception types, dropping the variable loses access to the actual exception instance, which may carry different state depending on which type was raised.
r1-p002fired, no fixInstance attribute with descriptor side effectIf the exception variable refers to an instance attribute accessed via a descriptor (__get__ has side effects), removing the variable prevents those side effects from occurring during the raise.
r1-p003fired, no fixEvaluation order in raised expressionIf the exception variable is used in a subexpression with side effects (e.g., function call), removing it changes evaluation order or eliminates the side effect entirely.
r1-p004fired, no fix prediction offPointless bare except after rewriteRemoving the variable from a single-except clause results in 'except:' which catches BaseException (including KeyboardInterrupt, SystemExit), changing exception semantics.
r1-p005fired, no fixDecorator inspecting raise statement sourceA decorator that parses the source code of the function to detect 'raise <var>' patterns will behave differently if the variable is removed, altering its logic.
r1-p006silentGenerator state lost by early raiseIf the exception variable is used to conditionally resume a generator, removing it breaks the ability to inspect the exception and decide whether to yield or terminate.
r1-p007fired, no fixCustom __bool__ affects control flowIf the exception variable is used in a boolean context (e.g., conditional raise), removing it may prevent inspection of a custom __bool__ that affects whether the raise happens.
r1-p008fired, no fixException in dict key with custom __hash__If the exception instance is used as a dict key (relying on __hash__/__eq__), removing the variable prevents it from being used in lookups or storage that depend on its identity or value.
r2-p001fired, no fixMutable default argument retains raised exception across callsmutable default arguments and aliasing after a rewrite
r2-p002fired, no fixGenerator expression consumes iterator before raisegenerators vs lists, iterators consumed once, lazy vs eager evaluation
r2-p003fired, no fixProperty getter accesses exception variable after raiseclass attributes vs instance attributes, descriptors, properties with side effects
r2-p004fired, no fixMulti-type except drops variable needed after raiseThe except clause contains multiple exception types (e.g., 'except (ValueError, TypeError) as exc') and the fix incorrectly drops the variable.
r2-p005fired, no fixException variable used after raise in same blockThe exception variable is used after the raise in the same block, so removing it causes an undefined variable error.
r2-p006silent prediction offFinally block references outer-scope exception variableThe raise statement is inside a finally block where the variable is referenced from an outer scope, and the fix breaks that reference.
r2-p007fired, no fixKeyword-only args and exception variable capturekeyword-only, positional-only, *args/**kwargs forwarding, defaults evaluated once
r2-p008fired, no fixDict ordering affects exception tracedict ordering, duplicate keys, __hash__/__eq__ overrides, sets

Sources

r1-p001 · Multiple exception types - variable dropped
1class MultiExc(Exception):
2 def __init__(self, typ, val):
3 self.typ = typ
4 self.val = val
5 super().__init__(f'{typ}:{val}')
6try:
7 raise MultiExc('TypeError', 42)
8except (ValueError, TypeError) as exc:
9 print(f'Caught {exc.typ}:{exc.val}')
10 raise exc
r1-p002 · Instance attribute with descriptor side effect
1class SideEffectDesc:
2 def __get__(self, obj, objtype):
3 print('DESC_GET')
4 return 42
5class ExcWithDesc(Exception):
6 attr = SideEffectDesc()
7 def __init__(self):
8 super().__init__('desc')
9try:
10 raise ExcWithDesc()
11except Exception as e:
12 print(e.attr) # Triggers DESC_GET
13 raise e
DESC_GET
42
r1-p003 · Evaluation order in raised expression
1state = 0
2def side_effect():
3 global state
4 state += 1
5 return state
6class Exc(Exception):
7 def __init__(self):
8 self.val = side_effect()
9try:
10 raise Exc()
11except Exception as e:
12 print(f'First: {e.val}, Second: {side_effect()}')
13 raise e # Uses e.val after first side_effect()
First: 1, Second: 2
r1-p004 · Pointless bare except after rewrite
1try:
2 raise KeyboardInterrupt('interrupted')
3except Exception as exc: # Would become 'except:' after fix
4 print(f'Caught {type(exc).__name__}')
5 raise exc
r1-p005 · Decorator inspecting raise statement source
1import inspect
2def trace_raise(func):
3 src = inspect.getsource(func)
4 def wrapper():
5 if 'raise exc' in src:
6 print('Trace: raise exc detected')
7 else:
8 print('Trace: raise exc NOT detected')
9 return func()
10 return wrapper
11@trace_raise
12def func():
13 try:
14 raise ValueError('test')
15 except ValueError as exc:
16 print('In handler')
17 raise exc
18func()
Trace: raise exc detected
In handler
r1-p006 · Generator state lost by early raise
1def gen():
2 try:
3 yield 1
4 except ValueError as v:
5 if v.args[0] == 'resume':
6 yield 2
7 else:
8 raise # re-raises only if not 'resume'
9 yield 3
10g = gen()
11print(next(g)) # 1
12g.throw(ValueError, 'resume') # Should yield 2
13print(next(g)) # 2
14g.throw(ValueError, 'stop') # Should raise
1
3
r1-p007 · Custom __bool__ affects control flow
1class ConditionalExc(Exception):
2 def __init__(self, flag):
3 self.flag = flag
4 super().__init__()
5 def __bool__(self):
6 return self.flag
7try:
8 raise ConditionalExc(False)
9except ConditionalExc as exc:
10 if exc: # False -> skip raise
11 raise exc
12 print('No raise due to __bool__ False')
No raise due to __bool__ False
r1-p008 · Exception in dict key with custom __hash__
1class HashExc(Exception):
2 def __init__(self, val):
3 self.val = val
4 super().__init__(str(val))
5 def __hash__(self):
6 return hash(self.val)
7 def __eq__(self, other):
8 return isinstance(other, HashExc) and self.val == other.val
9try:
10 exc = HashExc(42)
11 raise exc
12except HashExc as e:
13 d = {e: 'found'}
14 print(d.get(e, 'missing'))
15 raise e
found
r2-p001 · Mutable default argument retains raised exception across calls
1def f(x=[]):
2 try:
3 x.append(1)
4 raise ValueError('bad')
5 except ValueError as exc:
6 raise exc
7print(f())
8print(f())
r2-p002 · Generator expression consumes iterator before raise
1def gen():
2 yield 1
3 yield 2
4g = gen()
5try:
6 next(g)
7 raise RuntimeError('fail')
8except RuntimeError as err:
9 raise err
10print(list(gen()))
r2-p003 · Property getter accesses exception variable after raise
1class C:
2 @property
3 def val(self):
4 return self._v
5 def __init__(self):
6 try:
7 self._v = 5
8 raise ValueError('oops')
9 except ValueError as exc:
10 self._v = exc
11 raise exc
12c = C()
13try:
14 _ = c.val
15except ValueError as e:
16 print('caught:', e)
r2-p004 · Multi-type except drops variable needed after raise
1try:
2 raise ValueError('x')
3except (ValueError, TypeError) as exc:
4 print('exc type:', type(exc).__name__)
5 raise exc
6print('after')
exc type: ValueError
r2-p005 · Exception variable used after raise in same block
1try:
2 raise RuntimeError('fail')
3except RuntimeError as err:
4 raise err
5 print(err)
r2-p006 · Finally block references outer-scope exception variable
1def outer():
2 exc = None
3 try:
4 try:
5 raise ValueError('test')
6 except ValueError as e:
7 exc = e
8 raise
9 finally:
10 if exc is not None:
11 print('finally saw:', exc)
12outer()
finally saw: test
r2-p007 · Keyword-only args and exception variable capture
1def handler(*, ex=None):
2 try:
3 raise KeyError('missing')
4 except KeyError as k:
5 if ex is None:
6 ex = k
7 raise k
8 return ex
9print(handler())
r2-p008 · Dict ordering affects exception trace
1class BadKey:
2 def __init__(self, v):
3 self.v = v
4 def __hash__(self):
5 return hash(self.v)
6 def __eq__(self, other):
7 return self.v == other.v
8try:
9 d = {BadKey(1): 'a', BadKey(2): 'b'}
10 raise ValueError('collision')
11except ValueError as v:
12 d[BadKey(1)] = v
13 raise v
14print(d)