fixhunt

ruff PLR1733: fix changes program behavior

fix-changes-behaviorlabelled safereal · highrelated
Findings / 2026-10-17T21-02-04-500Z-1p30-ruff-PLR1733 / r1-p007ruff 0.17.117 → 11 lines2026-10-17 21:02 UTC

Hypothesis Nemotron 3 Super predicted to fire fired 1×

In async context, replacing d[k] with v alters await timing if d[k] is awaitable.

Program

before
1async def get_val(k):
2 return k
3class AwaitableDict(dict):
4 def __getitem__(self, key):
5 return get_val(key)
6async def main():
7 d = AwaitableDict({'x': 1})
8 for k, v in d.items():
9 x = await d[k] # triggers PLR1733: would become await v — but v is int, not awaitable
10import asyncio
11result = asyncio.run(main())
after
1async def get_val(k):
2 return k
3class AwaitableDict(dict):
4 def __getitem__(self, key):
5 return get_val(key)
6async def main():
7 d = AwaitableDict({'x': 1})
8 for k, v in d.items():
9 x = await v # triggers PLR1733: would become await v — but v is int, not awaitable
10import asyncio
11result = asyncio.run(main())

Output

stdout beforeexit 0
(no stdout)
stdout afterexit 1
(no stdout)
TypeError: object int can't be used in 'await' expression
fired
1 before · 0 after
runs
each version twice, same file name, no network

Judge Nemotron 3 Ultra advisory real · high

The rule assumes that the value yielded by .items() is identical to the value returned by __getitem__, but the program uses a custom dict subclass where __getitem__ returns an awaitable while the stored value is an int. The rewrite changes await d[k] (which awaits the async result) to await v (which tries to await an int), causing a TypeError.

Explanation Nemotron 3 Super unverified

The original code awaits `d[k]`, which invokes `__getitem__` returning `get_val(k)`—a coroutine that yields the integer value of `k`. After rewriting to `await v`, it attempts to await the loop variable `v`, which is the actual dictionary value (an integer), not a coroutine. Since integers are not awaitable, this raises a TypeError, changing behavior from successful execution to failure.

Reproduce

file
prog.py (the before program)
minimized program
r1-p007.py
evidence sheet
r1-p007.md
regression fixture
r1-p007.py the minimized program with a header stating the observed change, for the tool's test suite
python3 prog.py
ruff check --preview --isolated --select PLR1733 --fix prog.py
python3 prog.py

Upstream search GitHub issues Tavily Nemotron 3 Super + Ultra

PLR1733 fix · PLR1733 dict items loop · await expression optimization · ruff autofix await variable

Details

program
Async Def/Await Ordering
hazard
In async context, replacing d[k] with v alters await timing if d[k] is awaitable.
oracle
exit 0 → 1 stderr before: (none) stderr after: TypeError: object int can't be used in 'await' expression
run
2026-10-17T21-02-04-500Z-1p30-ruff-PLR1733
generated program (17 lines) and the fix's diff
1async def get_val(k):
2 await asyncio.sleep(0.001)
3 return k
4class AwaitableDict(dict):
5 def __getitem__(self, key):
6 return get_val(key)
7async def main():
8 d = AwaitableDict({'x': 1})
9 trace = []
10 for k, v in d.items():
11 trace.append('before')
12 x = await d[k] # triggers PLR1733: would become await v — but v is int, not awaitable
13 trace.append(('after', x))
14 return trace
15import asyncio
16result = asyncio.run(main())
17print('result:', result)
--- orig.py	2026-10-17 17:02:19.745168460 -0400
+++ fixed.py	2026-10-17 17:02:20.064172249 -0400
@@ -9,7 +9,7 @@
     trace = []
     for k, v in d.items():
         trace.append('before')
-        x = await d[k]  # triggers PLR1733: would become await v — but v is int, not awaitable
+        x = await v  # triggers PLR1733: would become await v — but v is int, not awaitable
         trace.append(('after', x))
     return trace
 import asyncio
rule documentation given to the models
# unnecessary-dict-index-lookup (PLR1733)

Derived from the **Pylint** linter.

Fix is always available.

## What it does
Checks for key-based dict accesses during `.items()` iterations.

## Why is this bad?
When iterating over a dict via `.items()`, the current value is already
available alongside its key. Using the key to look up the value is
unnecessary.

## Example
```python
FRUITS = {"apple": 1, "orange": 10, "berry": 22}

for fruit_name, fruit_count in FRUITS.items():
    print(FRUITS[fruit_name])
```

Use instead:
```python
FRUITS = {"apple": 1, "orange": 10, "berry": 22}

for fruit_name, fruit_count in FRUITS.items():
    print(fruit_count)
```