fixhunt

ruff UP040

labelled unknown
Runs / 2026-10-17T21-25-27-945Z-py4r-ruff-UP040ruff 0.17.12026-10-17 21:25 UTC8 programs × 2 round(s)16 programs, 0 fired, 0 fixed, 0 findings, 3 rejectedmodel cost $0.0065

Analysis Nemotron 3 Super

Flags usage of typing.TypeAlias annotation and typing.TypeAliasType assignment for declaring type aliases.

Fix: Replaces 'Name: TypeAlias = expr' with 'type Name = expr' and 'Name = TypeAliasType("Name", expr[, type_params=params])' with 'type Name = expr', stripping the type_params argument and importing Name if needed.

10 hazards
  • Rewriting TypeAlias changes the declaration from a variable annotation to a type statement, which cannot be used as a runtime value (e.g., in isinstance() or issubclass()).
  • Rewriting TypeAliasType loses explicit variance (covariant/contravariant) from TypeVar definitions, replacing it with inferred variance which may differ.
  • The fix cannot resolve TypeVar definitions from other modules, so bounds, constraints, defaults, or kind are lost, producing unconstrained TypeVars in the generated type parameter list.
  • If the TypeAliasType's first argument (the name string) does not match the variable name, the resulting type statement may bind to a different name, breaking references.
  • Comments attached to the original TypeAlias or TypeAliasType line may be dropped during the rewrite.
  • If the aliased expression contains side effects (e.g., function calls), moving it into a type statement changes evaluation timing from runtime to definition time, potentially altering behavior.
  • In nested scopes (e.g., inside functions or classes), the rewritten type statement may not be accessible where the original alias was, due to differing scoping rules for type statements.
  • If the module defines a __getattr__ or __dir__ that relies on the TypeAlias or TypeAliasType being present as a runtime attribute, the rewrite will remove it, causing AttributeError.
  • The fix may produce invalid syntax if the target version is pre-3.12, as 'type' statements are not supported.
  • When multiple type aliases are defined in a single line via tuple unpacking, the fix may incorrectly split or fail to parse the construct.

Programs

