ruff UP038
labelled unknown
Analysis Nemotron 3 Super
Flags isinstance/issubclass calls where the second argument is a tuple of two or more type expressions.
Fix: Replace the tuple of types in isinstance/issubclass with a PEP 604 union using the | operator, preserving parentheses only when needed for precedence.
8 hazards
- Rewriting isinstance(x, (int,)) to isinstance(x, int |) produces invalid syntax because the union operator requires both operands.
- If the tuple contains a forward reference as a string (e.g., 'MyClass'), the rewrite fails because strings cannot be used with the | operator in isinstance.
- When the tuple includes a generic alias like List[int], the rewrite to List[int] | ... may be invalid at runtime if Python version < 3.9 due to missing __or__ support.
- If the tuple contains a type variable that is not bound in the current scope, the rewritten union may resolve to a different type due to altered scoping.
- In a context where the tuple is built dynamically (e.g., isinstance(x, (*types,))), the static rewrite is impossible and attempting it would break the code.
- If the isinstance call appears in a lambda or comprehension, moving the union expression may alter evaluation timing if types have side effects (though rare).
- Comments attached to the tuple or its elements (e.g., isinstance(x, (int, # comment float))) may be lost during the rewrite.
- If the tuple is used elsewhere in the same line (e.g., in a larger expression), extracting it for rewriting may change precedence or association.
Findings
| program | oracle | label | judge | status | summary |
|---|---|---|---|---|---|
| r1-p001 | tool-crash | labelled unknown | program-bug · low | no match | The linter crashed because rule UP038 was removed from the codebase and cannot be selected, not because of any rewrite applied to the program. The program itse… |
| r1-p002 | tool-crash | labelled unknown | program-bug · low | related | The linter crashed because rule UP038 was removed from the codebase and cannot be selected, which is a tool availability issue unrelated to the program's synta… |
| r1-p003 | tool-crash | labelled unknown | program-bug · low | duplicate | The linter crashed because rule UP038 was removed from the codebase and cannot be selected, not because of any rewrite applied to the program. The program itse… |
| r1-p004 | tool-crash | labelled unknown | program-bug · low | related | The linter crashed because rule UP038 was removed from the codebase and cannot be selected, not because of any rewrite applied to the program. The program itse… |
| r1-p005 | tool-crash | labelled unknown | program-bug · low | duplicate | The linter crashed because rule UP038 was removed from the codebase and cannot be selected, not because of any semantic change to the program. The program itse… |
| r1-p006 | tool-crash | labelled unknown | program-bug · low | duplicate | The rule UP038 has been removed from ruff entirely, so the linter crashes when trying to apply it. The program itself is trivial (x = 5) and contains no isinst… |
| r1-p007 | tool-crash | labelled unknown | program-bug · low | duplicate | The linter crashed because rule UP038 was removed from the codebase and cannot be selected, not because of any rewrite applied to the program. The program itse… |
| r2-p001 | tool-crash | labelled unknown | program-bug · low | related | The linter crashed because rule UP038 was removed from the codebase and cannot be selected, so no fix was actually applied to the source code. The observed dif… |
| r2-p002 | tool-crash | labelled unknown | program-bug · low | no match | The linter crashed because rule UP038 was removed from the codebase and cannot be selected, so no fix was actually applied to the program. The observed differe… |
| r2-p003 | tool-crash | labelled unknown | program-bug · low | no match | The linter crashed because rule UP038 was removed from the codebase and cannot be selected, not because the rewrite changed program semantics. The oracle obser… |
| r2-p004 | tool-crash | labelled unknown | program-bug · low | related | The linter crashed because rule UP038 was removed from the codebase and cannot be selected, not because of any rewrite applied to the program. The program itse… |
| r2-p005 | tool-crash | labelled unknown | program-bug · low | related | The linter crashed because rule UP038 was removed from the codebase and cannot be selected, not because of any rewrite applied to the program. The program itse… |
| r2-p006 | tool-crash | labelled unknown | program-bug · low | related | The linter crashed because rule UP038 was removed from the codebase and cannot be selected, not because the rewrite changed program semantics. The oracle obser… |
| r2-p007 | tool-crash | labelled unknown | program-bug · low | duplicate | The linter crashed because rule UP038 was removed from the codebase and cannot be selected, not because of any rewrite applied to the program. The program itse… |
| r2-p008 | tool-crash | labelled unknown | program-bug · low | related | The linter crashed because rule UP038 was removed from the codebase and cannot be selected, not because the rewrite changed program semantics. The oracle obser… |
Programs
| id | result | title | hazard |
|---|---|---|---|
| r1-p001 | tool-crash | Truthiness and Custom Boolean Logic | Rewriting isinstance(x, (int, str)) to x is int | str breaks semantics when objects override __bool__ or __len__ to affect truthiness in ways unrelated to type. |
| r1-p002 | tool-crash | Lambda Evaluation Timing with Side-Effecting Types | Moving the union expression out of a lambda delays evaluation; if type construction has side effects (e.g., prints, state change), timing changes alter output. |
| r1-p003 | tool-crash | Forward Reference as String in Tuple | isinstance(x, ('MyClass', int)) contains a string forward reference; rewriting to 'MyClass' | int is invalid because strings cannot be used with | in isinstance. |
| r1-p004 | tool-crash | Bytes vs String in Mixed-Type Check | isinstance(x, (bytes, str)) distinguishes text and binary data; rewriting preserves this but must not be confused with encoding/decoding behavior. |
| r1-p005 | tool-crash | Numeric Coercion and Type Checks | isinstance(x, (int, float)) must distinguish actual int/float from bool (subclass of int) and Decimal; rewriting preserves type identity but not value semantics. |
| r1-p006 | tool-crash | Singleton Tuple Rewrite Produces Invalid Syntax | Rewriting isinstance(x, (int,)) to int | is invalid syntax; the | operator requires two operands, so the fix must not apply to 1-tuples. |
| r1-p007 | tool-crash | Runtime Evaluation of Type Annotations | If the tuple contains a name from a typing-only import (e.g., List), isinstance(List, ...) may fail at runtime if the import is not present or is TYPE_CHECKING-guarded. |
| r1-p008 | invalid | String Formatting and Representation in Type Checks | isinstance(x, (str,)) interacts with %s, .format(), f-strings, and repr; rewriting does not affect these, but the trace must show values to expose any coupling. |
| r2-p001 | tool-crash | Generic alias __or__ missing in Python < 3.9 | When the tuple includes a generic alias like List[int], the rewrite to List[int] | ... may be invalid at runtime if Python version < 3.9 due to missing __or__ support. |
| r2-p002 | tool-crash | Lambda/comprehension alters evaluation timing | If the isinstance call appears in a lambda or comprehension, moving the union expression may alter evaluation timing if types have side effects (though rare). |
| r2-p003 | tool-crash | Comments lost during rewrite | Comments attached to the tuple or its elements (e.g., isinstance(x, (int, # comment float))) may be lost during the rewrite. |
| r2-p004 | tool-crash | Context managers without close() | context managers that are not files, objects with write() but no close() |
| r2-p005 | tool-crash | Generators vs lists, iterators consumed once | generators vs lists, iterators consumed once, lazy vs eager evaluation |
| r2-p006 | tool-crash | Mutable default arguments and aliasing | mutable default arguments and aliasing after a rewrite |
| r2-p007 | tool-crash | Class vs instance attributes, descriptors, properties | class attributes vs instance attributes, descriptors, properties with side effects |
| r2-p008 | tool-crash | Walrus, comprehensions scoping, late binding | walrus, comprehensions scoping, late binding in closures and lambdas |
Sources
r1-p001 · Truthiness and Custom Boolean Logic
1class FalsyInt(int):
2 def __bool__(self): return False
3
4class TruthyStr(str):
5 def __bool__(self): return True
6
7x = FalsyInt(5)
8y = TruthyStr('')
9print(isinstance(x, (int, str)), isinstance(y, (int, str)))
True True
r1-p002 · Lambda Evaluation Timing with Side-Effecting Types
1counter = 0
2class T:
3 def __init__(self): nonlocal counter; counter += 1
4
5f = lambda x: isinstance(x, (T, int))
6print(f(T()), counter)
7counter = 0
8print([f(T()) for _ in range(2)], counter)
r1-p003 · Forward Reference as String in Tuple
1class MyClass: pass
2x = MyClass()
3try:
4 result = isinstance(x, ('MyClass', int))
5 print('Before:', result)
6except Exception as e:
7 print('Before error:', type(e).__name__, str(e))
Before error: TypeError isinstance() arg 2 must be a type, a tuple of types, or a union
r1-p004 · Bytes vs String in Mixed-Type Check
1x = b'hello'
2y = 'world'
3print(isinstance(x, (bytes, str)), isinstance(y, (bytes, str)))
4x = b'\xc3\xa9'
5y = 'é'
6print(isinstance(x, (bytes, str)), isinstance(y, (bytes, str)))
True True True True
r1-p005 · Numeric Coercion and Type Checks
1from decimal import Decimal
2x = 5
3y = 5.0
4z = True
5d = Decimal('5')
6for v in [x, y, z, d]:
7 print(isinstance(v, (int, float)), type(v).__name__)
True int True float True bool False Decimal
r1-p006 · Singleton Tuple Rewrite Produces Invalid Syntax
1x = 5
2print(isinstance(x, (int,)))
True
r1-p007 · Runtime Evaluation of Type Annotations
1from typing import List
2if False:
3 from typing import List as RuntimeList
4x = [1, 2]
5print(isinstance(x, (List, int)))
True
r1-p008 · String Formatting and Representation in Type Checks (generated program does not parse: Traceback (most recent call last):)
1x = 'Hello'
2print(isinstance(x, (str,)), '%s' % x, '{}
3'.format(x), f'{x!r}')
4x = 42
5print(isinstance(x, (str,)), '%s' % x, '{}
6'.format(x), f'{x!r}')
r2-p001 · Generic alias __or__ missing in Python < 3.9
1from typing import List
2x = [1]
3isinstance(x, (List[int],))
4print(isinstance(x, (List[int],)))
r2-p002 · Lambda/comprehension alters evaluation timing
1class T:
2 def __class_getitem__(cls, item):
3 print(f'T[{item}]')
4 return super().__class_getitem__(item)
5f = lambda: isinstance(1, (T[int],))
6print('before')
7f()
8print('after')
before T[<class 'int'>]
r2-p003 · Comments lost during rewrite
1x = 5
2isinstance(x, (int, # this is a comment
3float))
4print(isinstance(x, (int, float)))
True
r2-p004 · Context managers without close()
1class Writer:
2 def write(self, s): print(s)
3with Writer() as w:
4 w.write('hello')
r2-p005 · Generators vs lists, iterators consumed once
1def gen():
2 yield 1
3 yield 'a'
4g = gen()
5isinstance(next(g), (int, str))
6try:
7 isinstance(next(g), (int, str))
8except StopIteration as e:
9 print('StopIteration:', e)
r2-p006 · Mutable default arguments and aliasing
1def f(x, types=(int, str)):
2 print(isinstance(x, types))
3f(5)
4f('hello')
5f([1, 2])
True True False
r2-p007 · Class vs instance attributes, descriptors, properties
1class A:
2 attr = 1
3 @property
4 def prop(self):
5 return self._val
6 def __init__(self):
7 self._val = 2
8a = A()
9isinstance(a.attr, (int,))
10isinstance(a.prop, (int,))
r2-p008 · Walrus, comprehensions scoping, late binding
1fns = []
2for i in range(2):
3 if (x := i) == 0:
4 fns.append(lambda: isinstance(x, (int, str)))
5 else:
6 fns.append(lambda: isinstance(x, (int, str)))
7print(fns[0]())
8print(fns[1]())
True True