# 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 replaces an explicit key check (`'x' in d`) followed by access (`d['x']`) with a single `d.get('x')` call. In this program, the dictionary subclass overrides `__contains__` to produce a side effect (printing). The original code invokes `__contains__`, so the side effect occurs; the rewritten code calls `get`, which does not invoke `__contains__`, so the side effect is lost and the program's observable behavior changes silently.
- Program: minimized from 10 to 6 lines

## Reproducer

```python
class LoggingDict(dict):
    def __contains__(self, key):
        print(f'__contains__({key})')
d = LoggingDict({'x': 1})
if 'x' in d and d['x']:
    print('condition passed')
```

## Commands

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

## Observed

```
stdout before:
__contains__(x)
stdout after:
condition passed
```

## Diff applied by the fix

```diff
--- orig.py	2026-10-22 13:43:54.010646894 -0400
+++ fixed.py	2026-10-22 13:43:55.931430934 -0400
@@ -2,5 +2,5 @@
     def __contains__(self, key):
         print(f'__contains__({key})')
 d = LoggingDict({'x': 1})
-if 'x' in d and d['x']:
+if d.get('x'):
     print('condition passed')
```

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

The original condition evaluates `'x' in d` first, which triggers `__contains__`, printing '__contains__(x)'. Since this returns True, it then checks `d['x']`, retrieving the value 1 (truthy), so the message prints. The rewrite replaces both with `d.get('x')`, which directly fetches the value without calling `__contains__`. Thus, no print occurs, and only 'condition passed' appears—altering observable behavior. This change is unsafe because `__contains__` may have side effects beyond membership testing, which the fix ignores. The transformation assumes equivalence that does not hold when `__contains__` is overridden.

## Upstream search

Queries: "RUF019 fix", "RUF019 dict get truthiness", "LoggingDict contains method call", "in operator replaced with get"

- related: https://github.com/astral-sh/ruff/issues/14011
  same rule RUF019 but different construct (general false positives vs side-effectful __contains__)
- related: https://github.com/astral-sh/ruff/issues/12953
  same rule RUF019 and same root cause (contains_effect missing side-effects)

<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
- [github, open] needless bool for needless-bool — https://github.com/astral-sh/ruff/issues/15323
- [github, open] new rule - replace `d.update({'key': 'value'})` with `d['key'] = 'value'` — https://github.com/astral-sh/ruff/issues/13533
- [github, open] Support for refurb — https://github.com/astral-sh/ruff/issues/1348
- [github, open] Feature: Port Sonar's Python scanner checks — https://github.com/astral-sh/ruff/issues/4935
- [github, open] Implement Pylint — https://github.com/astral-sh/ruff/issues/970
- [github, closed] UP032: Don't suggest f-string inside logging statements — https://github.com/astral-sh/ruff/issues/13916
- [github, closed] `FURB118` is incorrector for `operator.contains`. The operands are reversed! — https://github.com/astral-sh/ruff/issues/9191
- [github, closed] [Infinite loop] My File: pandas\tests\io\test_common.py — https://github.com/astral-sh/ruff/issues/4281

</details>
