# ruff RUF019: 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: RUF019 (tool labels the fix: unknown, this fix reported as safe)
- Oracle: fix-changes-behavior
- Judge (Nemotron 3 Ultra, advisory): real, severity high. The rule assumes the object is a dict-like with a .get method, but the program uses a custom class that implements __contains__ and __getitem__ without providing .get. The rewrite replaces a working containment check and subscript access with a call to a missing method, causing an AttributeError.
- Program: minimized from 15 to 11 lines

## Reproducer

```python
import os
import tempfile
with tempfile.TemporaryDirectory() as td:
    p = os.path.join(td, 'link')
    class P:
        def __contains__(self, k): return os.path.exists(p) if k == 'x' else False
        def __getitem__(self, k):
            raise KeyError(k)
    d = P()
    if "x" in d and d["x"]:
        print("falsy")
```

## Commands

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

## Observed

```
exit 0 → 1
stderr before: (none)
stderr after: AttributeError: 'P' object has no attribute 'get'
```

## Diff applied by the fix

```diff
--- orig.py	2026-10-22 13:43:18.714383557 -0400
+++ fixed.py	2026-10-22 13:43:21.758204671 -0400
@@ -7,5 +7,5 @@
         def __getitem__(self, k):
             raise KeyError(k)
     d = P()
-    if "x" in d and d["x"]:
+    if d.get("x"):
         print("falsy")
```

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

The rewrite replaces `if \"x\" in d and d[\"x\"]:` with `if d.get(\"x\"):`. However, class `P` defines `__contains__` but does not define a `get` method, so calling `d.get(\"x\")` raises an `AttributeError`, changing the program’s behavior from printing nothing to crashing. The original condition safely checks membership via `__contains__` and then accesses the item only if present; the rewrite assumes a dictionary-like interface that `P` does not provide. Thus, the transformation is not semantics‑preserving for this user‑defined type.

## Upstream search

Queries: "RUF019 fix", "RUF019 dict get method call", "ruff RUF019 replace membership test", "rule RUF019 avoid redundant key lookup"

- related: https://github.com/astral-sh/ruff/issues/14011
  same rule RUF019 but different construct (general false positives vs custom mapping without get)
- related: https://github.com/astral-sh/ruff/issues/12953
  same rule RUF019 but different construct (f-string side effects vs custom mapping without get)
- related: https://github.com/astral-sh/ruff/issues/12285
  same rule RUF019 but different construct (not operator handling vs custom mapping without get)

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

- [github, open] Possible false positives for RUF019 — https://github.com/astral-sh/ruff/issues/14011
- [github, closed] unused-function-argument not silenced by prefixed underscore if variable name is "次" — https://github.com/astral-sh/ruff/issues/28721
- [github, closed] `contains_effect` should consider f-strings to have effects — https://github.com/astral-sh/ruff/issues/12953
- [github, closed] False negative for RUF019 with `not` operator — https://github.com/astral-sh/ruff/issues/12285
- [github, closed] Fixed code unformatted — https://github.com/astral-sh/ruff/issues/8619
- [github, closed] Add option to disable preview warnings — https://github.com/astral-sh/ruff/issues/8620

</details>
