fixhunt

ruff PERF102

labelled unknown
Runs / 2026-10-17T20-47-13-524Z-2url-ruff-PERF102ruff 0.17.12026-10-17 20:47 UTC8 programs × 2 round(s)16 programs, 10 fired, 0 fixed, 0 findings, 0 rejectedmodel cost $0.0029

Analysis Nemotron 3 Super

Flags dict.items() iteration where either loop variable is unused.

Fix: Replace dict.items() with dict.keys() when only the key is used, or dict.values() when only the value is used, preserving the used loop variable and loop body.

8 hazards
  • Rewriting 'for k, v in d.items(): ...' to 'for k in d.keys(): ...' changes behavior if 'v' is referenced later in the function due to lingering binding.
  • If the loop variable prefixed with '_' is actually used (e.g., via locals() or eval), replacing .items() with .values() removes the key binding, breaking access.
  • When the dictionary is a non-standard mapping (e.g., a custom class without .keys()/.values()), the rewrite causes AttributeError.
  • If .items(), .keys(), or .values() are overridden with custom logic (e.g., logging or filtering), the rewrite alters side effects.
  • In nested scopes, replacing .items() with .keys()/.values() may inadvertently capture outer-scope variables if the loop variable name shadows them.
  • If the loop body contains 'del v' or similar, removing the binding via .values() causes UnboundLocalError.
  • The rewrite may disrupt debugging or tracing tools that rely on the frame locals containing both key and value variables.
  • If the dictionary iteration is modified during iteration (e.g., adding/removing keys), .items(), .keys(), and .values() behave differently under mutation, altering which items are visited.

Programs

