ruff UP011
labelled unknown
Analysis Nemotron 3 Super
Flags redundant parentheses when using @functools.lru_cache() without arguments.
Fix: Removes the trailing empty parentheses from @functools.lru_cache(), rewriting it as @functools.lru_cache while preserving spacing, imports, aliases, and surrounding decorators.
12 hazards
- Rewriting @functools.lru_cache() inside a TYPE_CHECKING block may break type checking if the block is conditionally excluded at runtime but the linter assumes it is active.
- If the decorator line contains a trailing comment after the parentheses, the fix may drop or misplace that comment during whitespace normalization.
- When the decorator is applied to a class, removing parentheses could be misinterpreted if the class defines __call__ and the decorator is later invoked elsewhere in the module.
- In code that manually inspects decorator syntax via ast or inspect, the absence of parentheses may alter the perceived call structure and break introspection logic.
- If functools.lru_cache is shadowed by a local variable named lru_cache in an outer scope, the unqualified name after rewrite may resolve incorrectly.
- When the decorator is used on a forward-referenced name in a string annotation context, the rewrite has no effect but may create inconsistency in mixed-style codebases.
- If the line contains a line continuation character within the parentheses (e.g., due to formatting), removing them may break the syntactic validity of the statement.
- In Python versions prior to 3.8, removing the parentheses would cause a SyntaxError because lru_cache could not be used as a bare decorator.
- If the decorator is part of a macro-generated code block where the parentheses are significant for template matching, their removal may prevent correct expansion.
- When the decorator appears in a stub (.pyi) file, some type checkers may expect the explicit call form for compatibility with older stubs, and the rewrite could trigger false positives.
- If the decorator line is edited via a tool that relies on the presence of parentheses to delimit the decorator expression, subsequent edits may fail to parse correctly.
- In rare cases where lru_cache is imported via a relative import or through a namespace package, the rewrite does not affect resolution but may obscure intent in code review.
Findings
| program | oracle | label | judge | status | summary |
|---|---|---|---|---|---|
| r2-p005 | fix-changes-behavior | labelled safe | introspection-only · low | related | The program parses its own source code and inspects the AST node for the decorator, checking whether the decorator call has zero arguments. After the fix remov… |
Programs
| id | result | title | hazard |
|---|---|---|---|
| r1-p001 | invalid | Macro-generated code dependence on parentheses | If the decorator is part of a macro-generated code block where the parentheses are significant for template matching, their removal may prevent correct expansion. |
| r1-p002 | invalid | Tool-dependent parsing via parentheses delimiters | If the decorator line is edited via a tool that relies on the presence of parentheses to delimit the decorator expression, subsequent edits may fail to parse correctly. |
| r1-p003 | fix ok | Walrus/comprehension scoping interaction | walrus, comprehensions scoping, late binding in closures and lambdas |
| r1-p004 | silent | Comments/docstrings attachment preservation | comments and docstrings attached to the removed or moved code |
| r1-p005 | fix ok | Async def/await ordering interaction | async def/await ordering, async generators |
| r1-p006 | fix ok | String formatting edge case interaction | string formatting edge cases: %, format, f-strings, repr vs str, nested quotes |
| r1-p007 | fix ok | Shadowing by outer scope variable | If functools.lru_cache is shadowed by a local variable named lru_cache in an outer scope, the unqualified name after rewrite may resolve incorrectly. |
| r1-p008 | fix ok | Bytes/str encoding and newline handling | bytes vs str, encoding and newline handling, text vs binary modes |
| r2-p001 | fix ok | File mode truncation with LRU-cached writer | file modes and truncation, partial writes, non-str data passed to write |
| r2-p002 | fix ok | Numeric coercion with cached division | int/float/bool coercion, Decimal, division and rounding |
| r2-p003 | invalid | String formatting with nested quotes and repr | string formatting edge cases: %, format, f-strings, repr vs str, nested quotes |
| r2-p004 | fix ok | Forward reference in string annotation with LRU cache | When the decorator is used on a forward-referenced name in a string annotation context, the rewrite has no effect but may create inconsistency in mixed-style codebases. |
| r2-p005 | fix-changes-behavior | AST introspection of decorator call structure | In code that manually inspects decorator syntax via ast or inspect, the absence of parentheses may alter the perceived call structure and break introspection logic. |
| r2-p006 | invalid | Binary mode write with encoding-sensitive data | bytes vs str, encoding and newline handling, text vs binary modes |
| r2-p007 | fix ok | Stub file compatibility simulation | When the decorator appears in a stub (.pyi) file, some type checkers may expect the explicit call form for compatibility with older stubs, and the rewrite could trigger false positives. |
| r2-p008 | fix ok | Relative import and namespace package obscurity | In rare cases where lru_cache is imported via a relative import or through a namespace package, the rewrite does not affect resolution but may obscure intent in code review. |
Sources
r1-p001 · Macro-generated code dependence on parentheses (generated program does not parse: Traceback (most recent call last):)
1import functools
2decorator = '@functools.lru_cache()'
3exec(decorator + '
4def f(): return 1')
5print(f())
r1-p002 · Tool-dependent parsing via parentheses delimiters (generated program does not parse: Traceback (most recent call last):)
1import functools
2source = '''def foo(): pass'''
3injected = source.replace('def', '@functools.lru_cache()
4def')
5exec(injected)
6print(foo.__wrapped__( )) if hasattr(foo, '__wrapped__') else print(type(foo))
r1-p003 · Walrus/comprehension scoping interaction
1from functools import lru_cache
2@lru_cache()
3def make_adder(n):
4 return lambda x: x + n
5f1 = make_adder(10)
6print(f1(5))
7# After fix: same behavior but tests sensitivity
--- orig.py 2026-10-17 17:15:27.585818711 -0400 +++ fixed.py 2026-10-17 17:15:27.759805675 -0400 @@ -1,5 +1,5 @@ from functools import lru_cache -@lru_cache() +@lru_cache def make_adder(n): return lambda x: x + n f1 = make_adder(10)
15
r1-p004 · Comments/docstrings attachment preservation
1# This comment precedes the decorator
2def dummy():
3 """Docstring here."""
4 return 42
5print(dummy())
42
r1-p005 · Async def/await ordering interaction
1import functools
2@functools.lru_cache()
3async def fetch():
4 await _dummy()
5 return 99
6async def _dummy(): pass
7import asyncio
8result = asyncio.run(fetch())
9print(result)
--- orig.py 2026-10-17 17:15:27.992788219 -0400 +++ fixed.py 2026-10-17 17:15:28.291765859 -0400 @@ -1,5 +1,5 @@ import functools -@functools.lru_cache() +@functools.lru_cache async def fetch(): await _dummy() return 99
99
r1-p006 · String formatting edge case interaction
1from functools import lru_cache
2@lru_cache()
3def fmt(x):
4 return f'Value: {x!r}'
5print(fmt(42))
6print(fmt(42)) # cached
--- orig.py 2026-10-17 17:15:28.456753520 -0400 +++ fixed.py 2026-10-17 17:15:28.627740733 -0400 @@ -1,5 +1,5 @@ from functools import lru_cache -@lru_cache() +@lru_cache def fmt(x): return f'Value: {x!r}' print(fmt(42))
Value: 42 Value: 42
r1-p007 · Shadowing by outer scope variable
1def outer():
2 lru_cache = lambda f: f # breaks lru_cache
3 from functools import lru_cache as real_lru
4 @real_lru()
5 def inner():
6 return 1
7 return inner()
8print(outer())
--- orig.py 2026-10-17 17:15:28.732732881 -0400 +++ fixed.py 2026-10-17 17:15:28.909719645 -0400 @@ -1,7 +1,7 @@ def outer(): lru_cache = lambda f: f # breaks lru_cache from functools import lru_cache as real_lru - @real_lru() + @real_lru def inner(): return 1 return inner()
1
r1-p008 · Bytes/str encoding and newline handling
1import functools
2@functools.lru_cache()
3def read_file(path):
4 with open(path, 'r', encoding='utf-8') as f:
5 return f.read()
6with open('test.txt', 'w', encoding='utf-8') as f:
7 f.write('hello')
8print(read_file('test.txt'))
9import os
10os.remove('test.txt')
--- orig.py 2026-10-17 17:15:29.017711571 -0400 +++ fixed.py 2026-10-17 17:15:29.189698733 -0400 @@ -1,5 +1,5 @@ import functools -@functools.lru_cache() +@functools.lru_cache def read_file(path): with open(path, 'r', encoding='utf-8') as f: return f.read()
hello
r2-p001 · File mode truncation with LRU-cached writer
1import functools
2@functools.lru_cache()
3def cached_write(data):
4 with open('tmp.bin', 'wb') as f:
5 if isinstance(data, float):
6 data = struct.pack('<d', data)
7 elif isinstance(data, bool):
8 data = b'\x01' if data else b'\x00'
9 f.write(data[:2]) # Truncate to 2 bytes
10 f.seek(0); return f.read()
11try:
12 result1 = cached_write(3.14)
13 result2 = cached_write(True)
14 print('float->bytes:', repr(result1))
15 print('bool->bytes:', repr(result2))
16except Exception as e:
17 print(type(e).__name__, str(e))
18finally:
19 import os; os.remove('tmp.bin') if os.path.exists('tmp.bin') else None
--- orig.py 2026-10-17 17:15:46.183449093 -0400 +++ fixed.py 2026-10-17 17:15:46.360436288 -0400 @@ -1,5 +1,5 @@ import functools -@functools.lru_cache() +@functools.lru_cache def cached_write(data): with open('tmp.bin', 'wb') as f: if isinstance(data, float):
NameError name 'struct' is not defined
r2-p002 · Numeric coercion with cached division
1import functools, decimal
2@functools.lru_cache()
3def divide_cached(a, b):
4 return round(float(a) / float(b), 4)
5d = decimal.Decimal('10')
6print(divide_cached(d, 3)) # Decimal -> float via float()
7print(divide_cached(True, False)) # Bool division: True/False -> 1.0/0.0 -> ZeroDivisionError
8print(divide_cached(5, 2)) # Should hit cache on second call
9# Second call to same args should return cached value
10print(divide_cached(5, 2))
--- orig.py 2026-10-17 17:15:46.473428113 -0400 +++ fixed.py 2026-10-17 17:15:46.890397945 -0400 @@ -1,5 +1,5 @@ import functools, decimal -@functools.lru_cache() +@functools.lru_cache def divide_cached(a, b): return round(float(a) / float(b), 4) d = decimal.Decimal('10')
3.3333
r2-p003 · String formatting with nested quotes and repr (generated program does not parse: Traceback (most recent call last):)
1import functools
2@functools.lru_cache()
3def fmt_cached(x):
4 s = str(x)
5 return f'{repr(s)} | {s!r} | %{s}
6'
7print(fmt_cached('hello\ world'))
8print(fmt_cached(42))
9# Second call should hit cache
10print(fmt_cached('hello\ world'))
r2-p004 · Forward reference in string annotation with LRU cache
1from __future__ import annotations
2import functools
3@functools.lru_cache()
4def process_data(value: 'DataProcessor') -> int:
5 return len(str(value))
6class DataProcessor:
7 def __init__(self, data):
8 self.data = data
9 def __str__(self):
10 return str(self.data)
11dp = DataProcessor('test123')
12print(process_data(dp))
13print(process_data(dp)) # Should be cached
--- orig.py 2026-10-17 17:15:47.252371789 -0400 +++ fixed.py 2026-10-17 17:15:47.417359874 -0400 @@ -1,6 +1,6 @@ from __future__ import annotations import functools -@functools.lru_cache() +@functools.lru_cache def process_data(value: 'DataProcessor') -> int: return len(str(value)) class DataProcessor:
7 7
r2-p005 · AST introspection of decorator call structure
1import functools, ast
2@functools.lru_cache()
3def cached_func(x):
4 return x * 2
5tree = ast.parse(open(__file__).read())
6for node in ast.walk(tree):
7 if isinstance(node, ast.FunctionDef) and node.name == 'cached_func':
8 for dec in node.decorator_list:
9 if isinstance(dec, ast.Call) and dec.func.attr == 'lru_cache':
10 print('Decorator is call:', len(dec.args) == 0 and len(dec.keywords) == 0)
11 elif isinstance(dec, ast.Attribute) and dec.attr == 'lru_cache':
12 print('Decorator is attribute only:', dec.attr)
13print('Result:', cached_func(5))
--- orig.py 2026-10-17 17:15:47.523352220 -0400 +++ fixed.py 2026-10-17 17:15:47.705339077 -0400 @@ -1,5 +1,5 @@ import functools, ast -@functools.lru_cache() +@functools.lru_cache def cached_func(x): return x * 2 tree = ast.parse(open(__file__).read())
Decorator is call: True Result: 10
r2-p006 · Binary mode write with encoding-sensitive data (generated program does not parse: Traceback (most recent call last):)
1import functools, codecs
2@functools.lru_cache()
3def encode_and_write(text, encoding='utf-8'):
4 data = text.encode(encoding)
5 with open('tmp.dat', 'wb') as f:
6 f.write(data)
7 f.write(b'
8')
9 with open('tmp.dat', 'rb') as f:
10 return f.read().strip(b'
11')
12result1 = encode_and_write('café')
13result2 = encode_and_write('café') # Should hit cache
14print('First:', repr(result1))
15print('Second:', repr(result2))
16print('Equal:', result1 == result2)
17import os; os.remove('tmp.dat') if os.path.exists('tmp.dat') else None
r2-p007 · Stub file compatibility simulation
1# Simulate .pyi file behavior: type checker expects @functools.lru_cache()
2# But we are in .py file - rule will remove parentheses
3import functools
4@functools.lru_cache()
5def stubbed_func(x: int) -> str:
6 return str(x * 2)
7# If type checker sees @functools.lru_cache (no parens) in .pyi, it may error
8# We simulate by checking if the decorator form affects runtime
9print('Result:', stubbed_func(21))
10print('Cached:', stubbed_func.__wrapped__(21) if hasattr(stubbed_func, '__wrapped__') else 'no wrapper')
--- orig.py 2026-10-17 17:15:47.945321746 -0400 +++ fixed.py 2026-10-17 17:15:48.116309413 -0400 @@ -1,7 +1,7 @@ # Simulate .pyi file behavior: type checker expects @functools.lru_cache() # But we are in .py file - rule will remove parentheses import functools -@functools.lru_cache() +@functools.lru_cache def stubbed_func(x: int) -> str: return str(x * 2) # If type checker sees @functools.lru_cache (no parens) in .pyi, it may error
Result: 42 Cached: 42
r2-p008 · Relative import and namespace package obscurity
1import sys, os
2sys.path.insert(0, os.path.dirname(__file__))
3try:
4 from .local_functools import lru_cache # Relative import simulation
5 @lru_cache()
6 def cached_local(x):
7 return x + 10
8except ImportError:
9 # Fallback: simulate namespace package by importing via package-like name
10 import functools as local_functools
11 @local_functools.lru_cache()
12 def cached_local(x):
13 return x + 10
14print(cached_local(5))
15print(cached_local(5)) # Cache hit
16# Cleanup
17if 'local_functools' in locals():
18 del local_functools
19if '.' in sys.path[0]:
20 sys.path.pop(0)
--- orig.py 2026-10-17 17:15:48.221301844 -0400 +++ fixed.py 2026-10-17 17:15:48.392289519 -0400 @@ -8,7 +8,7 @@ except ImportError: # Fallback: simulate namespace package by importing via package-like name import functools as local_functools - @local_functools.lru_cache() + @local_functools.lru_cache def cached_local(x): return x + 10 print(cached_local(5))
15 15