ruff RUF019: fix changes program behavior
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
Output
(no stdout)
(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
- related astral-sh/ruff/issues/14011 same rule RUF019 but different construct (general false positives vs custom mapping without get)
- related astral-sh/ruff/issues/12953 same rule RUF019 but different construct (f-string side effects vs custom mapping without get)
- related astral-sh/ruff/issues/12285 same rule RUF019 but different construct (not operator handling vs custom mapping without get)
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
--- 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.