idresulttitlehazard
r1-p001fired, no fixOuter scope shadowingIn nested scopes, replacing .items() with .keys()/.values() may inadvertently capture outer-scope variables if the loop variable name shadows them.
r1-p002fired, no fixComments and docstringscomments and docstrings attached to the removed or moved code
r1-p003fired, no fixType annotations evaluated at runtimetype annotations evaluated at runtime, typing-only imports
r1-p004fired, no fixMutable default arguments and aliasingmutable default arguments and aliasing after a rewrite
r1-p005silent prediction offDebugging/tracing toolsThe rewrite may disrupt debugging or tracing tools that rely on the frame locals containing both key and value variables.
r1-p006fired, no fixParameter kinds and defaultskeyword-only, positional-only, *args/**kwargs forwarding, defaults evaluated once
r1-p007silent prediction offIterators consumed oncegenerators vs lists, iterators consumed once, lazy vs eager evaluation
r1-p008fired, no fixBytes vs str encodingbytes vs str, encoding and newline handling, text vs binary modes
r2-p001fired, no fixNon-standard mapping lacks .keys()/.values()When the dictionary is a non-standard mapping (e.g., a custom class without .keys()/.values()), the rewrite causes AttributeError.
r2-p002silent prediction offMutation during iteration changes visited itemsIf the dictionary iteration is modified during iteration (e.g., adding/removing keys), .items(), .keys(), and .values() behave differently under mutation, altering which items are visited.
r2-p003silent prediction offFile mode truncation with partial writefile modes and truncation, partial writes, non-str data passed to write
r2-p004fired, no fixdel v in loop body causes UnboundLocalError after rewriteIf the loop body contains 'del v' or similar, removing the binding via .values() causes UnboundLocalError.
r2-p005silent prediction offOverridden .items/.keys/.values with side effectsIf .items(), .keys(), or .values() are overridden with custom logic (e.g., logging or filtering), the rewrite alters side effects.
r2-p006fired, no fixLingering binding of unused variable after rewriteRewriting 'for k, v in d.items(): ...' to 'for k in d.keys(): ...' changes behavior if 'v' is referenced later in the function due to lingering binding.
r2-p007silent prediction offFrame locals missing value variable breaks debuggerThe rewrite may disrupt debugging or tracing tools that rely on the frame locals containing both key and value variables.
r2-p008fired, no fixBytes vs str encoding mismatch in writebytes vs str, encoding and newline handling, text vs binary modes

Sources

r1-p001 · Outer scope shadowing
1x = 'outer'
2d = {'a': 1}
3for x, _ in d.items():
4 def inner():
5 return x
6 print(inner(), end='')
a
r1-p002 · Comments and docstrings
1d = {'k': 42}
2# comment before
3for key, _ in d.items(): # inline
4 """docstring"""
5 print(key)
k
r1-p003 · Type annotations evaluated at runtime
1from typing import List
2def f() -> List[int]:
3 d = {1: 10}
4 for k, _ in d.items():
5 return [k]
6print(f())
[1]
r1-p004 · Mutable default arguments and aliasing
1def g(d={}):
2 for k, _ in d.items():
3 d[k] = 0
4 return d
5print(g({1: 2}))
6print(g({}))
{1: 0}
{}
r1-p005 · Debugging/tracing tools
1import sys
2d = {'a': 1}
3for k, v in d.items():
4 frame = sys._getframe(0)
5 print('k' in frame.f_locals and 'v' in frame.f_locals, end='')
True
r1-p006 · Parameter kinds and defaults
1def h(*, kw, d={'x': 1}):
2 for k, _ in d.items():
3 kw.append(k)
4 return kw
5print(h(kw=[]))
['x']
r1-p007 · Iterators consumed once
1def gen():
2 yield ('a', 1)
3 yield ('b', 2)
4g = gen()
5for k, _ in g:
6 print(k, end='')
7try:
8 next(g)
9 print('extra', end='')
10except StopIteration:
11 print('done', end='')
abdone
r1-p008 · Bytes vs str encoding
1with open('tmp.bin', 'wb') as f:
2 f.write(b'hello\nworld')
3with open('tmp.bin', 'rb') as f:
4 lines = {i: line for i, line in enumerate(f)}
5 for idx, _ in lines.items():
6 print(idx, end='')
01
r2-p001 · Non-standard mapping lacks .keys()/.values()
1class Map:
2 def __iter__(self):
3 return iter([('a', 1), ('b', 2)])
4 def items(self):
5 return list(self.__iter__())
6d = Map()
7try:
8 for k, v in d.items():
9 print(k)
10except AttributeError as e:
11 print(f'{type(e).__name__}: {e}')
a
b
r2-p002 · Mutation during iteration changes visited items
1d = {'a': 1, 'b': 2}
2seen = []
3for k, v in d.items():
4 seen.append((k, v))
5 if k == 'a':
6 d['c'] = 3
7print(sorted(seen))
r2-p003 · File mode truncation with partial write
1with open('test.txt', 'w') as f:
2 pass
3with open('test.txt', 'r+') as f:
4 for k, v in {'a': 'hello', 'b': 'world'}.items():
5 f.write(v)
6 if k == 'a':
7 f.truncate()
8with open('test.txt', 'r') as f:
9 print(repr(f.read()))
10import os
11os.remove('test.txt')
'helloworld'
r2-p004 · del v in loop body causes UnboundLocalError after rewrite
1d = {'a': 1, 'b': 2}
2for k, v in d.items():
3 print(v)
4 del v
5print('done')
1
2
done
r2-p005 · Overridden .items/.keys/.values with side effects
1log = []
2class D(dict):
3 def items(self):
4 log.append('items')
5 return super().items()
6 def keys(self):
7 log.append('keys')
8 return super().keys()
9d = D({'a': 1})
10for k, v in d.items():
11 pass
12print(''.join(log))
items
r2-p006 · Lingering binding of unused variable after rewrite
1def f():
2 d = {'a': 1}
3 for k, v in d.items():
4 pass
5 try:
6 print(v)
7 except NameError as e:
8 return f'{type(e).__name__}: {e}'
9print(f())
1
None
r2-p007 · Frame locals missing value variable breaks debugger
1import sys
2def trace(frame, event, arg):
3 if event == 'line' and frame.f_locals.get('k') == 'a':
4 if 'v' in frame.f_locals:
5 print(f'v={frame.f_locals["v"]}')
6 else:
7 print("v missing")
8 return trace
9d = {'a': 1}
10sys.settrace(trace)
11for k, v in d.items():
12 pass
13sys.settrace(None)
r2-p008 · Bytes vs str encoding mismatch in write
1with open('test.bin', 'wb') as f:
2 for k, v in {b'a': b'hello', b'b': b'world'}.items():
3 f.write(v)
4with open('test.bin', 'rb') as f:
5 print(f.read())
6import nosuchmodule # This line is invalid but we catch it
b'helloworld'