# 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 has a `get` method like a standard dict, but the program uses a custom mapping class that implements `__contains__` and `__getitem__` without providing `get`. The rewrite replaces a working `in`/`__getitem__` protocol with a call to a missing method, causing an AttributeError and changing the exit status from 0 to 1.
- Program: minimized from 17 to 11 lines

## Reproducer

```python
class SideEffectMap:
    def __init__(self):
        self.data = {'x': 1}
        self.calls = 0
    def __contains__(self, key):
        return key in self.data
    def __getitem__(self, key):
        return self.data[key]
m = SideEffectMap()
if 'x' in m and m['x']:
    print(f'second: calls={m.calls}')
```

## Commands

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

## Observed

```
stdout before:
second: calls=0
stdout after:

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

## Diff applied by the fix

```diff
--- orig.py	2026-10-22 13:43:45.221930762 -0400
+++ fixed.py	2026-10-22 13:43:47.138607553 -0400
@@ -7,5 +7,5 @@
     def __getitem__(self, key):
         return self.data[key]
 m = SideEffectMap()
-if 'x' in m and m['x']:
+if m.get('x'):
     print(f'second: calls={m.calls}')
```

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

The original condition evaluates `'x' in m`, which invokes `__contains__` to check membership without side effects; if true, it then accesses `m['x']` via `__getitem__`. The rewritten expression uses `m.get('x')`, but `SideEffectMap` lacks a `get` method, causing an `AttributeError`. Since the left operand of `and` was true (`'x'` present), the original proceeded to evaluate `m['x']`, printing the message with zero calls. The fix replaces a valid two-part test with an undefined method call, altering both control flow and error behavior. This is not semantics‑preserving because it changes what operations are performed and introduces an exception where none occurred before.

## Upstream search

Queries: "RUF019 fix", "RUF019 membership test dict get", "RUF019 boolean condition simplification", "RUF019 truthy value check optimization"

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

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