fixhunt

ruff C413: fix changes program behavior

fix-changes-behaviorlabelled safereal · highrelated
Findings / 2026-10-17T20-21-26-758Z-0hmi-ruff-C413 / r2-p006ruff 0.17.113 → 12 lines2026-10-17 20:21 UTC

Hypothesis Nemotron 3 Super predicted to fire fired 1×

If the code relies on the fact that list(sorted(...)) creates a new list object (which it does), and sorted(...) also creates a new list, there is no behavioral difference—but if sorted were overridde

Program

before
1import builtins
2original_sorted = builtins.sorted
3cache = []
4def cached_sorted(*args, **kwargs):
5 if not cache:
6 cache.append(original_sorted(*args, **kwargs))
7 return cache[0]
8builtins.sorted = cached_sorted
9x = [3, 1, 2]
10a = list(sorted(x))
11b = sorted(x)
12print(a is b)
after
1import builtins
2original_sorted = builtins.sorted
3cache = []
4def cached_sorted(*args, **kwargs):
5 if not cache:
6 cache.append(original_sorted(*args, **kwargs))
7 return cache[0]
8builtins.sorted = cached_sorted
9x = [3, 1, 2]
10a = sorted(x)
11b = sorted(x)
12print(a is b)

Output

stdout beforeexit 0
False
stdout afterexit 0
True
fired
1 before · 0 after
runs
each version twice, same file name, no network

Judge Nemotron 3 Ultra advisory real · high

The rule documentation explicitly states the fix is safe for `list()` cases because it does not change behavior, but the program demonstrates that removing `list()` changes object identity when `sorted` is monkey-patched to return a cached instance. The rewrite alters runtime semantics in plausible code that intercepts the built-in function.

Explanation Nemotron 3 Super unverified

The original code calls `list(sorted(x))`, which creates a new list from the iterator returned by `sorted`. Since `cached_sorted` returns the same cached list object on every call (after the first), both `a` and `b` refer to that same list only if `a` does not copy it. However, `list(sorted(x))` produces a distinct list instance, so `a` is a copy while `b` is the cached list—making `a is b` false. After the fix, `a = sorted(x)` directly assigns the cached list to `a`, so both `a` and `b` reference the identical cached object, making `a is b` true. The change removes the list-copying step, altering object identity.

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 C413 --fix prog.py
python3 prog.py

Upstream search GitHub issues Tavily Nemotron 3 Super + Ultra

C413 fix · C413 list constructor redundant · ruff C413 unnecessary list call · list() around function call avoidable

  • related astral-sh/ruff/issues/10245 C414 issue about sorted(reversed(iterable)) and other safety concerns, different construct than list(sorted(...))
  • related astral-sh/ruff/issues/20632 C413 documentation about fix safety mentions stable sorting but not list() call removal changing identity
  • related astral-sh/ruff/issues/2772 C413 incorrect double reverse assumption, same rule but different construct (reversed(sorted(...)) vs list(sorted(...)))

Details

program
singleton_sorted_return_exposure
hazard
If the code relies on the fact that list(sorted(...)) creates a new list object (which it does), and sorted(...) also creates a new list, there is no behavioral difference—but if sorted were overridde
oracle
stdout before: False stdout after: True
run
2026-10-17T20-21-26-758Z-0hmi-ruff-C413
generated program (13 lines) and the fix's diff
1import builtins
2original_sorted = builtins.sorted
3cache = []
4def cached_sorted(*args, **kwargs):
5 if not cache:
6 cache.append(original_sorted(*args, **kwargs))
7 return cache[0]
8builtins.sorted = cached_sorted
9x = [3, 1, 2]
10a = list(sorted(x))
11b = sorted(x)
12print(a is b)
13print(a == b)
--- orig.py	2026-10-17 16:21:49.098491615 -0400
+++ fixed.py	2026-10-17 16:21:49.261490804 -0400
@@ -7,7 +7,7 @@
     return cache[0]
 builtins.sorted = cached_sorted
 x = [3, 1, 2]
-a = list(sorted(x))
+a = sorted(x)
 b = sorted(x)
 print(a is b)
 print(a == b)
rule documentation given to the models
# unnecessary-call-around-sorted (C413)

Derived from the **flake8-comprehensions** linter.

Fix is always available.

## What it does
Checks for unnecessary `list()` or `reversed()` calls around `sorted()`
calls.

## Why is this bad?
It is unnecessary to use `list()` around `sorted()`, as the latter already
returns a list.

It is also unnecessary to use `reversed()` around `sorted()`, as the latter
has a `reverse` argument that can be used in lieu of an additional
`reversed()` call.

In both cases, it's clearer and more efficient to avoid the redundant call.

## Example
```python
reversed(sorted(iterable))
```

Use instead:
```python
sorted(iterable, reverse=True)
```

## Fix safety
This rule's fix is marked as unsafe for `reversed()` cases, as `reversed()`
and `reverse=True` will yield different results in the event of custom sort
keys or equality functions. Specifically, `reversed()` will reverse the order
of the collection, while `sorted()` with `reverse=True` will perform a stable
reverse sort, which will preserve the order of elements that compare as
equal.

The fix is marked as safe for `list()` cases, as removing `list()` around
`sorted()` does not change the behavior.