# ruff PLR1733: fix changes program behavior

Internal evidence sheet generated by fixhunt. Not an issue text. A human re-verifies, checks the duplicates
below, and writes the upstream report in their own words following the project's AI policy.

- Tool: ruff ruff 0.17.1
- Rule: PLR1733 (tool labels the fix: unknown, this fix reported as safe)
- Oracle: fix-changes-behavior
- Judge (Nemotron 3 Ultra, advisory): real, severity medium. The program uses a dict subclass that overrides __getitem__ to log each access. The rule's fix replaces d[k] with the loop variable v, which bypasses the overridden method and eliminates the log output. The documentation presents the rewrite as equivalent for ordinary dicts but does not account for subclasses with side effects in __getitem__.
- Program: minimized from 10 to 6 lines

## Reproducer

```python
class LogDict(dict):
    def __getitem__(self, key):
        print(f'GET {key}')
d = LogDict({'a': 1})
for k, v in d.items():
    _ = d[k]  # triggers PLR1733: logs again; fix would omit log
```

## Commands

```sh
python3 prog.py
ruff check --preview --isolated --select PLR1733 --fix prog.py
python3 prog.py
```

## Observed

```
stdout before:
GET a
stdout after:
```

## Diff applied by the fix

```diff
--- orig.py	2026-10-22 13:34:51.917444053 -0400
+++ fixed.py	2026-10-22 13:34:54.131481385 -0400
@@ -3,4 +3,4 @@
         print(f'GET {key}')
 d = LogDict({'a': 1})
 for k, v in d.items():
-    _ = d[k]  # triggers PLR1733: logs again; fix would omit log
+    _ = v  # triggers PLR1733: logs again; fix would omit log
```

## Why the rewrite is not equivalent (model draft, verify before use)

The original loop accesses each value via `d[k]`, which invokes `__getitem__` and prints 'GET a'. The fix replaces this with `_ = v`, using the already retrieved value from iteration without re-accessing the dictionary, so `__getitem__` is never called and nothing is printed. This changes observable behavior because the side effect of logging depends on dictionary access, which the fix eliminates. Thus, the rewrite is not semantics‑preserving—it removes a required side effect even though the variable `v` holds the same value as `d[k]` would return. The fix incorrectly assumes that avoiding re‑access preserves meaning, ignoring that the access itself performs work beyond value retrieval.

## Upstream search

Queries: "PLR1733 fix", "PLR1733 dict items iteration redundant access", "ruff PLR1733 unnecessary dictionary lookup during items loop", "dict.__getitem__ called on already retrieved value from items()"

- related: https://github.com/astral-sh/ruff/issues/25182
  same rule PLR1733 but different construct (else-clause access vs loop-body redundant indexing)
- related: https://github.com/astral-sh/ruff/issues/29422
  same rule PLR1733 mentioned among other fix bugs but no snippet shows this exact redundant-indexing pattern
- related: https://github.com/astral-sh/ruff/issues/25150
  same rule PLR1733 but different construct (else-clause false positive vs loop-body redundant indexing)

<details><summary>All hits</summary>

- [github, open] Additional false positives in `PLR1733` and `PLR1736` — https://github.com/astral-sh/ruff/issues/25182
- [github, open] A few more: missed reports vs flake8/pylint, plus two fix bugs (B012, F402, F823, SIM102, SIM401, PLW1641, PLR1736, PLW0406, N801, UP034) — https://github.com/astral-sh/ruff/issues/29422
- [github, closed] unused-function-argument not silenced by prefixed underscore if variable name is "次" — https://github.com/astral-sh/ruff/issues/28721
- [github, open] Implement Pylint — https://github.com/astral-sh/ruff/issues/970
- [github, closed] PLR1733 false positive — https://github.com/astral-sh/ruff/issues/25150
- [github, closed] Pack of fuzzer Autofix/Infinite loop errors [2] — https://github.com/astral-sh/ruff/issues/8402
- [tavily] PLC0206 should not flag if dict is being written · Issue #14585 · astral-sh/ruff · GitHub — https://github.com/astral-sh/ruff/issues/14585
- [tavily] dict-index-missing-items (PLC0206) should allow assignment by key · Issue #13821 · astral-sh/ruff · GitHub — https://github.com/astral-sh/ruff/issues/13821
- [tavily] [ty] fix query cycles in decorated function with parameter defaults by carljm · Pull Request #23014 · astral-sh/ruff · GitHub — https://github.com/astral-sh/ruff/pull/23014
- [tavily] [Feature Request] Sort dict keys · Issue #10085 · astral-sh/ruff · GitHub — https://github.com/astral-sh/ruff/issues/10085
- [tavily] [flake8-simplify] SIM910/SIM911 false negative if variable is named dict · Issue #18777 · astral-sh/ruff · GitHub — https://github.com/astral-sh/ruff/issues/18777
- [tavily] Style suggestion: Allow aligning dict values · Issue #10462 · astral-sh/ruff · GitHub — https://github.com/astral-sh/ruff/issues/10462
- [tavily] [ty] Fix iteration over intersections with TypeVars whose bounds contain non-iterable types by charliermarsh · Pull Request #22117 · astral-sh/ruff · GitHub — https://github.com/astral-sh/ruff/pull/22117
- [tavily] New Linting Rule: Prefer `setdefault` Over Conditional Key Checks for Dictionary Assignment · Issue #7897 · astral-sh/ruff · GitHub — https://github.com/astral-sh/ruff/issues/7897

</details>