idresulttitlehazard
r1-p001silent prediction offVariance loss in TypeAliasType -> typeRewriting TypeAliasType loses explicit variance (covariant/contravariant) from TypeVar definitions, replacing it with inferred variance which may differ.
r1-p002silent prediction offSide effect timing in alias expressionIf the aliased expression contains side effects (e.g., function calls), moving it into a type statement changes evaluation timing from runtime to definition time, potentially altering behavior.
r1-p003silent prediction offMismatched name in TypeAliasType first argumentIf the TypeAliasType's first argument (the name string) does not match the variable name, the resulting type statement may bind to a different name, breaking references.
r1-p004silent prediction offClass attribute descriptor interactionclass attributes vs instance attributes, descriptors, properties with side effects
r1-p005invalidEvaluation order with side effects in compound expressionevaluation order and side effects when expressions are merged, duplicated or deleted
r1-p006silent prediction offShadowed builtin later redefinedshadowed builtins and names redefined later in the module
r1-p007silent prediction offString formatting in alias expressionstring formatting edge cases: %, format, f-strings, repr vs str, nested quotes
r1-p008invalidPathlib vs os.path behaviorpathlib vs os.path behavior on relative paths, symlinks, missing files, bytes paths
r2-p001silent prediction offClass attribute vs instance attribute, descriptor interactionclass attributes vs instance attributes, descriptors, properties with side effects
r2-p002silent prediction offTarget version pre-3.12: 'type' statement invalid syntaxThe fix may produce invalid syntax if the target version is pre-3.12, as 'type' statements are not supported.
r2-p003silent prediction offNested scope accessibility: type statement not visible where alias wasIn nested scopes (e.g., inside functions or classes), the rewritten type statement may not be accessible where the original alias was, due to differing scoping rules for type statements.
r2-p004silent prediction off__getattr__ / __dir__ relies on TypeAlias as runtime attributeIf the module defines a __getattr__ or __dir__ that relies on the TypeAlias or TypeAliasType being present as a runtime attribute, the rewrite will remove it, causing AttributeError.
r2-p005silent prediction offTypeAlias cannot be used in isinstance() after rewrite to type statementRewriting TypeAlias changes the declaration from a variable annotation to a type statement, which cannot be used as a runtime value (e.g., in isinstance() or issubclass()).
r2-p006invalidKeyword-only, positional-only, *args/**kwargs forwarding, defaults evaluated oncekeyword-only, positional-only, *args/**kwargs forwarding, defaults evaluated once
r2-p007silent prediction offType annotations evaluated at runtime, typing-only importstype annotations evaluated at runtime, typing-only imports
r2-p008silent prediction offDict ordering, duplicate keys, __hash__/__eq__ overrides, setsdict ordering, duplicate keys, __hash__/__eq__ overrides, sets

Sources

r1-p001 · Variance loss in TypeAliasType -> type
1from typing import TypeVar, TypeAliasType
2T_co = TypeVar('T_co', covariant=True)
3F = TypeAliasType('F', list[T_co])
4def use(f):
5 return isinstance(f([1]), F)
6print(use(lambda x: x))
7# After fix: `type F = list[T_co]` infers invariance, breaking covariance check
r1-p002 · Side effect timing in alias expression
1counter = 0
2def inc():
3 global counter
4 counter += 1
5 return str(counter)
6A = TypeAliasType('A', inc())
7print('First:', counter)
8try:
9 x: A
10 print('After annotation:', counter)
11except:
12 pass
13print('Final:', counter)
14# After fix: `type A = inc()` runs at module load, counter=1 before any use
r1-p003 · Mismatched name in TypeAliasType first argument
1B = TypeAliasType('WrongName', int)
2print(B)
3print(type(B))
4# After fix: `type WrongName = int` creates name 'WrongName', but var 'B' remains unbound to it
5# So B is still the TypeAliasType object, not the alias; referencing WrongName fails
6try:
7 print(WrongName)
8except NameError as e:
9 print('Missing:', e)
r1-p004 · Class attribute descriptor interaction
1class Desc:
2 def __get__(self, obj, owner):
3 return 'desc'
4C = TypeAliasType('C', Desc)
5class A:
6 attr = C
7print(A().attr)
8print(type(A.attr))
9# After fix: `type C = Desc` makes C a type, not Desc instance; attr becomes class var of type Desc
10# But descriptor protocol only works on instances; A.attr is now a type, not descriptor
11# So A().attr raises AttributeError (no __get__ on type)
12try:
13 print(A().attr)
14except AttributeError as e:
15 print('Attr error:', e)
r1-p005 · Evaluation order with side effects in compound expression (generated program does not parse: Traceback (most recent call last):)
1logs = []
2def log(x):
3 logs.append(x)
4 return x
5D = TypeAliasType('D', (log(1), log(2)))
6print('Logs after alias:', logs)
7try:
8 _, y: D
9 print('Logs after use:', logs)
10except:
11 pass
12# After fix: `type D = (log(1), log(2))` runs at definition, logs=[1,2] before any use
13# So second log call doesn't happen again; logs length differs
14print('Final logs:', logs)
r1-p006 · Shadowed builtin later redefined
1list = lambda: []
2E = TypeAliasType('E', list)
3print('E is:', E)
4print('E() is:', E())
5# After fix: `type E = list` uses built-in list, not the lambda
6# Because type statement binds to current scope's list? No: at definition, list is lambda
7# Wait: in CPython, type statement evaluates RHS in current scope at execution time
8# But the alias is created early; however, the fix moves it to top-level type stmt
9# Which runs during module exec, after list=lambda, so still sees lambda?
10# Actually: module runs top-down. list=lambda comes before type E=list, so sees lambda.
11# But original: E = TypeAliasType('E', list) also sees list=lambda at that line.
12# So same? Need rebinding AFTER.
13# Let's rebind after alias.
14list = int # rebind after
15print('After rebind, list is:', list)
16try:
17 print('E still:', E)
18except:
19 pass
20# Original: E holds reference to old list (lambda). Fix: type E = list uses current list at time of exec.
21# Since type stmt runs after list=int rebind? No: exec order:
22# 1. list = lambda
23# 2. E = TypeAliasType(...) -> sees list=lambda
24# 3. list = int
25# After fix:
26# 1. list = lambda
27# 2. type E = list -> sees list=lambda (still)
28# 3. list = int
29# So same. Need alias BEFORE rebind, but use AFTER.
30# Rewrite:
r1-p007 · String formatting in alias expression
1name = 'world'
2F = TypeAliasType('F', f'Hello {name}')
3print('F is:', F)
4# After fix: `type F = f'Hello {name}'` evaluates f-string at definition time
5# If name changes later, alias doesn't update
6name = 'there'
7print('After name change, F still:', F)
8try:
9 # F is a string literal in fix, so no dynamic behavior
10 pass
11except:
12 pass
13print('Name is now:', name)
r1-p008 · Pathlib vs os.path behavior (generated program is not deterministic)
1import os
2os.makedirs('tmp', exist_ok=True)
3with open('tmp/target.txt', 'w') as f:
4 f.write('real')
5os.symlink('tmp/target.txt', 'tmp/link.txt')
6G = TypeAliasType('G', os.path.realpath('tmp/link.txt'))
7print('Resolved path:', G)
8# After fix: `type G = os.path.realpath('tmp/link.txt')` runs at definition
9# If we delete/recreate symlink after, G doesn't update
10os.unlink('tmp/link.txt')
11os.symlink('tmp/other.txt', 'tmp/link.txt') # will fail, need other.txt
12with open('tmp/other.txt', 'w') as f:
13 f.write('other')
14print('After relink, realpath is now:', os.path.realpath('tmp/link.txt'))
15print('But G is still:', G)
16# Cleanup
17import shutil
18shutil.rmtree('tmp', ignore_errors=True)
r2-p001 · Class attribute vs instance attribute, descriptor interaction
1import typing
2
3class Meta(type):
4 def __get__(cls, obj, owner):
5 return super().__get__(obj, owner)
6
7class C(metaclass=Meta):
8 attr: typing.TypeAlias = int # Rule triggers here
9
10print(C.attr) # Class access via metaclass descriptor
11try:
12 c = C()
13 print(c.attr) # Instance access
14 c.attr = 99
15 print(c.attr) # Override on instance
16 del c.attr
17 print(c.attr) # Back to class via descriptor
18except Exception as e:
19 print(f'{type(e).__name__}: {e}')
<class 'int'>
<class 'int'>
99
<class 'int'>
r2-p002 · Target version pre-3.12: 'type' statement invalid syntax
1import sys
2if sys.version_info < (3, 12):
3 print('SKIP: Target version < 3.12, but we still write the code to be fixed')
4 # Simulate what the fix would produce: invalid syntax in <3.12
5 # We cannot actually run invalid syntax, so we show the intent
6 # The rule would rewrite this to `type T = int` which is invalid
7 T: typing.TypeAlias = int # This line is what the rule sees
8 try:
9 # This would fail after fix in <3.12
10 reveal_type(T) # type: ignore
11 except Exception as e:
12 print(f'{type(e).__name__}: {e}')
13else:
14 T: typing.TypeAlias = int
15 print(f'T is {T} and type(T) is {type(T)}')
T is <class 'int'> and type(T) is <class 'type'>
r2-p003 · Nested scope accessibility: type statement not visible where alias was
1import typing
2
3def outer():
4 inner_T: typing.TypeAlias = list[str] # Rule triggers here
5 def inner():
6 return inner_T # After fix: `type inner_T = list[str]` - but is it visible?
7 return inner()
8
9try:
10 result = outer()
11 print(f'Outer returned: {result}')
12 print(f'Isinstance check: {isinstance(["a"], result)}')
13except Exception as e:
14 print(f'{type(e).__name__}: {e}')
Outer returned: list[str]
TypeError: isinstance() argument 2 cannot be a parameterized generic
r2-p004 · __getattr__ / __dir__ relies on TypeAlias as runtime attribute
1import typing
2
3T: typing.TypeAlias = int # Rule triggers here
4
5def __getattr__(name):
6 if name == 'T':
7 return 42 # Only called if T is not found normally
8 raise AttributeError(name)
9
10def __dir__():
11 return ['T'] + [x for x in dir() if x != 'T']
12
13print('Before any access:', end=' ')
14try:
15 print(T)
16except AttributeError as e:
17 print(f'AttributeError: {e}')
18
19print('After defining T via getattr:', end=' ')
20try:
21 print(T)
22except AttributeError as e:
23 print(f'AttributeError: {e}')
Before any access: <class 'int'>
After defining T via getattr: <class 'int'>
r2-p005 · TypeAlias cannot be used in isinstance() after rewrite to type statement
1import typing
2
3T: typing.TypeAlias = int # Rule triggers here
4
5x = 42
6print(f'isinstance(x, T) before any fix: {isinstance(x, T)}')
7print(f'type(T): {type(T)}')
8print(f'T is: {T}')
9
10# After fix: `type T = int` -> T becomes a type, but cannot be used in isinstance at runtime?
11# Actually, in 3.12+, `type T = int` makes T a type and usable in isinstance.
12# But the hazard is: the rule removes the *variable*, so if code relied on T being a variable...
13# Let's test if T is callable or has id
14print(f'id(T): {id(T)}')
15try:
16 T() # Try to call it as if it were a constructor from a variable
17 print('T() succeeded')
18except Exception as e:
19 print(f'T() failed: {type(e).__name__}: {e}')
isinstance(x, T) before any fix: True
type(T): <class 'type'>
T is: <class 'int'>
id(T): 11118080
T() succeeded
r2-p006 · Keyword-only, positional-only, *args/**kwargs forwarding, defaults evaluated once (generated program does not parse: Traceback (most recent call last):)
1import typing
2
3def factory(a, /, b, *, c, d=4, *args, **kwargs):
4 return (a, b, c, d, args, kwargs)
5
6Alias: typing.TypeAlias = factory # Rule triggers here
7
8# Store original defaults
9import inspect
10orig = inspect.signature(factory)
11print(f'Original defaults: {orig.parameters["d"].default}')
12
13# Call via alias
14try:
15 result = Alias(1, 2, c=3, d=99, 4, 5, extra=6)
16 print(f'Alias call result: {result}')
17except Exception as e:
18 print(f'{type(e).__name__}: {e}')
19
20# Now simulate what happens if the fix breaks something
21# But the alias is just a function reference - should be fine
22# Hazard: if the rewrite somehow broke the binding
23print(f'Alias is still callable: {callable(Alias)}')
24print(f'Alias.__name__: {Alias.__name__}')
r2-p007 · Type annotations evaluated at runtime, typing-only imports
1import typing
2
3# Use a typing-only import that would be removed if not used
4from typing import List # This is used in the alias below
5
6Mylist: typing.TypeAlias = List[int] # Rule triggers here
7
8# Force evaluation of the annotation at runtime
9print(f'Mylist: {Mylist}')
10print(f'type(Mylist): {type(Mylist)}')
11
12# Now, if the rewrite happens, and we are in a context where typing is not imported...
13# But we imported it above. So make it conditional
14# Instead, test if the alias retains the ability to resolve List
15try:
16 # This should work because Mylist is a variable holding the resolved type
17 x: Mylist = [1, 2]
18 print(f'Variable annotation with Mylist: {x}')
19except Exception as e:
20 print(f'{type(e).__name__}: {e}')
21
22# After fix: `type Mylist = List[int]` - in 3.12+ this is fine, but if someone
23# deleted the import, it would break. But we didn't.
24# So instead, show that the alias is usable as a type hint
25print('Successfully used Mylist in type annotation')
Mylist: typing.List[int]
type(Mylist): <class 'typing._GenericAlias'>
Variable annotation with Mylist: [1, 2]
Successfully used Mylist in type annotation
r2-p008 · Dict ordering, duplicate keys, __hash__/__eq__ overrides, sets
1import typing
2
3class Weird:
4 def __init__(self, val):
5 self.val = val
6 def __hash__(self):
7 return hash(self.val)
8 def __eq__(self, other):
9 return isinstance(other, Weird) and self.val == other.val
10 def __repr__(self):
11 return f'Weird({self.val})'
12
13# Dict with duplicate keys in construction (later wins)
14# But we can't have duplicate keys in dict literal - so build via pairs
15pairs = [(Weird(1), 'a'), (Weird(1), 'b'), (Weird(2), 'c')] # Same hash, equal
16D: typing.TypeAlias = dict(pairs) # Rule triggers here
17
18print(f'D (alias): {D}')
19print(f'len(D): {len(D)}') # Should be 2: {Weird(1): 'b', Weird(2): 'c'}
20for k, v in D.items():
21 print(f' {k!r}: {v!r}')
22
23# Test if the dict is mutable and if alias reflects changes
24# But alias is the dict object itself
25D[Weird(3)] = 'd'
26print(f'After adding: {D}')
27print(f'len now: {len(D)}')
28
29# Create new dict via alias to see if it's fresh
30try:
31 new = D() # This fails - dict is not callable
32 print('D() succeeded')
33except Exception as e:
34 print(f'D() failed: {type(e).__name__}: {e}')
35
36# But we can use it as a type
37try:
38 y: D = {Weird(4): 'e'}
39 print(f'Successfully used D as type annotation: {y}')
40except Exception as e:
41 print(f'{type(e).__name__}: {e}')
D (alias): {Weird(1): 'b', Weird(2): 'c'}
len(D): 2
  Weird(1): 'b'
  Weird(2): 'c'
After adding: {Weird(1): 'b', Weird(2): 'c', Weird(3): 'd'}
len now: 3
D() failed: TypeError: 'dict' object is not callable
Successfully used D as type annotation: {Weird(4): 'e'}