Repository navigation
Name the column in DataError from the fetch methods of every cursor - #1277
Conversation
…H-1217) A value that fails to convert in Cursor, DictCursor, S3FSCursor, ArrowCursor, or PolarsCursor, such as a timestamp with time zone value with an unknown zone or a local time that its zone repeats or skips, now raises DataError naming the column, as PandasCursor does since #1273. ArrowCursor converts each column of a record batch at once instead of each row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| else converter.convert(type_, value) | ||
| ) | ||
| except DataError as e: | ||
| raise DataError(f"Column {name!r}: {e}") from e |
There was a problem hiding this comment.
Self-review round one (behavior and implementation)
Base b8cec9dfc98fba2bcfd8470e82e1800ba02d85d0, initial head bc27a086e5e69ddc5706be5947db09943fa02652.
Covered: AthenaResultSet._get_rows / AthenaDictResultSet._get_rows (GetQueryResults rows, including the _fetch_all_rows fallback of S3FSCursor), AthenaS3FSResultSet._fetch, AthenaArrowResultSet._fetch, PolarsDataFrameIterator._iter_row_blocks; the async and aio cursors, which use these result sets; the four tightened tests and the new DictCursor test.
Checks:
- Rows with and without type hints:
hintsisNoneper column, soconvert()is still called withouttype_hintfor a column without one, as custom converters may not accept it. - Truncation: the row still gives as many values as the shorter of the columns and its values; DictCursor still zips names over those values.
- PEP 479: a
StopIterationfrom a converter still becomesRuntimeErrorin the generator, as in the previous generator expression. - Arrow: the table's columns come from the description with
strict=True(pyathena/arrow/result_set.py:302), so indexingcolumnsby description position is safe; with no description (DML), no column is converted, as before. - Polars: the error still raises when the row is iterated, so a chunked read raises at its chunk.
Result: FINDINGS (1, minor).
- The
trywrapped theyieldin_convert_valuesand in the Polarsconvertgenerator, so aDataErrorthrown into the generator at theyieldwould also be renamed. Repaired in 914200e: theyieldis outside thetry.
Limitations: async and aio variants are not tested separately because they share these result sets. The full affected suites are running locally.
…GH-1217) The column-wise conversion uses a generator, as the row-wise one did, so a StopIteration from a converter does not end an iteration of the rows. The GetQueryResults rows build their values in a list. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| try: | ||
| # Unlike a list comprehension, a generator turns a StopIteration from the | ||
| # converter into a RuntimeError instead of ending an iteration of the rows. | ||
| columns[i] = list(convert(v) for v in columns[i]) # noqa: C400 |
There was a problem hiding this comment.
Self-review round two (claims, callers, evidence)
Base b8cec9dfc98fba2bcfd8470e82e1800ba02d85d0, head 914200e121dcbecb5e20bbf91d10b25d52c362f9 (full pass), repaired at 80b8f91d96c6c5401665f1fe49dcccb1d9ce1442.
Claims checked:
- "async and aio variants share these result sets":
AsyncCursor/AsyncDictCursorbuildAthenaResultSet/AthenaDictResultSet(pyathena/async_cursor.py:115,381);AthenaAioResultSet/AthenaAioDictResultSetinherit_get_rows(pyathena/aio/result_set.py:32,323); the async and aio S3FS, Arrow, and Polars cursors build the same result set classes. Holds. - "the fetch methods raise": incorrect for
Cursor/DictCursor, whose first page is converted inexecute()(the tests raise there). PR body corrected. - "a
tryper value is free; 3.97 → 4.12 ms": the numbers came from a standalone emulation, not the changed code. Re-measured the real result sets against b8cec9d: GetQueryResults +7%, DictCursor +8%, CSV rows +2–3%, Arrow −20%. PR body now gives these measurements and states their scope (local, no I/O). - "the yield is outside the try": holds.
FINDINGS (1, introduced regression):
- The column-wise Arrow conversion used a list comprehension, so a
StopIterationfrom a custom converter escaped_fetch()asStopIteration; underfor row in cursor,__next__would end the iteration silently. On b8cec9d, the row-wise generator expression raisedRuntimeError. Repaired in 80b8f91: a generator with a comment (andnoqa: C400). Checked with a converter that raisesStopIteration:RuntimeErrorfor Arrow and the GetQueryResults rows on both b8cec9d and 80b8f91. A per-row tuple-returning method was faster for the CSV rows but has the same flaw, so_convert_valuesstays a generator, now documented with a comment.
Evidence: each tightened or added test was seen failing with the source change reverted (6 failures). Local runs use Python 3.15 only; other Python versions and the full suite run in CI when the PR is marked Ready. Docs: only docs/pandas.md documents this error; the cross-cursor contract page is #1221.
Converting a column at a time changed the order in which converters see values, and so the results of a stateful custom converter and the value whose failure is reported first. Each row is converted in column order again, naming the column of a failure. The tests anchor the column prefix at the start of the message. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| try: | ||
| converted = converter(value) | ||
| except DataError as e: | ||
| raise DataError(f"Column {name!r}: {e}") from e |
There was a problem hiding this comment.
Independent review (relayed): Codex CLI 0.163.0 (codex exec -s read-only, model gpt-6-astra, reasoning effort max), session 01a14db5-b194-7502-b3a3-1d170d2ddc3e. Static review only: no edits, builds, tests, or network. Snapshot: detached worktree at head 80b8f91d96c6c5401665f1fe49dcccb1d9ce1442, base b8cec9dfc98fba2bcfd8470e82e1800ba02d85d0; the prompt gave the diff, intended behavior, and test policy, without the PR text or prior findings.
Covered: Cursor/DictCursor, S3FS CSV and API fallback, Arrow batches, Polars eager and chunked iteration, Pandas conversion callers, async/aio delegation; hints, truncation, dict_type, duplicate names, empty/DML results, exception boundaries, hot-path structure.
Result: FINDINGS
- P2
pyathena/arrow/result_set.py:224(at 80b8f91): converting a whole column before the next changes the order in which converters see values. With two VARCHAR rows[("a", "b"), ("c", "d")]and a converterlambda v: next(counter)overiter([1, 2, 3, 4]), the base returns[(1, 2), (3, 4)]and the head[(1, 3), (2, 4)]; it can also change which failure is reported first.
Non-actionable observations:
- No other regression or double wrapping found; hint omission, short-row truncation, dict construction, positional column attribution, and
StopIteration→RuntimeErrorare preserved. - The tightened and added tests cover distinct entry points and fail on the base; their regexes were not anchored to the start of the message.
- Pre-existing: PandasCursor with
dtype={}(any givendtype,parse_dates, orconverters) bypasses_TemporalConverter(pyathena/pandas/result_set.py:1233), so the error is an unprefixedOperationalError(:1418). This is outside the changed paths and is the Give PandasCursor's temporal columns one dtype per Athena type #1273 design noted on Name the column in DataError for time zone values in every cursor #1217. - Performance impact was not verified by the reviewer.
There was a problem hiding this comment.
Repair in f81e5af: the Arrow fetch converts each row in column order again, through a per-row generator that names the column of a DataError; the no-converter path keeps list(zip(*columns)). The tests anchor the prefix with ^.
Self-review of the repair (both perspectives, scope: git range-diff b8cec9df..80b8f91d b8cec9df..f81e5afa):
- Behavior: the reviewer's scenario now gives
[(1, 2), (3, 4)]on both b8cec9d and f81e5af (script).StopIterationfrom a converter is stillRuntimeError. With no description (DML), no converter runs, as on the base. Names and converters come from the description by position, as the base's converters did. - Claims: the PR body no longer claims an Arrow speedup; re-measured Arrow 10,000 x 6: 41.2-42.2 ms vs 41.2-43.4 ms on the base. The commit message states the reason.
- Validation on f81e5af:
just lintpassed;pytest -n 4 tests/pyathena/test_cursor.py tests/pyathena/s3fs/test_cursor.py tests/pyathena/arrow tests/pyathena/aio/arrow tests/pyathena/polars/test_cursor.py -k "ambiguous or arrow": 146 passed.
The Pandas dtype/parse_dates/converters case is deferred as pre-existing (#1273's design, noted on #1217). An independent follow-up of this repair follows.
There was a problem hiding this comment.
Independent follow-up (relayed): Codex CLI 0.163.0 (codex exec -s read-only), session 01a14dbe-118f-7ba3-bbac-194f984d8826. Static review only. Snapshot: detached worktree at f81e5afa60d63e2180a6054830078ea93fb1c086; scope: the repair diff 80b8f91..f81e5af and git range-diff b8cec9df..80b8f91d b8cec9df..f81e5afa.
Result: CLEAN. Covered the Arrow fetch against b8cec9d (fetch and error propagation, converter order, default and DML paths, buffering) and the five anchored test assertions. The repair restores row-by-row conversion in column order; only a converter's DataError is wrapped, with the original as the cause; StopIteration still becomes RuntimeError; the list(zip(*columns)) path is intact. The reviewer did not measure performance. The snapshot and the PR worktree stayed unchanged.
…-1217) With parse_dates or dtype given to PandasCursor.execute(), or with a custom timestamp with time zone conversion function, pandas applies the column's conversion function to each value as it reads. A value that failed to convert raised OperationalError without the column name in a whole read, and DataError without it in a chunked read. The conversion function now names the column, and the read lets its DataError through. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-1217) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
||
| return result | ||
|
|
||
| except DataError: |
There was a problem hiding this comment.
Self-review round one, scope expansion (behavior and implementation): PandasCursor when pandas converts the values as it reads. Range f81e5afa60d63e2180a6054830078ea93fb1c086..d555a2c29e617881f0e95c854942e080f2ed9afb on base b8cec9dfc98fba2bcfd8470e82e1800ba02d85d0.
Covered: _TimeZoneConverter, both converter sites (_get_csv_read_options when pandas resolves the labels, _key_csv_columns_by_labels otherwise), _read_json_columns_as_text (keeps non-JSON converters as they are), the PyArrow engine (it pops converters, and is not chosen while a timestamp with time zone column has a read converter), the GetQueryResults fallback (same read_csv), and the except DataError passthrough in _read_csv().
Checks:
- No other
DataErrorcan arise inside thattry:_fetch_all_rows_as_csv(), whose_parse_result_rows()raisesDataErrorfor a malformed response, runs before it. TheExitStackstill closes the stream on the way out. - A whole read now raises
DataErrorfrom the constructor, as the default path's_finish_csv_frame()already did; a chunked read raises it throughPandasDataFrameIterator._read_chunk(), which marks the read failed as before. - With
convertersgiven, PyAthena's converters are replaced, so atimestamp with time zonevalue stays text (live check); unchanged. - Cost: the wrapper adds one call per value of
timestamp with time zonecolumns only, under 1% of_to_datetime_with_tz(101.6 ms -> 102.2 ms for 30,000 values).
Result: FINDINGS (1, docstring). _get_csv_converter()'s Returns did not mention _TimeZoneConverter; repaired in d555a2c.
Tests: the dtype={} cases fail without the change (12), and each converter site's cases fail when only that site skips the wrapper.
| return _to_timestamps(values, date=self._type == "date") | ||
|
|
||
|
|
||
| class _TimeZoneConverter: |
There was a problem hiding this comment.
Self-review round two, scope expansion (claims, callers, evidence). Same range as round one.
Claims checked:
- "OperationalError without the column name in a whole read, and DataError without it in a chunked read" (commit message, PR body): reproduced live on b8cec9d with
dtype={},parse_dates=[],engine="python", andchunksize=1; after the change all giveDataError: Column 'col_timestamp_tz': .... - "even
dtype={}":_get_csv_temporal_converters()treats any non-None option as given (pyathena/pandas/result_set.py, "None is the default of each option"). Holds. - "with a custom conversion function":
_get_csv_temporal_converters()skips atimestamp with time zonecolumn whose mapping is not_to_datetime_with_tz, soread_csvgets that function, wrapped. Holds by construction; not tested separately. - Documentation:
docs/pandas.mdalready states that such a value raisesDataErrornaming the column, without an exception for these options, and the section onparse_dates/dtype/converterssays nothing about errors; it is now accurate as written. - Callers:
DataErroris aDatabaseError, likeOperationalError, soexcept DatabaseErrorstill catches it;except OperationalErrordoes not, which is the intended contract change (Name the column in DataError for time zone values in every cursor #1217) and matches the default path since Give PandasCursor's temporal columns one dtype per Athena type #1273.
Result: CLEAN. Validation: just lint passed; pytest -n 4 tests/pyathena/pandas tests/pyathena/aio/pandas: 441 passed on 067a1ef (d555a2c changes a docstring only). The scope expansion goes to an independent review next.
A DataError from a conversion function given in converters is still raised as OperationalError by a whole read. _TimeZoneConverter raises a private DataError subclass, which _read_csv() lets through. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
||
| return result | ||
|
|
||
| except _ColumnDataError: |
There was a problem hiding this comment.
Independent review of the scope expansion (relayed): Codex CLI 0.163.0 (codex exec -s read-only), session 01a14dfc-3524-7292-85fa-47901996c1d6. Static review only. Snapshot: detached worktree at d555a2c29e617881f0e95c854942e080f2ed9afb, base b8cec9dfc98fba2bcfd8470e82e1800ba02d85d0; scope f81e5afa..d555a2c2 (PandasCursor) with the full diff for context; no PR text or prior findings.
Covered: both converter builders, whole and chunked reads and fetch callers, converter overrides, duplicate labels, names/usecols, header-less .txt, json/binary handling, PyArrow, GetQueryResults fallback, cleanup, docstrings, test policy.
Result: FINDINGS
- P2
pyathena/pandas/result_set.py:1467(at d555a2c):_read_csv()let everyDataErrorthrough, not only the labeled one. A whole read withconverters={"n": reject}, whererejectraisesDataError("invalid number"), raisedOperationalErroron the base and a rawDataErrorhere; the comment "whose converter names the column" was false for it.
Non-actionable observations: the added parametrizations exercise independently built converter mappings and fail on the base (exception type in whole reads, missing label in chunked reads); successful conversions are unchanged; stream cleanup stays under the ExitStack and iterator failure handlers; performance not measured by the reviewer.
There was a problem hiding this comment.
Repair in 4dd1dac (and 8f6101f, a test docstring): _TimeZoneConverter raises _ColumnDataError, a private DataError subclass, and _read_csv() lets only that through; any other DataError in a whole read is OperationalError again, as on the base. test_converter_data_error covers the reviewer's scenario.
Self-review of the repair (both perspectives, scope git range-diff b8cec9df..d555a2c2 b8cec9df..8f6101f1):
- Behavior:
_ColumnDataErroris aDataError, soexcept DataErrorandexcept DatabaseErrorstill catch the labeled error; chunked reads raise it through_read_chunk()unchanged. A customtimestamp with time zonemapping is wrapped too, so itsDataErroris labeled and passes through. Aconvertersfunction'sDataErrorin a chunked read is raw, as on the base (never wrapped there). - Visible change: the labeled error's class name is
_ColumnDataErrorin tracebacks of this path, while the default path raises plainDataError. - Evidence:
test_converter_data_errorfails when_read_csv()lets everyDataErrorthrough (checked) and passes on 8f6101f;just lintpassed;pytest -n 4 tests/pyathena/pandas tests/pyathena/aio/pandas: 442 passed; the live script still gives the labeled error fordtype={},parse_dates=[], chunked, andengine="python".
An independent follow-up of this repair follows.
There was a problem hiding this comment.
Independent follow-up (relayed): Codex CLI 0.163.0 (codex exec -s read-only), session 01a14e08-b175-7ab3-8b66-01571041678b. Static review only. Snapshot: detached worktree at 8f6101f1a556c3340e790ad06231a4224fb20abf; scope: the repair diff d555a2c..8f6101f and git range-diff b8cec9df..d555a2c2 b8cec9df..8f6101f1.
Result: CLEAN. Covered converter selection and overrides, whole-read wrapping, chunk propagation, and the tests. _TimeZoneConverter raises the private DataError subtype with the column prefix; the whole-read handler exempts only that subtype and wraps other DataErrors in OperationalError again; chunked reads keep the base's cleanup and re-raise; test_converter_data_error exercises a real whole CSV read with a caller-supplied converter and fails at d555a2c. The snapshot stayed unchanged.
There was a problem hiding this comment.
Repair at the maintainer's request in 37e9778: the labeled error's class is DataError again, as from the other paths, instead of the private _ColumnDataError. _TimeZoneConverter keeps the DataError it raised in error, and _read_csv() raises the caught exception as it is only when it is the error of one of the read's _TimeZoneConverters; any other exception is OperationalError as before.
Self-review of the repair (both perspectives, scope git range-diff b8cec9df..8f6101f1 b8cec9df..37e97783):
- Behavior: the converters that pandas calls are the objects in
read_csv_kwargs["converters"](_read_json_columns_as_text()rebuilds the dict but keeps non-JSON converters;_configure_binary_csv_read()wraps only binary columns), andread_csv_kwargsis built before thetry. Converters are built per read, soerrorbelongs to one read. Chunked reads raise the labeledDataErrorthrough_read_chunk()as before. - Claims: PR body updated (no private subclass). The commit message's "as from the other paths" holds: the default path raises
DataErrorfrom_TemporalConverter.convert(). - Evidence on 37e9778:
just lintpassed;pytest -n 4 tests/pyathena/pandas tests/pyathena/aio/pandas: 442 passed; the live script givesDataError: Column 'col_timestamp_tz': ...fordtype={},parse_dates=[], chunked, andengine="python". Mutations: making the identity check always false fails the 6 whole-readtest_time_zone_errorscases withdtype={}; making it accept anyDataErrorfailstest_converter_data_error. - Upstream: master moved to d7f6710 (Raise OperationalError for failed manifest and Spark output reads (GH-1235) #1278 and a Spark change); the changes in
pyathena/result_set.py(_read_data_manifest) andtests/pyathena/pandas/test_result_set.py(a pandas 3.0.0 condition) do not touch these contracts, and the branch merges cleanly.
An independent follow-up of this repair follows.
There was a problem hiding this comment.
Independent follow-up (relayed): Codex CLI 0.163.0 (codex exec -s read-only), session 01a14e29-acd8-7e43-b203-6d867689cf79. Static review only. Snapshot: detached worktree at 37e97783d41a57b8b9f75583b5249c9cf588b1e9; scope: repair diff 8f6101f..37e9778 and git range-diff b8cec9df..8f6101f1 b8cec9df..37e97783. Covered both converter builders, _key_csv_columns_by_labels, binary wrapping, JSON replacement, header relabeling, C/Python parsing, PyArrow selection, GetQueryResults fallback, whole/chunked propagation; the replacement paths keep the time zone converters' identity.
Result: FINDINGS
- P2
pyathena/pandas/result_set.py:1474: withconverters=[str]given toexecute(),_read_json_columns_as_text()raisesAttributeError, which the base wraps inOperationalError; the new handler called.values()on the list and leaked a secondAttributeError. - P3
tests/pyathena/pandas/test_result_set.py:1111: replacing onlyconverter.error is ewithTruepassed the tests, becausetest_converter_data_errorhad no_TimeZoneConverteramong its converters; a mixed case (a time zone column plus a mapped converter raisingDataError, withdtype={}) was missing.
Repair in df71232: the handler scans the converters only when they are a Mapping. test_converter_data_error becomes test_read_error with three cases: mapping (the reviewer's mixed case), converters, and invalid_converters.
Self-review of the repair (both perspectives): mutations checked: identity always true fails test_read_error[mapping]; no Mapping guard fails test_read_error[invalid_converters]; identity always false still fails the whole-read dtype={} cases of test_time_zone_errors. The PR body's validation is updated. Evidence on df71232: just lint passed; pytest -n 4 tests/pyathena/pandas tests/pyathena/aio/pandas: 444 passed. An independent follow-up of this repair follows.
There was a problem hiding this comment.
Independent follow-up (relayed): Codex CLI 0.163.0 (codex exec -s read-only), session 01a14e34-2c8e-7492-89e1-7346e5eea1ac. Static review only. Snapshot: detached worktree at df712324f396a0127c1c6ee815c9b2c0056da2b5; scope: repair diff 37e9778..df71232 against base b8cec9d.
Result: CLEAN. Both findings resolved: the Mapping guard lets converters=[str] reach the base OperationalError wrapper, and the identity check still preserves only the time zone converter's recorded DataError; the mapping case of test_read_error would fail if only converter.error is e were replaced with True. The rewritten test uses declarative case parameters and builds converters, mocks, and result sets during execution. The snapshot stayed unchanged.
Instead of a private DataError subclass, _TimeZoneConverter keeps the DataError it raised, and _read_csv() raises the error as it is when one of its converters raised it. The error's class is DataError, as from the other paths. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-1217) _read_csv() checks the converters for the time zone conversion's error only when they are a mapping, so converters of another type given to execute() still raise OperationalError. The read error test covers a DataError from a mapped conversion function read with a time zone column, and invalid converters. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
WHAT
Cursor,DictCursor,S3FSCursor,ArrowCursor, andPolarsCursor(and their async and aio variants, which share these result sets) now raiseDataErrornaming the column when a value fails to convert, inexecute()for the first page ofCursorandDictCursorand in the fetch methods otherwise, such as atimestamp with time zonevalue with an unknown zone or a local time that its zone repeats or skips:The message keeps the converter's text, which names the value and, for an unknown zone, the zone. The format is the one
PandasCursoruses since #1273.AthenaResultSet._convert_values()converts the values of one row and names the column of aDataError. The GetQueryResults rows ofAthenaResultSetandAthenaDictResultSetand the CSV rows ofAthenaS3FSResultSetgo through it. This merges the separate branches for rows with and without type hints.AthenaArrowResultSet._fetch()names the column of a failure in its per-row conversion; each row is still converted in column order.PandasCursor: withparse_datesordtypegiven toexecute()(evendtype={}), or with a customtimestamp with time zoneconversion function, pandas applies the column's conversion function to each value as it reads. That function is now wrapped in_TimeZoneConverter, which raises aDataErrornaming the column and keeps it, and_read_csv()raises that error as it is instead of wrapping it inOperationalError. ADataErrorfrom a function given inconvertersis stillOperationalErrorin a whole read. Before, such a value raisedOperationalErrorwithout the column name in a whole read andDataErrorwithout it in a chunked read. Withconvertersgiven, PyAthena's conversion functions are replaced and these values stay text, as before.StopIterationfrom a converter still raisesRuntimeErrorinstead of ending an iteration of the rows.PolarsDataFrameIteratornames the column in its per-column conversion generator; an error still raises when the row is iterated, so a chunked read raises when it reaches the chunk with the value.WHY
Closes #1217 (part of #1212). Time zone conversion failures raised
DataErrorwithout the column name, so the failing column of a wide result could not be identified. The PandasCursor part was done in #1273, except when pandas converts the values as it reads.The contract documentation and release notes are #1221.
Cost, measured locally on Python 3.15 by calling the result sets' conversion directly on in-memory rows (no I/O), as the minimum of 9 repeats, before (b8cec9d) and after:
Cursor)DictCursor)S3FSCursor)ArrowCursor)The
PandasCursorwrapper adds under 1% to converting 30,000timestamp with time zonevalues (101.6 ms → 102.2 ms). A GetQueryResults page holds at most 1,000 rows, so the added 0.3 ms per page is small next to the API call. A per-row method returning a tuple measured faster for the CSV rows but would let aStopIterationfrom a converter end an iteration of the rows, so the generator stays.TEST
Python 3.15, local.
just lint: passed on df71232.uv run --env-file .env pytest -n 4 tests/pyathena/pandas tests/pyathena/aio/pandas: 444 passed.test_read_errorchecks that a whole read still raises other errors asOperationalError: aDataErrorfrom a mapped conversion function read with a time zone column, one from aconvertersfunction, and invalidconverters. Making the identity check always true or dropping itsMappingcheck fails one of these cases; making it always false fails the whole-readdtype={}cases oftest_time_zone_errors.SELECT at_timezone(TIMESTAMP '2020-11-01 06:30:00 UTC', 'America/New_York'):DataError: Column 'col_timestamp_tz': ...by default and withdtype={},parse_dates=[],dtype={}withchunksize=1, anddtype={}withengine="python"; withconverters={}, the value stays text. On b8cec9d, thedtype/parse_datescases raisedOperationalError(whole) orDataError(chunked) without the column name.pytest -n 4 tests/pyathena/test_cursor.py tests/pyathena/s3fs/test_cursor.py tests/pyathena/arrow tests/pyathena/aio/arrow tests/pyathena/polars/test_cursor.py -k "ambiguous or arrow": 146 passed. A stateful custom Arrow converter sees the values in the same order as on b8cec9d (script).pytest -n 4 tests/pyathena/test_cursor.py tests/pyathena/test_async_cursor.py tests/pyathena/test_result_set.py tests/pyathena/s3fs tests/pyathena/arrow tests/pyathena/polars tests/pyathena/aio/test_cursor.py tests/pyathena/aio/s3fs tests/pyathena/aio/arrow tests/pyathena/aio/polars: 573 passed, 3 skipped.test_ambiguous_time_zone_valuetests of each non-pandas cursor now match^Column 'col_timestamp_tz': .* is ambiguous, andTestDictCursorgains one; with the source changes reverted, all 6 fail.StopIterationgivesRuntimeErrorfrom the Arrow fetch and the GetQueryResults rows, before and after (script).🤖 Generated with Claude Code