# 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 target object has a `get` method like a standard dict, but the program uses a custom class `D` that implements `__contains__` and `__getitem__` without providing `get`. After the fix, calling `d.get("x")` raises an AttributeError, changing the program's behavior from catching a ValueError to crashing with an AttributeError.
- Program: minimized from 26 to 14 lines

## Reproducer

```python
class D:
    def __contains__(self, k): return k == 'x'
    def __getitem__(self, k):
        if k == 'x': raise ValueError('bad')
        try:
            return self[k]
        except ValueError:
            return default
d = D()
try:
    if "x" in d and d["x"]:
        print("yes after get")
except ValueError as e:
    print("no exception after get")
```

## Commands

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

## Observed

```
stdout before:
no exception after get
stdout after:

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

## Diff applied by the fix

```diff
--- orig.py	2026-10-22 13:43:38.465278058 -0400
+++ fixed.py	2026-10-22 13:43:40.378862388 -0400
@@ -8,7 +8,7 @@
             return default
 d = D()
 try:
-    if "x" in d and d["x"]:
+    if d.get("x"):
         print("yes after get")
 except ValueError as e:
     print("no exception after get")
```

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

The original condition checks membership via `__contains__` then accesses `d["x"]`, which raises `ValueError` caught by the outer `try`. The rewritten expression uses `d.get("x")`, but class `D` defines no `get` method, causing an `AttributeError` that propagates uncaught, changing both output and exit status. The transformation assumes a mapping interface with `get`, which `D` does not provide.

## Upstream search

Queries: "RUF019 fix", "RUF019 dict membership access pattern", "RUF019 replace in-and-get with get method call", "RUF019 unsafe dictionary key access transformation"

- related: https://github.com/astral-sh/ruff/issues/14011
  same rule RUF019 but different construct (general false positives vs specific .get() rewrite on non-dict)
- related: https://github.com/astral-sh/ruff/issues/12953
  same rule RUF019 but different construct (f-string effects vs .get() rewrite on non-dict)
- related: https://github.com/astral-sh/ruff/issues/12285
  same rule RUF019 but different construct (not operator handling vs .get() rewrite on non-dict)

<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>
