ruff UP047
labelled unknown
Analysis Nemotron 3 Super
Flags standalone TypeVar and ParamSpec usage in generic function annotations.
Fix: Removes the standalone TypeVar/ParamSpec variable definitions and adds the corresponding type parameters to the function signature using PEP 695 syntax (e.g., def func[T](x: T) -> T: ...).
12 hazards
- Rewriting changes the scope of the type variable from module-level to function-local, which can break code that references the variable outside the function (e.g., in other function signatures or class definitions).
- If the TypeVar uses a 'default' argument (Python 3.13+), the fix omits the default, altering the inferred type when the parameter is omitted.
- If the TypeVar has a 'bound' constraint, the fix does not preserve the bound in the new syntax, potentially changing type-checking behavior.
- If the TypeVar is contravariant or covariant, the fix drops the variance keyword, and PEP 695 infers variance instead, which may differ and affect subtyping relationships.
- If multiple functions share the same TypeVar, rewriting one function leaves the shared variable defined but may cause mismatches if other functions are not also rewritten.
- If the TypeVar is used in a default argument value (e.g., def f(x: T = SomeDefault)), the fix does not update the default expression, leaving a reference to an undefined name after removal.
- If the TypeVar is used in a type ignore comment or string annotation nearby, removing the variable definition may cause those references to become unresolved.
- If the file contains a conditional import or definition of the TypeVar (e.g., inside an if block), the fix may hoist or misplace the removal, causing syntax errors.
- If the TypeVar is aliased via import (e.g., from mod import TypeVar as TV), the rule may not detect it and leave the alias while incorrectly adding a type parameter, resulting in mixed old/new style.
- If the function is decorated, the fix inserts type parameters before the decorator, which is invalid syntax.
- If the function has overlapping type parameters with another generic function in the same scope, the fix may introduce duplicate or conflicting type parameter names.
- If the code uses a stringized annotation (from __future__ import annotations) and the TypeVar is referenced in a string, removing the definition may break runtime evaluation of annotations.
Programs
| id | result | title | hazard |
|---|---|---|---|
| r1-p001 | silent prediction off | Context manager without close() - resource leak risk | context managers that are not files, objects with write() but no close() |
| r1-p002 | silent prediction off | Decorator placement breaks syntax | If the function is decorated, the fix inserts type parameters before the decorator, which is invalid syntax. |
| r1-p003 | silent prediction off | Variance dropped in PEP 695 rewrite | If the TypeVar is contravariant or covariant, the fix drops the variance keyword, and PEP 695 infers variance instead, which may differ and affect subtyping relationships. |
| r1-p004 | silent prediction off | Mutable default argument capture | mutable default arguments and aliasing after a rewrite |
| r1-p005 | silent prediction off | Truthiness and identity checks | truthiness of custom __bool__/__len__, None vs falsy, 'is' vs '==' |
| r1-p006 | silent prediction off | Unresolved reference in type ignore or string annotation | If the TypeVar is used in a type ignore comment or string annotation nearby, removing the variable definition may cause those references to become unresolved. |
| r1-p007 | silent prediction off | Class vs instance attribute interaction | class attributes vs instance attributes, descriptors, properties with side effects |
| r1-p008 | silent prediction off | Exception handling and finally clauses | exceptions raised inside the rewritten code, finally/else clauses, exception chaining |
| r2-p001 | silent prediction off | TypeVar referenced in string annotation becomes unresolved after rewrite | Unresolved reference in type ignore or string annotation |
| r2-p002 | silent prediction off | Runtime evaluation of TypeVar annotation fails after removal due to typing-only import assumption | type annotations evaluated at runtime, typing-only imports |
| r2-p003 | silent prediction off | Covariant TypeVar loses variance in PEP 695 rewrite, affecting subtype checks | Variance dropped in PEP 695 rewrite |
| r2-p004 | silent prediction off | Binary write fails after rewrite due to str/bytes confusion in reused buffer | bytes vs str, encoding and newline handling, text vs binary modes |
| r2-p005 | silent prediction off | Module-level TypeVar reused in another function breaks after local scoping rewrite | Rewriting changes scope from module-level to function-local |
| r2-p006 | silent prediction off | Async function rewritten with misplaced type parameters breaks await ordering | async def/await ordering, async generators |
| r2-p007 | silent prediction off | Shared TypeVar across functions causes mismatch when only one is rewritten | Multiple functions share TypeVar; rewriting one leaves others mismatched |
| r2-p008 | silent prediction off | File truncation occurs on rewrite due to mode confusion in buffered write loop | file modes and truncation, partial writes, non-str data passed to write |
Sources
r1-p001 · Context manager without close() - resource leak risk
1from typing import TypeVar
2T = TypeVar('T')
3class Buffer:
4 def __init__(self): self.data = []
5 def write(self, x): self.data.append(x)
6 def __enter__(self): return self
7 # Note: no __exit__ or close!
8def process(x: T) -> T:
9 with Buffer() as b:
10 b.write(x)
11 return x
12result = process("hello")
13print(f"Buffer data: {result}")
r1-p002 · Decorator placement breaks syntax
1from typing import TypeVar
2T = TypeVar('T')
3import functools
4@functools.lru_cache(maxsize=128)
5def cached_func(x: T) -> T:
6 return x
7print(cached_func(42))
42
r1-p003 · Variance dropped in PEP 695 rewrite
1from typing import TypeVar
2T_co = TypeVar('T_co', covariant=True)
3class Container(Generic[T_co]):
4 def __init__(self, item): self.item = item
5 @property
6 def value(self) -> T_co:
7 return self.item
8c: Container[object] = Container("str")
9print(f"Is str-subtype of object? {isinstance(c.value, str)}")
r1-p004 · Mutable default argument capture
1from typing import TypeVar
2T = TypeVar('T')
3def append_to_list(x: T, lst=[]):
4 lst.append(x)
5 return lst
6r1 = append_to_list(1)
7r2 = append_to_list(2)
8print(f"List 1: {r1}, List 2: {r2}, Same object: {r1 is r2}")
List 1: [1, 2], List 2: [1, 2], Same object: True
r1-p005 · Truthiness and identity checks
1from typing import TypeVar
2T = TypeVar('T')
3class FakeBool:
4 def __init__(self, val): self.val = val
5 def __bool__(self): return self.val is not None
6 def __eq__(self, other): return isinstance(other, FakeBool) and self.val == other.val
7def check(x: T) -> T:
8 if x is None:
9 return x
10 if x:
11 return FakeBool(True)
12 return x
13result = check(FakeBool(0))
14print(f"Result truthy: {bool(result)}, Result is FakeBool(True): {result == FakeBool(True)}")
Result truthy: True, Result is FakeBool(True): True
r1-p006 · Unresolved reference in type ignore or string annotation
1from typing import TypeVar
2T = TypeVar('T')
3def func(x: 'T') -> T: # type: ignore
4 return x
5print(func("test"))
test
r1-p007 · Class vs instance attribute interaction
1from typing import TypeVar
2T = TypeVar('T')
3class Tracking:
4 access_count = 0
5 def __init__(self, value): self._value = value
6 @property
7 def value(self) -> T:
8 Tracking.access_count += 1
9 return self._value
10obj = Tracking("hello")
11print(f"First access: {obj.value}, Count: {Tracking.access_count}")
12print(f"Second access: {obj.value}, Count: {Tracking.access_count}")
First access: hello, Count: 1 Second access: hello, Count: 2
r1-p008 · Exception handling and finally clauses
1from typing import TypeVar
2T = TypeVar('T')
3def risky(x: T) -> T:
4 try:
5 if x == 0:
6 raise ValueError("zero")
7 return x
8 except ValueError as e:
9 raise RuntimeError("bad") from e
10 finally:
11 print("cleanup")
12try:
13 risky(0)
14except RuntimeError as e:
15 print(f"Caught: {e}, Cause: {type(e.__cause__).__name__ if e.__cause__ else None}")
cleanup Caught: bad, Cause: ValueError
r2-p001 · TypeVar referenced in string annotation becomes unresolved after rewrite
1from typing import TypeVar
2T = TypeVar('T')
3def f(x: T) -> 'list[T]': # String annotation uses T
4 return [x]
5print(f(1))
[1]
r2-p002 · Runtime evaluation of TypeVar annotation fails after removal due to typing-only import assumption
1from typing import TypeVar
2T = TypeVar('T')
3def f(x):
4 return x.__annotations__['x'] # Accesses annotation at runtime
5try:
6 result = f(1)
7except NameError as e:
8 print('NameError:', e)
r2-p003 · Covariant TypeVar loses variance in PEP 695 rewrite, affecting subtype checks
1from typing import TypeVar
2T_co = TypeVar('T_co', covariant=True)
3def f(x: T_co) -> T_co:
4 return x
5class A: pass
6class B(A): pass
7print(isinstance(f(B()), A)) # Should be True; may break if variance inferred incorrectly
True
r2-p004 · Binary write fails after rewrite due to str/bytes confusion in reused buffer
1from typing import TypeVar
2T = TypeVar('T')
3def process(data: T) -> T:
4 with open('tmp.bin', 'wb') as f:
5 f.write(data) # Expects bytes
6 return data
7try:
8 process(b'hello')
9except TypeError as e:
10 print('TypeError:', e)
11finally:
12 import os
13 if os.path.exists('tmp.bin'):
14 os.remove('tmp.bin')
r2-p005 · Module-level TypeVar reused in another function breaks after local scoping rewrite
1from typing import TypeVar
2T = TypeVar('T')
3def f(x: T) -> T:
4 return x
5def g(y: T) -> str: # Reuses T from module scope
6 return f(y)
7print(g(42))
42
r2-p006 · Async function rewritten with misplaced type parameters breaks await ordering
1from typing import TypeVar
2T = TypeVar('T')
3async def fetch(x: T) -> T:
4 await asyncio.sleep(0.01) # type: ignore
5 return x
6import asyncio
7async def main():
8 result = await fetch('hello')
9 print(result)
10asyncio.run(main())
hello
r2-p007 · Shared TypeVar across functions causes mismatch when only one is rewritten
1from typing import TypeVar
2T = TypeVar('T')
3def identity(x: T) -> T:
4 return x
5def wrapper(x: T) -> T: # Still uses module T
6 return identity(x)
7print(wrapper('test')) # May fail if identity is rewritten but wrapper not
test
r2-p008 · File truncation occurs on rewrite due to mode confusion in buffered write loop
1from typing import TypeVar
2T = TypeVar('T')
3def log_message(msg: T) -> None:
4 with open('log.txt', 'w') as f: # Truncates each call
5 f.write(str(msg))
6log_message('first')
7with open('log.txt', 'r') as f:
8 print(f.read())
first