ruff FURB129: fix changes program behavior
Hypothesis Nemotron 3 Super predicted to fire fired 1×
If the code relies on the list-specific behavior of `readlines()` output (e.g., checking `type(result)` or using list methods like `.append()` on the iterable), replacing it with the file object break
Program
Output
is list is list
is list
- fired
- 1 before · 0 after
- runs
- each version twice, same file name, no network
Judge Nemotron 3 Ultra advisory real · high
The fix rewrites the loop to iterate over the file object directly, which consumes the file iterator. The subsequent call to `fp.readlines()` inside the loop then returns an empty list because the file position is at EOF, changing the `isinstance` check from True to False and halving the output. The documentation presents the rewrite as equivalent for iteration but does not account for code that also calls `readlines()` on the same file object inside the loop.
Explanation Nemotron 3 Super unverified
The original loop iterates over `fp.readlines()`, which returns a new list of all lines each time it’s called; inside the loop body, another call to `fp.readlines()` reads from the file again, but since iteration has already consumed the iterator via the first readlines, subsequent calls return empty lists, yet still produce output because the condition checks type only—however, the outer loop runs once per line due to the initial list length. After rewriting to iterate directly over `fp`, the file iterator is advanced incrementally; each iteration consumes one line, leaving fewer unread lines, so later calls to `fp.readlines()` return shorter or empty lists, reducing the number of times the inner condition yields true and thus decreasing output. The change alters when and how file position advances, affecting repeated reads within the loop body.
Reproduce
- file
prog.py(the before program)- minimized program
- r2-p008.py
- evidence sheet
- r2-p008.md
- regression fixture
- r2-p008.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 FURB129 --fix prog.py python3 prog.py
Upstream search GitHub issues Tavily Nemotron 3 Super + Ultra
FURB129 fix · FURB129 file iteration readlines · Ruff rule FURB129 iterator optimization · Replace fp.readlines() with direct file iteration
- related astral-sh/ruff/issues/18843 same FURB129 rule but different defect about comment deletion
- related astral-sh/ruff/issues/17683 same FURB129 rule but different defect about missing whitespace
- related astral-sh/ruff/issues/18231 same FURB129 rule but different defect about parenthesized variable
Details
- program
- List-specific Behavior Reliance
- hazard
- If the code relies on the list-specific behavior of `readlines()` output (e.g., checking `type(result)` or using list methods like `.append()` on the iterable), replacing it with the file object break
- oracle
stdout before: is list is list stdout after: is list- run
- 2026-10-17T21-30-17-728Z-bxl4-ruff-FURB129
generated program (12 lines) and the fix's diff
--- orig.py 2026-10-17 17:30:51.161630325 -0400 +++ fixed.py 2026-10-17 17:30:51.326627691 -0400 @@ -2,7 +2,7 @@ with open('tmp.txt', 'w') as f: f.write('a\nb') with open('tmp.txt') as fp: - for line in fp.readlines(): + for line in fp: if isinstance(fp.readlines(), list): print('is list') print(line.strip())
rule documentation given to the models
# readlines-in-for (FURB129)
Derived from the **refurb** linter.
Fix is always available.
## What it does
Checks for uses of `readlines()` when iterating over a file line-by-line.
## Why is this bad?
Rather than iterating over all lines in a file by calling `readlines()`,
it's more convenient and performant to iterate over the file object
directly.
## Example
```python
with open("file.txt") as fp:
for line in fp.readlines():
...
```
Use instead:
```python
with open("file.txt") as fp:
for line in fp:
...
```
## Fix safety
This rule's fix is marked as unsafe if there's comments in the
`readlines()` call, as comments may be removed.
For example, the fix would be marked as unsafe in the following case:
```python
with open("file.txt") as fp:
for line in ( # comment
fp.readlines() # comment
):
...
```
## References
- [Python documentation: `io.IOBase.readlines`](https://docs.python.org/3/library/io.html#io.IOBase.readlines)