ruff RUF019: fix changes program behavior
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
Output
no exception after get
(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
- related astral-sh/ruff/issues/14011 same rule RUF019 but different construct (general false positives vs specific .get() rewrite on non-dict)
- related astral-sh/ruff/issues/12953 same rule RUF019 but different construct (f-string effects vs .get() rewrite on non-dict)
- related astral-sh/ruff/issues/12285 same rule RUF019 but different construct (not operator handling vs .get() rewrite on non-dict)
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
--- 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.