fixhunt

ruff UP038

labelled unknown
Runs / 2026-10-17T21-20-52-142Z-httm-ruff-UP038ruff 0.17.12026-10-17 21:20 UTC8 programs × 2 round(s)16 programs, 0 fired, 0 fixed, 15 findings, 1 rejectedmodel cost $0.17

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

programoraclelabeljudgestatussummary
r1-p001tool-crashlabelled unknownprogram-bug · lowno matchThe 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-p002tool-crashlabelled unknownprogram-bug · lowrelatedThe 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-p003tool-crashlabelled unknownprogram-bug · lowduplicateThe 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-p004tool-crashlabelled unknownprogram-bug · lowrelatedThe 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-p005tool-crashlabelled unknownprogram-bug · lowduplicateThe 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-p006tool-crashlabelled unknownprogram-bug · lowduplicateThe 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-p007tool-crashlabelled unknownprogram-bug · lowduplicateThe 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-p001tool-crashlabelled unknownprogram-bug · lowrelatedThe 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-p002tool-crashlabelled unknownprogram-bug · lowno matchThe 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-p003tool-crashlabelled unknownprogram-bug · lowno matchThe 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-p004tool-crashlabelled unknownprogram-bug · lowrelatedThe 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-p005tool-crashlabelled unknownprogram-bug · lowrelatedThe 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-p006tool-crashlabelled unknownprogram-bug · lowrelatedThe 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-p007tool-crashlabelled unknownprogram-bug · lowduplicateThe 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-p008tool-crashlabelled unknownprogram-bug · lowrelatedThe 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

idresulttitlehazard
r1-p001tool-crashTruthiness and Custom Boolean LogicRewriting 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-p002tool-crashLambda Evaluation Timing with Side-Effecting TypesMoving 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-p003tool-crashForward Reference as String in Tupleisinstance(x, ('MyClass', int)) contains a string forward reference; rewriting to 'MyClass' | int is invalid because strings cannot be used with | in isinstance.
r1-p004tool-crashBytes vs String in Mixed-Type Checkisinstance(x, (bytes, str)) distinguishes text and binary data; rewriting preserves this but must not be confused with encoding/decoding behavior.
r1-p005tool-crashNumeric Coercion and Type Checksisinstance(x, (int, float)) must distinguish actual int/float from bool (subclass of int) and Decimal; rewriting preserves type identity but not value semantics.
r1-p006tool-crashSingleton Tuple Rewrite Produces Invalid SyntaxRewriting isinstance(x, (int,)) to int | is invalid syntax; the | operator requires two operands, so the fix must not apply to 1-tuples.
r1-p007tool-crashRuntime Evaluation of Type AnnotationsIf 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-p008invalidString Formatting and Representation in Type Checksisinstance(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-p001tool-crashGeneric alias __or__ missing in Python < 3.9When 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-p002tool-crashLambda/comprehension alters evaluation timingIf 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-p003tool-crashComments lost during rewriteComments attached to the tuple or its elements (e.g., isinstance(x, (int, # comment float))) may be lost during the rewrite.
r2-p004tool-crashContext managers without close()context managers that are not files, objects with write() but no close()
r2-p005tool-crashGenerators vs lists, iterators consumed oncegenerators vs lists, iterators consumed once, lazy vs eager evaluation
r2-p006tool-crashMutable default arguments and aliasingmutable default arguments and aliasing after a rewrite
r2-p007tool-crashClass vs instance attributes, descriptors, propertiesclass attributes vs instance attributes, descriptors, properties with side effects
r2-p008tool-crashWalrus, comprehensions scoping, late bindingwalrus, 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