ruff B014
labelled unknown
Analysis Nemotron 3 Super
Flags except clauses containing duplicate or redundant exception types where one type subsumes another.
Fix: Removes redundant exception types from the except tuple, keeping only the most general (superclass) exception when a subtype is present, or deduplicates identical types, preserving the original order of non-redundant types.
12 hazards
- Rewriting `except (Exception, ValueError):` to `except Exception:` changes behavior if the handler relies on `ValueError` being matched first for conditional logic inside the handler, though such logic is unreachable anyway.
- If the except clause contains a comment after a redundant type like `except (Exception, # keep ValueError):`, the autofix may incorrectly remove or misplace the comment during tuple restructuring.
- When the except clause is part of a larger compound statement with line continuations, the rewrite may alter formatting in a way that breaks parsing if the tool does not preserve exact whitespace or parentheses structure.
- In code that uses stringized exceptions via `from __future__ import annotations`, the autofix might not correctly resolve forward references, leading to incorrect removal or retention of types.
- If the except tuple contains a variable name that shadows an exception class (e.g., `ValueError = 5; except (Exception, ValueError):`), the rewrite incorrectly treats it as a type and may produce invalid syntax or change meaning.
- The autofix may fail to recognize that a user-defined exception inherits from a built-in, leading to incomplete redundancy removal and leaving the code still flagged.
- In nested try statements, rewriting an outer except clause may affect the control flow of inner handlers if exceptions are re-raised, though this is unlikely due to redundancy.
- If the except clause is followed by a `finally` block, the rewrite does not affect finally execution, but removing redundant types could be misconstrued as altering exception propagation timing—though it does not.
- When the except clause uses a bare `except:` (no arguments), the rule does not fire, but if mistakenly applied, the fix would break syntax by introducing an empty tuple.
- If the code depends on the exact tuple object for introspection (e.g., storing the except clause via `inspect`), the rewritten tuple may differ in identity or content, affecting metaprogramming.
- Autofix may leave a trailing comma in a singleton tuple (e.g., `except (Exception,):`) which is valid but may trigger style warnings in strict codebases.
- In code processed by tools that rely on the AST node's exact source representation (e.g., certain refactoring tools), the rewritten source may break source-to-AST roundtripping.
Programs
| id | result | title | hazard |
|---|---|---|---|
| r1-p001 | silent prediction off | String Formatting Edge Cases - % Operator | string formatting edge cases: %, format, f-strings, repr vs str, nested quotes |
| r1-p002 | silent prediction off | Generators vs Lists - Iterator Consumption | generators vs lists, iterators consumed once, lazy vs eager evaluation |
| r1-p003 | silent prediction off | Trailing Comma in Singleton Tuple | Autofix may leave a trailing comma in a singleton tuple (e.g., `except (Exception,):`) which is valid but may trigger style warnings in strict codebases. |
| r1-p004 | silent prediction off | Finally Block Interaction | If the except clause is followed by a `finally` block, the rewrite does not affect finally execution, but removing redundant types could be misconstrued as altering exception propagation timing—though |
| r1-p005 | silent prediction off | Handler Conditional Logic Dependency | Rewriting `except (Exception, ValueError):` to `except Exception:` changes behavior if the handler relies on `ValueError` being matched first for conditional logic inside the handler, though such logi |
| r1-p006 | silent prediction off | Comments Attached to Removed Code | comments and docstrings attached to the removed or moved code |
| r1-p007 | silent | Runtime Type Annotations Evaluation | type annotations evaluated at runtime, typing-only imports |
| r1-p008 | silent prediction off | Truthiness and Identity Checks | truthiness of custom __bool__/__len__, None vs falsy, 'is' vs '==' |
| r2-p001 | silent prediction off | Comment Preservation During Redundant Except Rewrite | Comments attached to redundant exception types may be lost or misplaced during autofix. |
| r2-p002 | silent prediction off | AST Roundtrip Integrity After Except Rewrite | Rewritten except tuple may break source-to-AST roundtripping in tools relying on exact source form. |
| r2-p003 | silent prediction off | Variable Shadowing Exception Class in Except Tuple | When a variable shadows an exception class (e.g., ValueError=5), the linter may mistakenly treat it as a type and rewrite incorrectly. |
| r2-p004 | silent prediction off | Except Rewrite Followed by Finally Block | Autofix of except clause does not alter finally execution, but may be misconstrued as changing propagation timing. |
| r2-p005 | silent | Context Manager Without Close() Method | Using non-file context managers that implement write() but not close() — unrelated to except rewrite but exercises resource handling patterns. |
| r2-p006 | silent prediction off | Except Tuple Used for Introspection via inspect | Storing the except clause tuple for introspection (e.g., via inspect) may break if autofix changes tuple identity or content. |
| r2-p007 | silent | Bytes vs Str Encoding in Except-Related I/O | Mixing bytes and str in I/O operations within except handlers — encoding/newline hazards unrelated to rewrite but sensitive to type changes. |
| r2-p008 | silent prediction off | Async Generator and Await Ordering in Except Handler | Interaction between async def/await ordering and except clause rewrite — ensuring await happens correctly after potential fix. |
Sources
r1-p001 · String Formatting Edge Cases - % Operator
1try:
2 x = '%s' % [1, 2]
3except (TypeError, ValueError):
4 print('caught:', type(e).__name__)
r1-p002 · Generators vs Lists - Iterator Consumption
1def gen():
2 yield 1
3 yield 2
4g = gen()
5try:
6 next(g)
7 next(g)
8 next(g)
9except (StopIteration, Exception):
10 print('caught:', type(e).__name__)
r1-p003 · Trailing Comma in Singleton Tuple
1try:
2 int('a')
3except (ValueError,):
4 print('value error caught')
value error caught
r1-p004 · Finally Block Interaction
1try:
2 1/0
3except (ZeroDivisionError, ArithmeticError):
4 print('except')
5finally:
6 print('finally')
except finally
r1-p005 · Handler Conditional Logic Dependency
1try:
2 int('x')
3except (Exception, ValueError) as e:
4 if isinstance(e, ValueError):
5 print('value error path')
6 else:
7 print('other error:', type(e).__name__)
value error path
r1-p006 · Comments Attached to Removed Code
1try:
2 [] + {}
3except (TypeError, Exception): # TypeError is more specific
4 print('type error')
type error
r1-p007 · Runtime Type Annotations Evaluation
1from typing import List
2try:
3 [1, 2] + 'a'
4except (TypeError,):
5 print('type error')
type error
r1-p008 · Truthiness and Identity Checks
1class Fake:
2 def __bool__(self): return False
3try:
4 bool(Fake())
5except (Exception, ValueError):
6 print('no exception raised')
7except:
8 print('unexpected')
r2-p001 · Comment Preservation During Redundant Except Rewrite
1# Test comment preservation
2try:
3 raise ValueError
4except (Exception, # keep this comment
5 ValueError): # this should stay
6 print('Caught:', type(__import__('sys').exc_info()[1]).__name__)
7else:
8 print('No exception')
9finally:
10 print('Finally ran')
Caught: ValueError Finally ran
r2-p002 · AST Roundtrip Integrity After Except Rewrite
1import ast
2src = '''\
3try:
4 1/0
5except (ZeroDivisionError, Exception):\
6 pass'''
7tree = ast.parse(src)
8fixed = ast.unparse(tree) # May not preserve original tuple form
9print('Original:', repr(src))
10print('Unparsed:', repr(fixed))
11try:
12 exec(fixed)
13except Exception as e:
14 print('Error in fixed code:', type(e).__name__)
Original: 'try:\n 1/0\nexcept (ZeroDivisionError, Exception): pass' Unparsed: 'try:\n 1 / 0\nexcept (ZeroDivisionError, Exception):\n pass'
r2-p003 · Variable Shadowing Exception Class in Except Tuple
1ValueError = 42 # shadows built-in
2try:
3 raise ValueError # raises the variable (int), not exception
4except (Exception, ValueError): # linter sees two 'types', may dedupe
5 print('Caught:', type(__import__('sys').exc_info()[1]))
6 print('Value is:', ValueError)
7except TypeError:
8 print('Got TypeError as expected')
r2-p004 · Except Rewrite Followed by Finally Block
1def test():
2 try:
3 raise OSError('disk error')
4 except (OSError, IOError): # IOError is subclass of OSError in Python 3
5 print('Handler: OSError caught')
6 return 'from_except'
7 finally:
8 print('Finally: always runs')
9 return 'from_finally'
10result = test()
11print('Result:', result)
Handler: OSError caught Finally: always runs Result: from_finally
r2-p005 · Context Manager Without Close() Method
1class WriteOnly:
2 def __enter__(self):
3 self.data = []
4 return self
5 def write(self, x):
6 self.data.append(x)
7 def __exit__(self, *args):
8 pass
9
10with WriteOnly() as w:
11 w.write('hello')
12 w.write('world')
13print('Written:', ''.join(w.data))
Written: helloworld
r2-p006 · Except Tuple Used for Introspection via inspect
1import inspect
2
3def func():
4 try:
5 raise ValueError
6 except (ValueError, TypeError): # not redundant, so rule should not fire
7 pass
8
9handler_tuple = None
10for name, cell in inspect.getclosurevars(func).nonlocals.items():
11 if isinstance(cell, tuple) and all(isinstance(t, type) for t in cell):
12 handler_tuple = cell
13 break
14
15print('Handler tuple:', handler_tuple)
16print('Length:', len(handler_tuple) if handler_tuple else 0)
17print('Types:', [t.__name__ for t in handler_tuple] if handler_tuple else [])
Handler tuple: None Length: 0 Types: []
r2-p007 · Bytes vs Str Encoding in Except-Related I/O
1try:
2 with open('test.bin', 'wb') as f:
3 f.write(b'hello\n')
4 with open('test.bin', 'rb') as f:
5 data = f.read()
6 print('Raw bytes:', repr(data))
7 try:
8 text = data.decode('utf-8')
9 print('Decoded:', repr(text))
10 except UnicodeDecodeError as ude:
11 print('Decode failed:', ude)
12except (OSError, IOError): # redundant: IOError is alias for OSError
13 print('File error caught')
14finally:
15 import os
16 try: os.remove('test.bin')
17 except: pass
Raw bytes: b'hello\n' Decoded: 'hello\n'
r2-p008 · Async Generator and Await Ordering in Except Handler
1async def maybe_fail():
2 await asyncio.sleep(0) # yield control
3 raise ValueError('async error')
4
5async def main():
6 try:
7 await maybe_fail()
8 except (ValueError, Exception): # ValueError subsumed by Exception
9 print('Caught ValueError')
10 return 42
11 finally:
12 print('Finally in async')
13
14import asyncio
15result = asyncio.run(main())
16print('Result:', result)
Caught ValueError Finally in async Result: 42