fixhunt

ruff RUF019: fix changes program behavior

fix-changes-behaviorlabelled safereal · highrelated
Findings / 2026-10-17T21-39-23-080Z-ah8u-ruff-RUF019 / r1-p007ruff 0.17.126 → 14 lines2026-10-17 21:39 UTC

Hypothesis Nemotron 3 Super predicted to fire fired 1×

If d["x"] raises an exception (e.g. ValueError), the original is guarded by 'in', but .get() does not suppress it—though for missing keys it returns None. Hazard arises if 'in' is removed and .get() n

Program

before
1class D:
2 def __contains__(self, k): return k == 'x'
3 def __getitem__(self, k):
4 if k == 'x': raise ValueError('bad')
5 try:
6 return self[k]
7 except ValueError:
8 return default
9d = D()
10try:
11 if "x" in d and d["x"]:
12 print("yes after get")
13except ValueError as e:
14 print("no exception after get")
after
1class D:
2 def __contains__(self, k): return k == 'x'
3 def __getitem__(self, k):
4 if k == 'x': raise ValueError('bad')
5 try:
6 return self[k]
7 except ValueError:
8 return default
9d = D()
10try:
11 if d.get("x"):
12 print("yes after get")
13except ValueError as e:
14 print("no exception after get")

Output

stdout beforeexit 0
no exception after get
stdout afterexit 1
(no stdout)
AttributeError: 'D' object has no attribute 'get'
fired
1 before · 0 after
runs
each version twice, same file name, no network

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

Explanation Nemotron 3 Super unverified

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.

Reproduce

file
prog.py (the before program)
minimized program
r1-p007.py
evidence sheet
r1-p007.md
regression fixture
r1-p007.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 membership access pattern · RUF019 replace in-and-get with get method call · RUF019 unsafe dictionary key access transformation

Details

program
Exceptions inside rewritten code with finally/else
hazard
If d["x"] raises an exception (e.g. ValueError), the original is guarded by 'in', but .get() does not suppress it—though for missing keys it returns None. Hazard arises if 'in' is removed and .get() n
oracle
stdout before: no exception after get stdout after: exit 0 → 1 stderr before: (none) stderr after: AttributeError: 'D' object has no attribute 'get'
run
2026-10-17T21-39-23-080Z-ah8u-ruff-RUF019
generated program (26 lines) and the fix's diff
1class D:
2 def __contains__(self, k): return k == 'x'
3 def __getitem__(self, k):
4 if k == 'x': raise ValueError('bad')
5 return None
6 def get(self, k, default=None):
7 try:
8 return self[k]
9 except ValueError:
10 return default
11
12d = D()
13try:
14 if "x" in d and d["x"]:
15 print("yes")
16except ValueError as e:
17 print(f"caught: {e}")
18else:
19 print("no exception")
20try:
21 if d.get("x"):
22 print("yes after get")
23except ValueError as e:
24 print(f"caught after get: {e}")
25else:
26 print("no exception after get")
--- orig.py	2026-10-17 17:39:38.076512087 -0400
+++ fixed.py	2026-10-17 17:39:38.239510700 -0400
@@ -11,7 +11,7 @@
 
 d = D()
 try:
-    if "x" in d and d["x"]:
+    if d.get("x"):
         print("yes")
 except ValueError as e:
     print(f"caught: {e}")
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.