fixhunt

ruff RUF019: fix changes program behavior

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

Hypothesis Nemotron 3 Super predicted to fire fired 1×

Path.exists() and os.path.exists() differ on broken symlinks; the fix assumes dict-like truthiness but Path objects have different semantics.

Program

before
1import os
2import tempfile
3with tempfile.TemporaryDirectory() as td:
4 p = os.path.join(td, 'link')
5 class P:
6 def __contains__(self, k): return os.path.exists(p) if k == 'x' else False
7 def __getitem__(self, k):
8 raise KeyError(k)
9 d = P()
10 if "x" in d and d["x"]:
11 print("falsy")
after
1import os
2import tempfile
3with tempfile.TemporaryDirectory() as td:
4 p = os.path.join(td, 'link')
5 class P:
6 def __contains__(self, k): return os.path.exists(p) if k == 'x' else False
7 def __getitem__(self, k):
8 raise KeyError(k)
9 d = P()
10 if d.get("x"):
11 print("falsy")

Output

stdout beforeexit 0
(no stdout)
stdout afterexit 1
(no stdout)
AttributeError: 'P' 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 object is a dict-like with a .get method, but the program uses a custom class that implements __contains__ and __getitem__ without providing .get. The rewrite replaces a working containment check and subscript access with a call to a missing method, causing an AttributeError.

Explanation Nemotron 3 Super unverified

The rewrite replaces `if \"x\" in d and d[\"x\"]:` with `if d.get(\"x\"):`. However, class `P` defines `__contains__` but does not define a `get` method, so calling `d.get(\"x\")` raises an `AttributeError`, changing the program’s behavior from printing nothing to crashing. The original condition safely checks membership via `__contains__` and then accesses the item only if present; the rewrite assumes a dictionary-like interface that `P` does not provide. Thus, the transformation is not semantics‑preserving for this user‑defined type.

Reproduce

file
prog.py (the before program)
minimized program
r1-p002.py
evidence sheet
r1-p002.md
regression fixture
r1-p002.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 method call · ruff RUF019 replace membership test · rule RUF019 avoid redundant key lookup

Details

program
pathlib vs os.path: existence and symlink sensitivity
hazard
Path.exists() and os.path.exists() differ on broken symlinks; the fix assumes dict-like truthiness but Path objects have different semantics.
oracle
exit 0 → 1 stderr before: (none) stderr after: AttributeError: 'P' object has no attribute 'get'
run
2026-10-17T21-39-23-080Z-ah8u-ruff-RUF019
generated program (15 lines) and the fix's diff
1import os
2import tempfile
3with tempfile.TemporaryDirectory() as td:
4 p = os.path.join(td, 'link')
5 os.symlink('nonexistent', p)
6 class P:
7 def __contains__(self, k): return os.path.exists(p) if k == 'x' else False
8 def __getitem__(self, k):
9 if k == 'x': return p
10 raise KeyError(k)
11 d = P()
12 if "x" in d and d["x"]:
13 print("truthy")
14 else:
15 print("falsy")
--- orig.py	2026-10-17 17:39:36.693523869 -0400
+++ fixed.py	2026-10-17 17:39:36.911522010 -0400
@@ -9,7 +9,7 @@
             if k == 'x': return p
             raise KeyError(k)
     d = P()
-    if "x" in d and d["x"]:
+    if d.get("x"):
         print("truthy")
     else:
         print("falsy")
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.