Spenserrrr opened a new pull request, #57830:
URL: https://github.com/apache/spark/pull/57830

   ### What changes were proposed in this pull request?
   
   This PR adds a golden-file test pinning the behavior of 
`pa.Array.from_pandas` with default arguments (no `mask`, no `type`, no 
`safe`), under the SPARK-54936 umbrella for monitoring upstream library 
behavior changes.
   
   New file 
`python/pyspark/tests/upstream/pyarrow/test_pyarrow_array_from_pandas_default.py`
 with `PyArrowArrayFromPandasDefaultTests`, plus the golden files 
`golden_pyarrow_array_from_pandas_default.{csv,md}` (97 rows x 2 columns) and 
one line in `dev/sparktestsupport/modules.py`.
   
   The source Series are built by three group methods, so the follow-up 
non-default tests can reuse the whole inventory or one group:
   
   - `_numpy_backed_sources()` -- numpy storage, so `hasattr(series.array, 
"__arrow_array__")` is `False` and PySpark passes `mask=series.isnull()`.
   - `_protocol_sources()` -- dtypes implementing `__arrow_array__`, where 
PySpark passes `mask=None` because supplying a mask raises. The protocol means 
"can export Arrow", not "is stored as Arrow": `Int64` is numpy values plus a 
byte mask and `string[python]` is an object ndarray, yet both implement it 
alongside the genuinely Arrow-backed `[pyarrow]` dtypes. PySpark does not 
distinguish them -- `conversion.py:435` branches only on the protocol -- so 
they share one group.
   - `_coercion_sources()` -- values that are lossy or ambiguous once a target 
type is requested. Their unconverted results are recorded here to give the 
follow-up type tests a baseline to be read against.
   
   A small helper spells a `ChunkedArray` result as `chunked<type>`. 
`repr_value` reports the element type, which is identical for an `Array` and a 
`ChunkedArray`, so without it the two are indistinguishable in a cell -- and 
which one comes back is a real contract, since `create_arrow_table_from_pandas` 
builds a `pa.Table` rather than a `pa.RecordBatch` precisely because a chunked 
input can produce a `ChunkedArray`.
   
   ### Why are the changes needed?
   
   `from_pandas` is the pandas -> Arrow direction, which the previously merged 
golden tests under SPARK-54936 do not cover -- they are all Arrow -> pandas. 
PySpark depends on it at three production call sites: 
`python/pyspark/sql/conversion.py:443` and `:474` (the pandas UDF return leg, 
legacy and non-legacy), and `python/pyspark/sql/pandas/conversion.py:117` 
(`create_arrow_array_from_pandas`, used by `createDataFrame(pandas_df)`). A 
silent change here becomes wrong data on the JVM side rather than a visible 
error.
   
   Two behaviors the golden pins that are easy to miss:
   
   - **pandas 3 changed its default temporal resolution.** 
`pd.Series([datetime])` infers `datetime64[ns]` on pandas 2 and 
`datetime64[us]` on pandas 3, and `Categorical.categories` become `str` (so the 
dictionary's values become `large_string`). The rows pin each resolution 
explicitly and add deliberately unpinned `:inferred` rows whose only job is to 
record which default pandas picks.
   - **`object` dtype and `datetime64[ns]` disagree.** In object dtype pyarrow 
takes the unit from `datetime.datetime`'s own microsecond resolution rather 
than from a numpy dtype, so the same values give `timestamp[us]` instead of 
`timestamp[ns]` -- and a sub-microsecond value is dropped silently, while an 
explicit `type=pa.timestamp("us")` raises `ArrowInvalid` on that same loss.
   
   Writing these tests is also how the `ChunkedArray` return type came to 
light: `from_pandas` can return a `ChunkedArray` rather than an `Array`, which 
led to tracing the two consumers and finding that `PandasToArrowConversion` did 
not guard for it. That is fixed separately in #57829 (SPARK-58625); this PR 
only records the upstream behavior.
   
   ### Does this PR introduce _any_ user-facing change?
   
   No. Test-only.
   
   ### How was this patch tested?
   
   New golden-file test. It runs without a Spark session, so it exercises 
PyArrow and pandas directly.
   
   Validated across **16 environments -- PyArrow 18/19/20/21/22/23/24/25 x 
pandas 2 and pandas 3** -- with a fresh virtualenv per combination, running the 
committed test file against the committed golden file. All 16 pass. Six cells 
legitimately differ on pandas 3 and are carried in the `overrides` dict rather 
than by regenerating; disabling the `LooseVersion` guard makes pandas 3 fail on 
exactly those six while pandas 2 still passes, so the overrides are 
load-bearing.
   
   Also verified: the test passes without `SPARK_GENERATE_GOLDEN_FILES`, 
regeneration is byte-identical, and `ruff check` / `ruff format --check` / 
`mypy` are clean.
   
   ### Was this patch authored or co-authored using generative AI tooling?
   
   Generated-by: Claude Code (Opus 5)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to