One function
JSONLIterator in mahmoud/boltons
The author described this change as “jsonutils: fix JSONLIterator nontermination at EOF and negative rel_seek”. It counts as a record because the check below fails on the code as it stood at 435774ef8 and passes on f1034b07d, with nothing else changed between the two runs.
Projectmahmoud/boltons
Fix saved2026-08-06
Sharing licenceBSD-2-Clause · LICENSE
Change size+7 −5
What the code was meant to do, written into the code itself as a docstring
The `JSONLIterator` is used to iterate over JSON-encoded objects stored in the `JSON Lines format`_ (one object per line). Most notably it has the ability to efficiently read from the bottom of files, making it very effective for reading in simple append-only JSONL use cases. It also has the ability to start from anywhere in the file and ignore corrupted lines. Args: file_obj (file): An open file object. ignore_errors (bool): Whether to skip over lines that raise an error on deserialization (:func:`json.loads`). reverse (bool): Controls the direction of the iteration. Defaults to `False`. If set to `True` and *rel_seek* is unset, seeks to the end of the file before iteration begins. rel_seek (float): Used to preseek the start position of iteration. Set to 0.0 for the start of the file, 1.0 for the end, and anything in between. .. _JSON Lines format: http://jsonlines.org/
The change
| 34 | 34 | raise ValueError("'rel_seek' expected a float between" | |
| 35 | 35 | " -1.0 and 1.0, not %r" % rel_seek) | |
| 36 | 36 | elif rel_seek < 0: | |
| 37 | - | rel_seek = 1.0 - rel_seek | |
| 37 | + | rel_seek = 1.0 + rel_seek | |
| 38 | 38 | self._rel_seek = rel_seek | |
| 39 | 39 | self._blocksize = 4096 | |
| 40 | 40 | if rel_seek is not None: | |
| ⋯ | |||
| 58 | 58 | cur_pos = fo.tell() | |
| 59 | 59 | while '\n' not in cur: | |
| 60 | 60 | cur = fo.read(bsize) | |
| 61 | + | if not cur: | |
| 62 | + | # no newline until EOF; a partial trailing line was | |
| 63 | + | # never yieldable from a mid-line seek anyway | |
| 64 | + | fo.seek(0, os.SEEK_END) | |
| 65 | + | return | |
| 61 | 66 | total_read += bsize | |
| 62 | - | try: | |
| 63 | - | newline_offset = cur.index('\n') + total_read - bsize | |
| 64 | - | except ValueError: | |
| 65 | - | raise # TODO: seek to end? | |
| 67 | + | newline_offset = cur.index('\n') + total_read - bsize | |
| 66 | 68 | fo.seek(cur_pos + newline_offset) | |
| 67 | 69 | ||
| 68 | 70 | def _init_rel_seek(self): | |
The check that tells the two apart
fail→pass·tests/test_jsonutils.py::test_jsonl_iterator_mid_last_line_seek_terminates
Check file tests/test_jsonutils.py, taken without changes from the fix and copied onto the older code, so the exact same check runs against both versions.
Origin and history
The code before it435774ef8b10c1355bf77483a837945034011754
Broken version dated2026-08-06
Moduleboltons.jsonutils
Units changedJSONLIterator
Fingerprint76ad3601e9b14a72
Checked2026-08-18 by goldset/0.1
Every field above is generated by our program. None of it is written by hand.
Other bugs found in mahmoud/boltons
- 2026-08-06Bits
- 2026-08-06tableutils: fix Table.to_text crashes on degenerate tables and headers
- 2026-07-18backoff_iter
- 2026-07-17singularize
- 2026-07-17fix(fileutils): accept os.PathLike in AtomicSaver
- 2026-07-17Fix copy.copy/copy.deepcopy collapsing OrderedMultiDict values