-
Notifications
You must be signed in to change notification settings - Fork 116
Read columns with the same name by their own types from CSV result files #1066
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
6af16a0
936a692
e1567d1
af023a2
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 |
|---|---|---|
|
|
@@ -429,6 +429,32 @@ class AthenaPandasResultSet(AthenaResultSet): | |
| ] | ||
| # The pandas.read_csv() options given to execute() that _read_csv_with_pyarrow() reads. | ||
| _PYARROW_READ_CSV_OPTIONS: ClassVar[frozenset[str]] = frozenset({"dtype", "parse_dates"}) | ||
| # The pandas.read_csv() options given to execute() that do not change how pandas reads | ||
| # the header row, with which _read_csv_header_as_labels() replaces the header. | ||
| _LABELED_HEADER_READ_CSV_OPTIONS: ClassVar[frozenset[str]] = frozenset( | ||
|
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 follow-up review (relayed): Codex CLI 0.160.0, model gpt-6-astra, reasoning effort max, Round 1, session
Findings 2 and 3 were regressions from master for those options; I verified both offline. Repaired in af023a2: the header is replaced only when every option given to Repair self-review (both perspectives):
Round 2, session |
||
| { | ||
| "cache_dates", | ||
| "converters", | ||
| "date_format", | ||
| "dayfirst", | ||
| "decimal", | ||
| "dtype_backend", | ||
| "false_values", | ||
| "float_precision", | ||
| "index_col", | ||
| "keep_default_na", | ||
| "low_memory", | ||
| "na_filter", | ||
| "na_values", | ||
| "nrows", | ||
| "on_bad_lines", | ||
| "parse_dates", | ||
| "skipfooter", | ||
| "thousands", | ||
| "true_values", | ||
| "usecols", | ||
| } | ||
| ) | ||
|
|
||
| def __init__( | ||
| self, | ||
|
|
@@ -808,6 +834,9 @@ def _read_csv(self) -> TextFileReader | DataFrame: | |
| with ExitStack() as stack: | ||
| source: str | IOBase = self.output_location | ||
| binary_columns = self._configure_binary_csv_read(read_csv_kwargs, labels) | ||
| if labels is not None: | ||
| # After _configure_binary_csv_read(), which checks for the header row. | ||
| self._read_csv_header_as_labels(read_csv_kwargs, csv_engine) | ||
| self._csv_converters = read_csv_kwargs.get("converters") or {} | ||
| if binary_columns: | ||
| # Given storage_options, even None, open the file through fsspec | ||
|
|
@@ -1058,6 +1087,41 @@ def _key_csv_columns_by_labels( | |
| ] | ||
| self._time_columns = [label for label, d in columns if d[1] == "time"] | ||
|
|
||
| def _read_csv_header_as_labels(self, read_csv_kwargs: dict[str, Any], csv_engine: str) -> 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. Self-review round one (behavior and implementation) — base Covered:
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 two (claims, callers, operations, evidence): base Claims checked:
Callers and operations: there are no API signature or default changes. Public Corrections: the TEST section claimed 6af16a0 for a run that preceded a comment-only edit, and did not cover the repair head. It now lists the run per revision (473 on the pre-comment tree; 119 Arrow sync and async tests on 936a692). "Measured with this branch" became a dated Athena measurement, and the Polars evidence was added. |
||
| """Read the header of the CSV result file as the labels of columns with the same name. | ||
|
|
||
| pandas gives a column that it renames, such as ``x.1``, the dtype of the first | ||
| column with the name when it has none of its own. When PyAthena builds the | ||
| dtypes, the columns are read under their labels, so that each keeps its own type. | ||
|
|
||
| Args: | ||
| read_csv_kwargs: The options for ``pandas.read_csv()``, updated in place. | ||
| csv_engine: The CSV engine that reads the file. | ||
| """ | ||
| import pandas as pd | ||
|
|
||
| names = [d[0] for d in self.description or []] | ||
| if len(set(names)) == len(names): | ||
| return | ||
| if not self._kwargs.keys() <= self._LABELED_HEADER_READ_CSV_OPTIONS: | ||
| # Other options, such as dtype, names, sep, comment, or encoding, keep the | ||
| # header row as pandas reads it. | ||
| return | ||
| # pandas renames the names in a header row, and copies their dtypes, even with | ||
| # names given, so the header row is skipped instead. | ||
| read_csv_kwargs["names"] = self._resolve_csv_column_names( | ||
|
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 CLI 0.160.0, model gpt-6-astra, reasoning effort max, Finding 1 (P2), this line in 936a692: the replacement names came from |
||
| names, read_csv_kwargs, pd.read_csv | ||
| )[0] | ||
| read_csv_kwargs["header"] = None | ||
| if csv_engine == "python": | ||
| # The python engine skips lines, which a name with a newline spans. | ||
| header = StringIO(newline="") | ||
| csv.writer(header, quoting=csv.QUOTE_ALL, lineterminator="").writerow(names) | ||
| read_csv_kwargs["skiprows"] = len(StringIO(header.getvalue(), newline="").readlines()) | ||
| else: | ||
| # The C engine skips parsed rows. | ||
| read_csv_kwargs["skiprows"] = 1 | ||
|
|
||
| def _configure_binary_csv_read( | ||
| self, read_csv_kwargs: dict[str, Any], labels: list[Any] | None | ||
| ) -> set[int]: | ||
|
|
||
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 one (behavior and implementation) — base
8c9c201ac53a06c2f54d7f282ec4de23b0fe449f, head6af16a0dfc8c6810e634d8c4b1390435c101c354. Result: FINDINGS (1, repaired in 936a692).Finding: for results with unique column names,
_read_csv()built its own positionalcolumn_typesdict instead of reading the publiccolumn_typesproperty, so a subclass overriding that property would no longer affect reading, a change outside #1051.Repair: unique names use
self.column_typesand the master read options exactly; only repeated names take the positional path. Rerun:pytest -n 4 tests/pyathena/arrow tests/pyathena/aio/arrow→ 119 passed.Covered without findings:
column_names/column_typesandrename_columns(names);zip(strict=True)lengths always match by construction.skip_rows_after_names=1skips a parsed row (verified locally on pyarrow 25.0.1 with a quoted newline in the header and small block sizes). A header-only file gives an empty table with the renamed columns..txtresults with repeated names, varbinary NULL handling by position, and async (AsyncArrowCursorshares the result set).