fixhunt

ruff F632: fix changes program behavior

fix-changes-behaviorlabelled safeintended-change · highduplicate
Findings / 2026-10-17T20-54-08-790Z-5ytj-ruff-F632 / r2-p006ruff 0.17.118 → 6 lines2026-10-17 20:54 UTC

Hypothesis Nemotron 3 Super predicted to fire fired 1×

If the literal is a mutable compound like [] and x is a local variable, 'x is []' is False unless x is that exact list, but 'x == []' may be True for empty lists—fix corrects mistaken identity use, bu

Program

before
1cache = []
2def get_cache():
3 return cache
4x = get_cache()
5if x is []:
6 print('cache hit')
after
1cache = []
2def get_cache():
3 return cache
4x = get_cache()
5if x == []:
6 print('cache hit')

Output

stdout beforeexit 0
(no stdout)
stdout afterexit 0
cache hit
fired
1 before · 0 after
runs
each version twice, same file name, no network

Judge Nemotron 3 Ultra advisory intended-change · high

The rule rewrites 'x is []' to 'x == []' because identity comparison with a list literal is always False; the program demonstrates the semantic change by printing 'cache hit' only after the fix, which is exactly the behavior the rule documents as correct.

Explanation Nemotron 3 Super unverified

The original condition `x is []` checks whether `x` refers to the exact same list object as the newly created literal `[]`. Since each evaluation of `[]` produces a distinct list object, even if two lists have identical contents, they are different objects in memory. Therefore, `x is []` evaluates to False when `x` is the list returned by `get_cache()`, which is the same object as `cache`. After the rewrite, `x == []` compares the contents of the lists for equality. Because both lists are empty, this comparison returns True, causing the print statement to execute where it previously did not. This change in behavior occurs because identity (`is`) and equality (`==`) are fundamentally different operations in Python. The fix incorrectly assumes they are interchangeable here, but they are not—especially when dealing with mutable objects like lists that can have equal contents without being the same object. Thus, the transformation alters the program’s semantics.

Reproduce

file
prog.py (the before program)
minimized program
r2-p006.py
evidence sheet
r2-p006.md
regression fixture
r2-p006.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 F632 --fix prog.py
python3 prog.py

Upstream search GitHub issues Tavily Nemotron 3 Super + Ultra

F632 fix · ruff F632 identity comparison list literal · F632 replace is with equals empty list · rule F632 fix identity check to equality

  • related astral-sh/ruff/issues/12518 Same F632 rule but different construct: False == 0 boolean/integer comparison vs list identity check.
  • related astral-sh/ruff/issues/11736 Same F632 rule but different trigger: parser panic on invalid syntax vs valid list identity comparison.
  • related astral-sh/ruff/issues/9374 Same F632 rule but different construct: chained comparison loop (c > 0 == None) vs simple list identity check.
  • duplicate astral-sh/ruff/issues/8589 Same F632 rule and same construct: 'variable is []' list identity comparison incorrectly rewritten to '=='.
  • related astral-sh/ruff/issues/6891 Same F632 rule but different construct: complex generator expression comparison causing infinite loop vs simple list identity.
  • related astral-sh/ruff/issues/1057 Same F632 rule but different construct: modulo comparison 'value % 2 is 0' causing CST extraction failure vs list identity.

Details

program
Mutable List Identity vs Equality
hazard
If the literal is a mutable compound like [] and x is a local variable, 'x is []' is False unless x is that exact list, but 'x == []' may be True for empty lists—fix corrects mistaken identity use, bu
oracle
stdout before: stdout after: cache hit
run
2026-10-17T20-54-08-790Z-5ytj-ruff-F632
generated program (18 lines) and the fix's diff
1a = []
2b = []
3if a is []:
4 print('a is new list')
5else:
6 print('a is not new list')
7if a == []:
8 print('a is empty')
9else:
10 print('a is not empty')
11cache = []
12def get_cache():
13 return cache
14x = get_cache()
15if x is []:
16 print('cache hit')
17else:
18 print('cache miss')
--- orig.py	2026-10-17 16:54:37.612970517 -0400
+++ fixed.py	2026-10-17 16:54:37.771972979 -0400
@@ -1,6 +1,6 @@
 a = []
 b = []
-if a is []:
+if a == []:
   print('a is new list')
 else:
   print('a is not new list')
@@ -12,7 +12,7 @@
 def get_cache():
   return cache
 x = get_cache()
-if x is []:
+if x == []:
   print('cache hit')
 else:
   print('cache miss')
rule documentation given to the models
# is-literal (F632)

Derived from the **Pyflakes** linter.

Fix is always available.

## What it does
Checks for `is` and `is not` comparisons against literals, like integers,
strings, or lists.

## Why is this bad?
The `is` and `is not` comparators operate on identity, in that they check
whether two objects are the same object. If the objects are not the same
object, the comparison will always be `False`. Using `is` and `is not` with
constant literals often works "by accident", but are not guaranteed to produce
the expected result.

As of Python 3.8, using `is` and `is not` with constant literals will produce
a `SyntaxWarning`.

This rule will also flag `is` and `is not` comparisons against non-constant
literals, like lists, sets, and dictionaries. While such comparisons will
not raise a `SyntaxWarning`, they are still likely to be incorrect, as they
will compare the identities of the objects instead of their values, which
will always evaluate to `False`.

Instead, use `==` and `!=` to compare literals, which will compare the
values of the objects instead of their identities.

## Example
```python
x = 200
if x is 200:
    print("It's 200!")
```

Use instead:
```python
x = 200
if x == 200:
    print("It's 200!")
```

## References
- [Python documentation: Identity comparisons](https://docs.python.org/3/reference/expressions.html#is-not)
- [Python documentation: Value comparisons](https://docs.python.org/3/reference/expressions.html#value-comparisons)
- [_Why does Python log a SyntaxWarning for ‘is’ with literals?_ by Adam Johnson](https://adamj.eu/tech/2020/01/21/why-does-python-3-8-syntaxwarning-for-is-literal/)