fixhunt

ruff RUF019: fix changes program behavior

fix-changes-behaviorlabelled safereal · highrelated
Findings / 2026-10-17T21-39-23-080Z-ah8u-ruff-RUF019 / r2-p008ruff 0.17.110 → 6 lines2026-10-17 21:39 UTC

Hypothesis Nemotron 3 Super predicted to fire fired 1×

8. If d is a subclass of dict that overrides __contains__ or __getitem__ with side effects, the fix may alter behavior.

Program

before
1class LoggingDict(dict):
2 def __contains__(self, key):
3 print(f'__contains__({key})')
4d = LoggingDict({'x': 1})
5if 'x' in d and d['x']:
6 print('condition passed')
after
1class LoggingDict(dict):
2 def __contains__(self, key):
3 print(f'__contains__({key})')
4d = LoggingDict({'x': 1})
5if d.get('x'):
6 print('condition passed')

Output

stdout beforeexit 0
__contains__(x)
stdout afterexit 0
condition passed
fired
1 before · 0 after
runs
each version twice, same file name, no network

Judge Nemotron 3 Ultra advisory real · 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.

Explanation Nemotron 3 Super unverified

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.

Reproduce

file
prog.py (the before program)
minimized program
r2-p008.py
evidence sheet
r2-p008.md
regression fixture
r2-p008.py the minimized program with a header stating the observed change, for the tool's test suite
python3 prog.py
ruff check --preview --isolated --select RUF019 --fix prog.py
python3 prog.py

Upstream search GitHub issues Tavily Nemotron 3 Super + Ultra

RUF019 fix · RUF019 dict get truthiness · LoggingDict contains method call · in operator replaced with get

Details

program
Dict subclass overriding __contains__ and __getitem__ with side effects
hazard
8. If d is a subclass of dict that overrides __contains__ or __getitem__ with side effects, the fix may alter behavior.
oracle
stdout before: __contains__(x) stdout after: condition passed
run
2026-10-17T21-39-23-080Z-ah8u-ruff-RUF019
generated program (10 lines) and the fix's diff
1class LoggingDict(dict):
2 def __contains__(self, key):
3 print(f'__contains__({key})')
4 return super().__contains__(key)
5 def __getitem__(self, key):
6 print(f'__getitem__({key})')
7 return super().__getitem__(key)
8d = LoggingDict({'x': 1})
9if 'x' in d and d['x']:
10 print('condition passed')
--- orig.py	2026-10-17 17:39:47.030436187 -0400
+++ fixed.py	2026-10-17 17:39:47.188434854 -0400
@@ -6,5 +6,5 @@
         print(f'__getitem__({key})')
         return super().__getitem__(key)
 d = LoggingDict({'x': 1})
-if 'x' in d and d['x']:
+if d.get('x'):
     print('condition passed')
rule documentation given to the models
# unnecessary-key-check (RUF019)

Derived from the **Ruff-specific rules** linter.

Fix is always available.

## What it does
Checks for unnecessary key checks prior to accessing a dictionary.

## Why is this bad?
When working with dictionaries, the `get` can be used to access a value
without having to check if the dictionary contains the relevant key,
returning `None` if the key is not present.

## Example
```python
if "key" in dct and dct["key"]:
    ...
```

Use instead:
```python
if dct.get("key"):
    ...
```

## Fix safety
This rule's fix is marked as safe, unless the expression contains comments
or may have side effects.