Repository navigation
Skip empty Arrow record batches and move the fetchmany() clamp #1222
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
8a868f4
6b8612c
58ced3e
9809e33
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -197,24 +197,27 @@ def converters(self) -> dict[str, Callable[[str | None], Any | None]]: | |
|
|
||
| @override | ||
| def _fetch(self) -> None: | ||
| try: | ||
| rows = next(self._batches) | ||
| except StopIteration: | ||
| """Buffer the rows of the next record batch that has any. | ||
|
|
||
| A table can hold empty record batches between others, such as one of | ||
| an empty result file, which would otherwise end the fetches early. | ||
| """ | ||
| rows = next((batch for batch in self._batches if batch.num_rows), None) | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Independent review (relayed Codex result). Codex CLI 0.162.1, model |
||
| if rows is None: | ||
| return | ||
| # Read the columns and their converters by position; to_pydict() and the | ||
| # converters property keep one column per name. | ||
| columns = [column.to_pylist() for column in rows.columns] | ||
| description = self.description if self.description else [] | ||
| converters = [self._converter.get(d[1]) for d in description] | ||
| if any(convert is not _to_default for convert in converters): | ||
| processed_rows = [ | ||
| tuple(convert(v) for convert, v in zip(converters, row, strict=False)) | ||
| for row in zip(*columns, strict=False) | ||
| ] | ||
| else: | ||
| # Read the columns and their converters by position; to_pydict() and the | ||
| # converters property keep one column per name. | ||
| columns = [column.to_pylist() for column in rows.columns] | ||
| description = self.description if self.description else [] | ||
| converters = [self._converter.get(d[1]) for d in description] | ||
| if any(convert is not _to_default for convert in converters): | ||
| processed_rows = [ | ||
| tuple(convert(v) for convert, v in zip(converters, row, strict=False)) | ||
| for row in zip(*columns, strict=False) | ||
| ] | ||
| else: | ||
| processed_rows = list(zip(*columns, strict=False)) | ||
| self._rows.extend(processed_rows) | ||
| processed_rows = list(zip(*columns, strict=False)) | ||
| self._rows.extend(processed_rows) | ||
|
|
||
| def _open_csv(self, location: str) -> Any: | ||
| """Open the CSV result file for ``pyarrow.csv.read_csv()``. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,6 +5,7 @@ | |
| import csv | ||
| import importlib | ||
| import logging | ||
| import sys | ||
| import threading | ||
| from collections import abc | ||
| from collections.abc import Callable, Iterable, Iterator | ||
|
|
@@ -992,8 +993,9 @@ def _take_rows(self, size: int | None) -> list[tuple[Any | None, ...] | dict[Any | |
| """ | ||
| rows: list[tuple[Any | None, ...] | dict[Any, Any | None]] = [] | ||
| try: | ||
| # extend() keeps the rows that it appended before an exception. | ||
| rows.extend(islice(self._row_iter, size)) | ||
| # extend() keeps the rows that it appended before an exception, and | ||
| # islice() takes no size above sys.maxsize. | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Self-review round 2 (claims, callers, docs, evidence). Base
|
||
| rows.extend(islice(self._row_iter, None if size is None else min(size, sys.maxsize))) | ||
| finally: | ||
| if rows: | ||
| self._count_rows(len(rows)) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Self-review round 1 (behavior, failure paths, simplicity, tests). Base
b2d7aa24b00a204e9e150952c708e3bb1126f9e2, head9809e3312b116d50e0cb98d177b848e32de99c8f. Inputs: my pass and/code-review highon the earlier head6b8612c3(7 findings). Result: FINDINGS, resolved by narrowing./code-reviewfindings on the rows returned to the buffer (an interrupt between taking and counting rows, a concurrentclose()) and on the token reordering (Arrow/S3FS not covered, a deterministic conversion error repeating its request, a test checking only the private token) were verified from the source. The maintainer chose, after a consultation with Codex andclaude-fable-5-1that disagreed (unspecified vs. retryable), to leave the behavior after a fetch error unspecified here and to decide one contract for all result sets in Define the behavior of result set fetches after an error #1225; both changes are removed (58ced3e, 9809e33)._batchesis an iterator, so the generator expression resumes after the batches it consumed;close()replaces it with an empty iterator. The test asserts that the table has an empty batch before each case (pyarrow'sto_batches()yields none after the last rows, so leading, middle, and consecutive cases are covered) and fails on master in all 12 cases.