This is an automated email from the ASF dual-hosted git repository.
uros-b pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/spark.git
The following commit(s) were added to refs/heads/master by this push:
new 055c0b9647b5 [SPARK-58545][PYTHON][TEST] Add tests for
pa.Array.to_pandas with types_mapper
055c0b9647b5 is described below
commit 055c0b9647b5a28ca5d590bdd2ce0cc2a32e3c98
Author: Spenser Sun <[email protected]>
AuthorDate: Wed Aug 5 09:37:20 2026 +0000
[SPARK-58545][PYTHON][TEST] Add tests for pa.Array.to_pandas with
types_mapper
### What changes were proposed in this pull request?
This PR adds a golden-file test pinning the behavior of
`pa.Array.to_pandas(types_mapper=pd.ArrowDtype)`, under the umbrella
[SPARK-54936](https://issues.apache.org/jira/browse/SPARK-54936) (monitor
behavior changes from upstream). It is a new test method on the existing
`PyArrowArrayToPandasZeroCopyTests`, so it reuses that class's row set and the
`_verify_zero_copy` / `_arrow_buffers` helpers rather than adding new machinery:
- `test_to_pandas_zero_copy_only_arrow_backed`, with the new golden file
`golden_pyarrow_arrow_to_pandas_zero_copy_arrow_backed.{csv,md}` (125 rows x 4
columns).
- Columns: `pyarrow array` | `types_mapper=pd.ArrowDtype,
zero_copy_only=False` | `types_mapper=pd.ArrowDtype, zero_copy_only=True` |
`verified zero-copy`.
Both `zero_copy_only` states are recorded even though the flag makes no
observable difference on this backend today. Omitting a column because it looks
redundant would bake that assumption into a monitoring test; recording both
means a version where the flag starts to matter shows up as a diff instead of
passing silently.
No `dev/sparktestsupport/modules.py` change is needed, since this is a new
method in an already-registered test module.
### Why are the changes needed?
PySpark calls this argument in production, at
`ArrowArrayToPandasConversion.convert_numpy`
(`python/pyspark/sql/conversion.py`), for `ByteType` / `ShortType` /
`IntegerType` / `LongType` when
`spark.sql.execution.pythonUDF.pandas.preferIntExtensionDtype` is enabled. That
config exists because the integer dtype handed to a pandas UDF otherwise
depends on whether the current batch happens to contain a null (SPARK-55788),
so a change in how `types_mapper` maps Arrow types would change wh [...]
The existing goldens cover `to_pandas` on the NumPy backend. Asking pandas
for Arrow-backed storage is a different conversion, and the point of that
backend is that it can keep pointing at the Arrow buffers instead of
materializing them into NumPy. Recording it in its own golden file lets the two
be read side by side, and every row in this golden is `zero-copy`: strings,
nested types and multi-chunk arrays, which
`golden_pyarrow_arrow_to_pandas_zero_copy` records as copied on the NumP [...]
### Does this PR introduce _any_ user-facing change?
No. Test-only.
### How was this patch tested?
New golden-file test. Generated with `SPARK_GENERATE_GOLDEN_FILES=1`, then
every cell was reviewed before being committed.
The whole `python/pyspark/tests/upstream/pyarrow/` directory passes (49
passed, 1 skipped), and regeneration is byte-identical, so the golden is
deterministic.
Validated across **PyArrow 18-25 x pandas 2 and pandas 3 = 16/16
combinations pass**, each in a fresh venv running the committed test file
against the committed golden.
All 16 pass with an empty `overrides` dict, which is worth noting because
the sibling methods in this file do need a pandas-3 override for their string
rows. `types_mapper=pd.ArrowDtype` selects the dtype explicitly, so pandas'
default never applies and the pandas-3 change to it does not reach these cells:
the default string conversion goes `object` (pandas 2) -> `str` (pandas 3),
while `types_mapper` reads `string[pyarrow]` on both. This was checked cell by
cell, not just on the stri [...]
Since this golden is uniformly `zero-copy`, the `verified zero-copy` column
was sanity-checked rather than trusted: the empty rows perform a real buffer
comparison rather than a vacuous zero-length one, and multi-chunk arrays
preserve both chunk addresses instead of being combined. The same helper
produces 86 `copied` rows in `golden_pyarrow_arrow_to_pandas_zero_copy`, so it
is not rubber-stamping.
### Was this patch authored or co-authored using generative AI tooling?
Generated-by: Claude Code (Opus 5)
Closes #57771 from Spenserrrr/types-mapper-tests.
Authored-by: Spenser Sun <[email protected]>
Signed-off-by: Uros Bojanic <[email protected]>
---
...rrow_arrow_to_pandas_zero_copy_arrow_backed.csv | 126 ++++++++++++++++++++
...arrow_arrow_to_pandas_zero_copy_arrow_backed.md | 127 +++++++++++++++++++++
.../test_pyarrow_arrow_to_pandas_non_default.py | 50 ++++++++
3 files changed, 303 insertions(+)
diff --git
a/python/pyspark/tests/upstream/pyarrow/golden_pyarrow_arrow_to_pandas_zero_copy_arrow_backed.csv
b/python/pyspark/tests/upstream/pyarrow/golden_pyarrow_arrow_to_pandas_zero_copy_arrow_backed.csv
new file mode 100644
index 000000000000..11f2831ca359
--- /dev/null
+++
b/python/pyspark/tests/upstream/pyarrow/golden_pyarrow_arrow_to_pandas_zero_copy_arrow_backed.csv
@@ -0,0 +1,126 @@
+test case pyarrow array types_mapper=pd.ArrowDtype,
zero_copy_only=False types_mapper=pd.ArrowDtype, zero_copy_only=True
verified zero-copy
+int8:standard [0, 1, -1, 127, -128]@int8 [0, 1, -1, 127,
-128]@Series[int8[pyarrow]] [0, 1, -1, 127, -128]@Series[int8[pyarrow]]
zero-copy
+int8:nullable [0, 1, None]@int8 [0, 1, <NA>]@Series[int8[pyarrow]]
[0, 1, <NA>]@Series[int8[pyarrow]] zero-copy
+int8:empty []@int8 []@Series[int8[pyarrow]]
[]@Series[int8[pyarrow]] zero-copy
+int16:standard [0, 1, -1, 32767, -32768]@int16 [0, 1, -1, 32767,
-32768]@Series[int16[pyarrow]] [0, 1, -1, 32767,
-32768]@Series[int16[pyarrow]] zero-copy
+int16:nullable [0, 1, None]@int16 [0, 1, <NA>]@Series[int16[pyarrow]]
[0, 1, <NA>]@Series[int16[pyarrow]] zero-copy
+int16:empty []@int16 []@Series[int16[pyarrow]]
[]@Series[int16[pyarrow]] zero-copy
+int32:standard [0, 1, -1, 2147483647, -2147483648]@int32 [0, 1, -1,
2147483647, -2147483648]@Series[int32[pyarrow]] [0, 1, -1, 2147483647,
-2147483648]@Series[int32[pyarrow]] zero-copy
+int32:nullable [0, 1, None]@int32 [0, 1, <NA>]@Series[int32[pyarrow]]
[0, 1, <NA>]@Series[int32[pyarrow]] zero-copy
+int32:empty []@int32 []@Series[int32[pyarrow]]
[]@Series[int32[pyarrow]] zero-copy
+int64:standard [0, 1, -1, 9223372036854775807, -9223372036854775808]@int64
[0, 1, -1, 9223372036854775807, -9223372036854775808]@Series[int64[pyarrow]]
[0, 1, -1, 9223372036854775807, -9223372036854775808]@Series[int64[pyarrow]]
zero-copy
+int64:nullable [0, 1, None]@int64 [0, 1, <NA>]@Series[int64[pyarrow]]
[0, 1, <NA>]@Series[int64[pyarrow]] zero-copy
+int64:empty []@int64 []@Series[int64[pyarrow]]
[]@Series[int64[pyarrow]] zero-copy
+uint8:standard [0, 1, 255]@uint8 [0, 1, 255]@Series[uint8[pyarrow]]
[0, 1, 255]@Series[uint8[pyarrow]] zero-copy
+uint8:nullable [0, 1, None]@uint8 [0, 1, <NA>]@Series[uint8[pyarrow]]
[0, 1, <NA>]@Series[uint8[pyarrow]] zero-copy
+uint8:empty []@uint8 []@Series[uint8[pyarrow]]
[]@Series[uint8[pyarrow]] zero-copy
+uint16:standard [0, 1, 65535]@uint16 [0, 1,
65535]@Series[uint16[pyarrow]] [0, 1, 65535]@Series[uint16[pyarrow]]
zero-copy
+uint16:nullable [0, 1, None]@uint16 [0, 1,
<NA>]@Series[uint16[pyarrow]] [0, 1, <NA>]@Series[uint16[pyarrow]]
zero-copy
+uint16:empty []@uint16 []@Series[uint16[pyarrow]]
[]@Series[uint16[pyarrow]] zero-copy
+uint32:standard [0, 1, 4294967295]@uint32 [0, 1,
4294967295]@Series[uint32[pyarrow]] [0, 1,
4294967295]@Series[uint32[pyarrow]] zero-copy
+uint32:nullable [0, 1, None]@uint32 [0, 1,
<NA>]@Series[uint32[pyarrow]] [0, 1, <NA>]@Series[uint32[pyarrow]]
zero-copy
+uint32:empty []@uint32 []@Series[uint32[pyarrow]]
[]@Series[uint32[pyarrow]] zero-copy
+uint64:standard [0, 1, 18446744073709551615]@uint64 [0, 1,
18446744073709551615]@Series[uint64[pyarrow]] [0, 1,
18446744073709551615]@Series[uint64[pyarrow]] zero-copy
+uint64:nullable [0, 1, None]@uint64 [0, 1,
<NA>]@Series[uint64[pyarrow]] [0, 1, <NA>]@Series[uint64[pyarrow]]
zero-copy
+uint64:empty []@uint64 []@Series[uint64[pyarrow]]
[]@Series[uint64[pyarrow]] zero-copy
+float32:standard [0.0, 1.5, -1.5]@float32 [0.0, 1.5,
-1.5]@Series[float[pyarrow]] [0.0, 1.5, -1.5]@Series[float[pyarrow]] zero-copy
+float32:nullable [0.0, 1.5, None]@float32 [0.0, 1.5,
<NA>]@Series[float[pyarrow]] [0.0, 1.5, <NA>]@Series[float[pyarrow]] zero-copy
+float32:empty []@float32 []@Series[float[pyarrow]]
[]@Series[float[pyarrow]] zero-copy
+float64:standard [0.0, 1.5, -1.5]@float64 [0.0, 1.5,
-1.5]@Series[double[pyarrow]] [0.0, 1.5, -1.5]@Series[double[pyarrow]]
zero-copy
+float64:nullable [0.0, 1.5, None]@float64 [0.0, 1.5,
<NA>]@Series[double[pyarrow]] [0.0, 1.5, <NA>]@Series[double[pyarrow]]
zero-copy
+float64:special [nan, inf, -inf]@float64 [nan, inf,
-inf]@Series[double[pyarrow]] [nan, inf, -inf]@Series[double[pyarrow]]
zero-copy
+float64:empty []@float64 []@Series[double[pyarrow]]
[]@Series[double[pyarrow]] zero-copy
+bool:standard [True, False, True]@bool [True, False,
True]@Series[bool[pyarrow]] [True, False, True]@Series[bool[pyarrow]]
zero-copy
+bool:nullable [True, False, None]@bool [True, False,
<NA>]@Series[bool[pyarrow]] [True, False, <NA>]@Series[bool[pyarrow]]
zero-copy
+bool:empty []@bool []@Series[bool[pyarrow]]
[]@Series[bool[pyarrow]] zero-copy
+string:standard [hello, world, ]@string ['hello', 'world',
'']@Series[string[pyarrow]] ['hello', 'world', '']@Series[string[pyarrow]]
zero-copy
+string:nullable [hello, None, world]@string ['hello', <NA>,
'world']@Series[string[pyarrow]] ['hello', <NA>,
'world']@Series[string[pyarrow]] zero-copy
+string:empty []@string []@Series[string[pyarrow]]
[]@Series[string[pyarrow]] zero-copy
+large_string:standard [hello, world]@large_string ['hello',
'world']@Series[large_string[pyarrow]] ['hello',
'world']@Series[large_string[pyarrow]] zero-copy
+large_string:nullable [hello, None]@large_string ['hello',
<NA>]@Series[large_string[pyarrow]] ['hello',
<NA>]@Series[large_string[pyarrow]] zero-copy
+large_string:empty []@large_string []@Series[large_string[pyarrow]]
[]@Series[large_string[pyarrow]] zero-copy
+binary:standard [b'hello', b'world']@binary [b'hello',
b'world']@Series[binary[pyarrow]] [b'hello',
b'world']@Series[binary[pyarrow]] zero-copy
+binary:nullable [b'hello', None]@binary [b'hello',
<NA>]@Series[binary[pyarrow]] [b'hello', <NA>]@Series[binary[pyarrow]]
zero-copy
+binary:empty []@binary []@Series[binary[pyarrow]]
[]@Series[binary[pyarrow]] zero-copy
+large_binary:standard [b'hello', b'world']@large_binary [b'hello',
b'world']@Series[large_binary[pyarrow]] [b'hello',
b'world']@Series[large_binary[pyarrow]] zero-copy
+large_binary:nullable [b'hello', None]@large_binary [b'hello',
<NA>]@Series[large_binary[pyarrow]] [b'hello',
<NA>]@Series[large_binary[pyarrow]] zero-copy
+large_binary:empty []@large_binary []@Series[large_binary[pyarrow]]
[]@Series[large_binary[pyarrow]] zero-copy
+decimal128:standard [1.23, 4.56, -7.89]@decimal128(5, 2)
[Decimal('1.23'), Decimal('4.56'), Decimal('-7.89')]@Series[decimal128(5,
2)[pyarrow]] [Decimal('1.23'), Decimal('4.56'),
Decimal('-7.89')]@Series[decimal128(5, 2)[pyarrow]] zero-copy
+decimal128:nullable [1.23, None, 4.56]@decimal128(5, 2)
[Decimal('1.23'), <NA>, Decimal('4.56')]@Series[decimal128(5, 2)[pyarrow]]
[Decimal('1.23'), <NA>, Decimal('4.56')]@Series[decimal128(5, 2)[pyarrow]]
zero-copy
+decimal128:empty []@decimal128(5, 2) []@Series[decimal128(5,
2)[pyarrow]] []@Series[decimal128(5, 2)[pyarrow]] zero-copy
+date32:standard [2024-01-01, 2024-06-15]@date32[day]
[datetime.date(2024, 1, 1), datetime.date(2024, 6,
15)]@Series[date32[day][pyarrow]] [datetime.date(2024, 1, 1),
datetime.date(2024, 6, 15)]@Series[date32[day][pyarrow]] zero-copy
+date32:nullable [2024-01-01, None]@date32[day] [datetime.date(2024, 1,
1), <NA>]@Series[date32[day][pyarrow]] [datetime.date(2024, 1, 1),
<NA>]@Series[date32[day][pyarrow]] zero-copy
+date32:empty []@date32[day] []@Series[date32[day][pyarrow]]
[]@Series[date32[day][pyarrow]] zero-copy
+date64:standard [2024-01-01, 2024-06-15]@date64[ms]
[datetime.date(2024, 1, 1), datetime.date(2024, 6,
15)]@Series[date64[ms][pyarrow]] [datetime.date(2024, 1, 1),
datetime.date(2024, 6, 15)]@Series[date64[ms][pyarrow]] zero-copy
+date64:nullable [2024-01-01, None]@date64[ms] [datetime.date(2024, 1,
1), <NA>]@Series[date64[ms][pyarrow]] [datetime.date(2024, 1, 1),
<NA>]@Series[date64[ms][pyarrow]] zero-copy
+date64:empty []@date64[ms] []@Series[date64[ms][pyarrow]]
[]@Series[date64[ms][pyarrow]] zero-copy
+timestamp[s]:standard [2024-01-01 12:00:00, 2024-06-15 18:30:00]@timestamp[s]
[Timestamp('2024-01-01 12:00:00'), Timestamp('2024-06-15
18:30:00')]@Series[timestamp[s][pyarrow]] [Timestamp('2024-01-01
12:00:00'), Timestamp('2024-06-15 18:30:00')]@Series[timestamp[s][pyarrow]]
zero-copy
+timestamp[s]:nullable [2024-01-01 12:00:00, None]@timestamp[s]
[Timestamp('2024-01-01 12:00:00'), <NA>]@Series[timestamp[s][pyarrow]]
[Timestamp('2024-01-01 12:00:00'), <NA>]@Series[timestamp[s][pyarrow]]
zero-copy
+timestamp[s]:empty []@timestamp[s] []@Series[timestamp[s][pyarrow]]
[]@Series[timestamp[s][pyarrow]] zero-copy
+timestamp[ms]:standard [2024-01-01 12:00:00, 2024-06-15
18:30:00]@timestamp[ms] [Timestamp('2024-01-01 12:00:00'),
Timestamp('2024-06-15 18:30:00')]@Series[timestamp[ms][pyarrow]]
[Timestamp('2024-01-01 12:00:00'), Timestamp('2024-06-15
18:30:00')]@Series[timestamp[ms][pyarrow]] zero-copy
+timestamp[ms]:nullable [2024-01-01 12:00:00, None]@timestamp[ms]
[Timestamp('2024-01-01 12:00:00'), <NA>]@Series[timestamp[ms][pyarrow]]
[Timestamp('2024-01-01 12:00:00'), <NA>]@Series[timestamp[ms][pyarrow]]
zero-copy
+timestamp[ms]:empty []@timestamp[ms]
[]@Series[timestamp[ms][pyarrow]] []@Series[timestamp[ms][pyarrow]]
zero-copy
+timestamp[us]:standard [2024-01-01 12:00:00, 2024-06-15
18:30:00]@timestamp[us] [Timestamp('2024-01-01 12:00:00'),
Timestamp('2024-06-15 18:30:00')]@Series[timestamp[us][pyarrow]]
[Timestamp('2024-01-01 12:00:00'), Timestamp('2024-06-15
18:30:00')]@Series[timestamp[us][pyarrow]] zero-copy
+timestamp[us]:nullable [2024-01-01 12:00:00, None]@timestamp[us]
[Timestamp('2024-01-01 12:00:00'), <NA>]@Series[timestamp[us][pyarrow]]
[Timestamp('2024-01-01 12:00:00'), <NA>]@Series[timestamp[us][pyarrow]]
zero-copy
+timestamp[us]:empty []@timestamp[us]
[]@Series[timestamp[us][pyarrow]] []@Series[timestamp[us][pyarrow]]
zero-copy
+timestamp[ns]:standard [2024-01-01 12:00:00, 2024-06-15
18:30:00]@timestamp[ns] [Timestamp('2024-01-01 12:00:00'),
Timestamp('2024-06-15 18:30:00')]@Series[timestamp[ns][pyarrow]]
[Timestamp('2024-01-01 12:00:00'), Timestamp('2024-06-15
18:30:00')]@Series[timestamp[ns][pyarrow]] zero-copy
+timestamp[ns]:nullable [2024-01-01 12:00:00, None]@timestamp[ns]
[Timestamp('2024-01-01 12:00:00'), <NA>]@Series[timestamp[ns][pyarrow]]
[Timestamp('2024-01-01 12:00:00'), <NA>]@Series[timestamp[ns][pyarrow]]
zero-copy
+timestamp[ns]:empty []@timestamp[ns]
[]@Series[timestamp[ns][pyarrow]] []@Series[timestamp[ns][pyarrow]]
zero-copy
+timestamp[us,tz=UTC]:standard [2024-01-01 12:00:00+00:00, 2024-06-15
18:30:00+00:00]@timestamp[us, tz=UTC] [Timestamp('2024-01-01 12:00:00+0000',
tz='UTC'), Timestamp('2024-06-15 18:30:00+0000',
tz='UTC')]@Series[timestamp[us, tz=UTC][pyarrow]] [Timestamp('2024-01-01
12:00:00+0000', tz='UTC'), Timestamp('2024-06-15 18:30:00+0000',
tz='UTC')]@Series[timestamp[us, tz=UTC][pyarrow]] zero-copy
+timestamp[us,tz=UTC]:nullable [2024-01-01 12:00:00+00:00, None]@timestamp[us,
tz=UTC] [Timestamp('2024-01-01 12:00:00+0000', tz='UTC'),
<NA>]@Series[timestamp[us, tz=UTC][pyarrow]] [Timestamp('2024-01-01
12:00:00+0000', tz='UTC'), <NA>]@Series[timestamp[us, tz=UTC][pyarrow]]
zero-copy
+timestamp[us,tz=UTC]:empty []@timestamp[us, tz=UTC]
[]@Series[timestamp[us, tz=UTC][pyarrow]] []@Series[timestamp[us,
tz=UTC][pyarrow]] zero-copy
+duration[s]:standard [1 day, 0:00:00, 2:30:00]@duration[s] [Timedelta('1
days 00:00:00'), Timedelta('0 days 02:30:00')]@Series[duration[s][pyarrow]]
[Timedelta('1 days 00:00:00'), Timedelta('0 days
02:30:00')]@Series[duration[s][pyarrow]] zero-copy
+duration[s]:nullable [1 day, 0:00:00, None]@duration[s] [Timedelta('1
days 00:00:00'), <NA>]@Series[duration[s][pyarrow]] [Timedelta('1 days
00:00:00'), <NA>]@Series[duration[s][pyarrow]] zero-copy
+duration[s]:empty []@duration[s] []@Series[duration[s][pyarrow]]
[]@Series[duration[s][pyarrow]] zero-copy
+duration[ms]:standard [1 day, 0:00:00, 2:30:00]@duration[ms] [Timedelta('1
days 00:00:00'), Timedelta('0 days 02:30:00')]@Series[duration[ms][pyarrow]]
[Timedelta('1 days 00:00:00'), Timedelta('0 days
02:30:00')]@Series[duration[ms][pyarrow]] zero-copy
+duration[ms]:nullable [1 day, 0:00:00, None]@duration[ms] [Timedelta('1
days 00:00:00'), <NA>]@Series[duration[ms][pyarrow]] [Timedelta('1 days
00:00:00'), <NA>]@Series[duration[ms][pyarrow]] zero-copy
+duration[ms]:empty []@duration[ms] []@Series[duration[ms][pyarrow]]
[]@Series[duration[ms][pyarrow]] zero-copy
+duration[us]:standard [1 day, 0:00:00, 2:30:00]@duration[us] [Timedelta('1
days 00:00:00'), Timedelta('0 days 02:30:00')]@Series[duration[us][pyarrow]]
[Timedelta('1 days 00:00:00'), Timedelta('0 days
02:30:00')]@Series[duration[us][pyarrow]] zero-copy
+duration[us]:nullable [1 day, 0:00:00, None]@duration[us] [Timedelta('1
days 00:00:00'), <NA>]@Series[duration[us][pyarrow]] [Timedelta('1 days
00:00:00'), <NA>]@Series[duration[us][pyarrow]] zero-copy
+duration[us]:empty []@duration[us] []@Series[duration[us][pyarrow]]
[]@Series[duration[us][pyarrow]] zero-copy
+duration[ns]:standard [1 days 00:00:00, 0 days 02:30:00]@duration[ns]
[Timedelta('1 days 00:00:00'), Timedelta('0 days
02:30:00')]@Series[duration[ns][pyarrow]] [Timedelta('1 days 00:00:00'),
Timedelta('0 days 02:30:00')]@Series[duration[ns][pyarrow]] zero-copy
+duration[ns]:nullable [1 days 00:00:00, None]@duration[ns] [Timedelta('1
days 00:00:00'), <NA>]@Series[duration[ns][pyarrow]] [Timedelta('1 days
00:00:00'), <NA>]@Series[duration[ns][pyarrow]] zero-copy
+duration[ns]:empty []@duration[ns] []@Series[duration[ns][pyarrow]]
[]@Series[duration[ns][pyarrow]] zero-copy
+time32[s]:standard [12:30:00, 18:45:30]@time32[s] [datetime.time(12, 30),
datetime.time(18, 45, 30)]@Series[time32[s][pyarrow]] [datetime.time(12, 30),
datetime.time(18, 45, 30)]@Series[time32[s][pyarrow]] zero-copy
+time32[s]:nullable [12:30:00, None]@time32[s] [datetime.time(12, 30),
<NA>]@Series[time32[s][pyarrow]] [datetime.time(12, 30),
<NA>]@Series[time32[s][pyarrow]] zero-copy
+time32[s]:empty []@time32[s] []@Series[time32[s][pyarrow]]
[]@Series[time32[s][pyarrow]] zero-copy
+time32[ms]:standard [12:30:00, 18:45:30]@time32[ms] [datetime.time(12, 30),
datetime.time(18, 45, 30)]@Series[time32[ms][pyarrow]] [datetime.time(12, 30),
datetime.time(18, 45, 30)]@Series[time32[ms][pyarrow]] zero-copy
+time32[ms]:nullable [12:30:00, None]@time32[ms] [datetime.time(12, 30),
<NA>]@Series[time32[ms][pyarrow]] [datetime.time(12, 30),
<NA>]@Series[time32[ms][pyarrow]] zero-copy
+time32[ms]:empty []@time32[ms] []@Series[time32[ms][pyarrow]]
[]@Series[time32[ms][pyarrow]] zero-copy
+time64[us]:standard [12:30:00, 18:45:30]@time64[us] [datetime.time(12, 30),
datetime.time(18, 45, 30)]@Series[time64[us][pyarrow]] [datetime.time(12, 30),
datetime.time(18, 45, 30)]@Series[time64[us][pyarrow]] zero-copy
+time64[us]:nullable [12:30:00, None]@time64[us] [datetime.time(12, 30),
<NA>]@Series[time64[us][pyarrow]] [datetime.time(12, 30),
<NA>]@Series[time64[us][pyarrow]] zero-copy
+time64[us]:empty []@time64[us] []@Series[time64[us][pyarrow]]
[]@Series[time64[us][pyarrow]] zero-copy
+time64[ns]:standard [12:30:00, 18:45:30]@time64[ns] [datetime.time(12, 30),
datetime.time(18, 45, 30)]@Series[time64[ns][pyarrow]] [datetime.time(12, 30),
datetime.time(18, 45, 30)]@Series[time64[ns][pyarrow]] zero-copy
+time64[ns]:nullable [12:30:00, None]@time64[ns] [datetime.time(12, 30),
<NA>]@Series[time64[ns][pyarrow]] [datetime.time(12, 30),
<NA>]@Series[time64[ns][pyarrow]] zero-copy
+time64[ns]:empty []@time64[ns] []@Series[time64[ns][pyarrow]]
[]@Series[time64[ns][pyarrow]] zero-copy
+null:standard [None, None, None]@null [<NA>, <NA>,
<NA>]@Series[null[pyarrow]] [<NA>, <NA>, <NA>]@Series[null[pyarrow]]
zero-copy
+null:empty []@null []@Series[null[pyarrow]]
[]@Series[null[pyarrow]] zero-copy
+list<int64>:standard [[1, 2], [3, 4, 5]]@list<item: int64> [[1, 2], [3, 4,
5]]@Series[list<item: int64>[pyarrow]] [[1, 2], [3, 4, 5]]@Series[list<item:
int64>[pyarrow]] zero-copy
+list<int64>:nullable [[1, 2], None, [3]]@list<item: int64> [[1, 2], <NA>,
[3]]@Series[list<item: int64>[pyarrow]] [[1, 2], <NA>, [3]]@Series[list<item:
int64>[pyarrow]] zero-copy
+list<int64>:empty []@list<item: int64> []@Series[list<item:
int64>[pyarrow]] []@Series[list<item: int64>[pyarrow]] zero-copy
+list<string>:standard [['a', 'b'], ['c']]@list<item: string> [['a', 'b'],
['c']]@Series[list<item: string>[pyarrow]] [['a', 'b'],
['c']]@Series[list<item: string>[pyarrow]] zero-copy
+large_list<int64>:standard [[1, 2], [3, 4]]@large_list<item: int64>
[[1, 2], [3, 4]]@Series[large_list<item: int64>[pyarrow]] [[1, 2], [3,
4]]@Series[large_list<item: int64>[pyarrow]] zero-copy
+large_list<int64>:empty []@large_list<item: int64>
[]@Series[large_list<item: int64>[pyarrow]] []@Series[large_list<item:
int64>[pyarrow]] zero-copy
+fixed_size_list<int64>[3]:standard [[1, 2, 3], [4, 5,
6]]@fixed_size_list<item: int64>[3] [[1, 2, 3], [4, 5,
6]]@Series[fixed_size_list<item: int64>[3][pyarrow]] [[1, 2, 3], [4, 5,
6]]@Series[fixed_size_list<item: int64>[3][pyarrow]] zero-copy
+fixed_size_list<int64>[3]:empty []@fixed_size_list<item: int64>[3]
[]@Series[fixed_size_list<item: int64>[3][pyarrow]]
[]@Series[fixed_size_list<item: int64>[3][pyarrow]] zero-copy
+struct:standard [[('x', 1), ('y', 'a')], [('x', 2), ('y',
'b')]]@struct<x: int64, y: string> [{'x': 1, 'y': 'a'}, {'x': 2, 'y':
'b'}]@Series[struct<x: int64, y: string>[pyarrow]] [{'x': 1, 'y': 'a'}, {'x':
2, 'y': 'b'}]@Series[struct<x: int64, y: string>[pyarrow]] zero-copy
+struct:nullable [[('x', 1), ('y', 'a')], None]@struct<x: int64, y:
string> [{'x': 1, 'y': 'a'}, <NA>]@Series[struct<x: int64, y:
string>[pyarrow]] [{'x': 1, 'y': 'a'}, <NA>]@Series[struct<x: int64, y:
string>[pyarrow]] zero-copy
+struct:empty []@struct<x: int64, y: string> []@Series[struct<x: int64, y:
string>[pyarrow]] []@Series[struct<x: int64, y: string>[pyarrow]] zero-copy
+map<string,int64>:standard [[('a', 1), ('b', 2)], [('c', 3)]]@map<string,
int64> [[('a', 1), ('b', 2)], [('c', 3)]]@Series[map<string, int64>[pyarrow]]
[[('a', 1), ('b', 2)], [('c', 3)]]@Series[map<string, int64>[pyarrow]]
zero-copy
+map<string,int64>:empty []@map<string, int64> []@Series[map<string,
int64>[pyarrow]] []@Series[map<string, int64>[pyarrow]] zero-copy
+list<list<int64>>:standard [[[1, 2], [3]], [[4, 5, 6]]]@list<item:
list<item: int64>> [[[1, 2], [3]], [[4, 5, 6]]]@Series[list<item:
list<item: int64>>[pyarrow]] [[[1, 2], [3]], [[4, 5, 6]]]@Series[list<item:
list<item: int64>>[pyarrow]] zero-copy
+list<struct>:standard [[{'x': 1}, {'x': 2}], [{'x': 3}]]@list<item: struct<x:
int64>> [[{'x': 1}, {'x': 2}], [{'x': 3}]]@Series[list<item: struct<x:
int64>>[pyarrow]] [[{'x': 1}, {'x': 2}], [{'x': 3}]]@Series[list<item:
struct<x: int64>>[pyarrow]] zero-copy
+list<map<string,int64>>:standard [[[('a', 1)], [('b', 2)]], [[('c',
3)]]]@list<item: map<string, int64>> [[[('a', 1)], [('b', 2)]], [[('c',
3)]]]@Series[list<item: map<string, int64>>[pyarrow]] [[[('a', 1)],
[('b', 2)]], [[('c', 3)]]]@Series[list<item: map<string, int64>>[pyarrow]]
zero-copy
+struct<struct>:standard [[('outer', {'inner': 1})], [('outer',
{'inner': 2})]]@struct<outer: struct<inner: int64>> [{'outer': {'inner':
1}}, {'outer': {'inner': 2}}]@Series[struct<outer: struct<inner:
int64>>[pyarrow]] [{'outer': {'inner': 1}}, {'outer': {'inner':
2}}]@Series[struct<outer: struct<inner: int64>>[pyarrow]] zero-copy
+struct<list<int64>>:standard [[('items', [1, 2, 3])], [('items', [4,
5])]]@struct<items: list<item: int64>> [{'items': [1, 2, 3]}, {'items': [4,
5]}]@Series[struct<items: list<item: int64>>[pyarrow]] [{'items': [1, 2,
3]}, {'items': [4, 5]}]@Series[struct<items: list<item: int64>>[pyarrow]]
zero-copy
+struct<map<string,int64>>:standard [[('mapping', [('a', 1)])],
[('mapping', [('b', 2)])]]@struct<mapping: map<string, int64>>
[{'mapping': [('a', 1)]}, {'mapping': [('b', 2)]}]@Series[struct<mapping:
map<string, int64>>[pyarrow]] [{'mapping': [('a', 1)]}, {'mapping': [('b',
2)]}]@Series[struct<mapping: map<string, int64>>[pyarrow]] zero-copy
+map<string,list<int64>>:standard [[('a', [1, 2]), ('b', [3])], [('c',
[4, 5, 6])]]@map<string, list<item: int64>> [[('a', [1, 2]), ('b',
[3])], [('c', [4, 5, 6])]]@Series[map<string, list<item: int64>>[pyarrow]]
[[('a', [1, 2]), ('b', [3])], [('c', [4, 5, 6])]]@Series[map<string,
list<item: int64>>[pyarrow]] zero-copy
+map<string,struct>:standard [[('a', {'v': 1}), ('b', {'v': 2})], [('c',
{'v': 3})]]@map<string, struct<v: int64>> [[('a', {'v': 1}), ('b', {'v':
2})], [('c', {'v': 3})]]@Series[map<string, struct<v: int64>>[pyarrow]]
[[('a', {'v': 1}), ('b', {'v': 2})], [('c', {'v': 3})]]@Series[map<string,
struct<v: int64>>[pyarrow]] zero-copy
+map<string,map<string,int64>>:standard [[('a', [('x', 1)]), ('b', [('y',
2)])], [('c', [('z', 3)])]]@map<string, map<string, int64>> [[('a', [('x',
1)]), ('b', [('y', 2)])], [('c', [('z', 3)])]]@Series[map<string, map<string,
int64>>[pyarrow]] [[('a', [('x', 1)]), ('b', [('y', 2)])], [('c', [('z',
3)])]]@Series[map<string, map<string, int64>>[pyarrow]] zero-copy
+dictionary<int32,string>:standard [a, b, a, b]@dictionary<values=string,
indices=int32, ordered=0> ['a', 'b', 'a',
'b']@Series[dictionary<values=string, indices=int32, ordered=0>[pyarrow]]
['a', 'b', 'a', 'b']@Series[dictionary<values=string, indices=int32,
ordered=0>[pyarrow]] zero-copy
+dictionary<int32,string>:nullable [a, b, None,
a]@dictionary<values=string, indices=int32, ordered=0> ['a', 'b', <NA>,
'a']@Series[dictionary<values=string, indices=int32, ordered=0>[pyarrow]]
['a', 'b', <NA>, 'a']@Series[dictionary<values=string, indices=int32,
ordered=0>[pyarrow]] zero-copy
+dictionary<int32,string>:empty []@dictionary<values=string, indices=int32,
ordered=0> []@Series[dictionary<values=string, indices=int32,
ordered=0>[pyarrow]] []@Series[dictionary<values=string, indices=int32,
ordered=0>[pyarrow]] zero-copy
+int64:sliced [2, 3, 4]@int64 [2, 3, 4]@Series[int64[pyarrow]] [2, 3,
4]@Series[int64[pyarrow]] zero-copy
+int64:sliced-with-null [2, None, 4]@int64 [2, <NA>,
4]@Series[int64[pyarrow]] [2, <NA>, 4]@Series[int64[pyarrow]] zero-copy
+int64:single-chunk [1, 2, 3]@int64 [1, 2, 3]@Series[int64[pyarrow]]
[1, 2, 3]@Series[int64[pyarrow]] zero-copy
+int64:multi-chunk [1, 2, 3, 4]@int64 [1, 2, 3,
4]@Series[int64[pyarrow]] [1, 2, 3, 4]@Series[int64[pyarrow]] zero-copy
diff --git
a/python/pyspark/tests/upstream/pyarrow/golden_pyarrow_arrow_to_pandas_zero_copy_arrow_backed.md
b/python/pyspark/tests/upstream/pyarrow/golden_pyarrow_arrow_to_pandas_zero_copy_arrow_backed.md
new file mode 100644
index 000000000000..ac177461a2ed
--- /dev/null
+++
b/python/pyspark/tests/upstream/pyarrow/golden_pyarrow_arrow_to_pandas_zero_copy_arrow_backed.md
@@ -0,0 +1,127 @@
+| test case | pyarrow array
|
types_mapper=pd.ArrowDtype, zero_copy_only=False
|
types_mapper=pd.ArrowDtype, zero_copy_only=True
| verified zero-copy
|
+|----------------------------------------|-----------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------|----------------------|
+| int8:standard | [0, 1, -1, 127, -128]@int8
| [0, 1, -1, 127,
-128]@Series[int8[pyarrow]]
| [0, 1, -1, 127,
-128]@Series[int8[pyarrow]]
| zero-copy |
+| int8:nullable | [0, 1, None]@int8
| [0, 1,
<NA>]@Series[int8[pyarrow]]
| [0, 1,
<NA>]@Series[int8[pyarrow]]
| zero-copy |
+| int8:empty | []@int8
|
[]@Series[int8[pyarrow]]
|
[]@Series[int8[pyarrow]]
| zero-copy
|
+| int16:standard | [0, 1, -1, 32767, -32768]@int16
| [0, 1, -1, 32767,
-32768]@Series[int16[pyarrow]]
| [0, 1, -1, 32767,
-32768]@Series[int16[pyarrow]]
| zero-copy |
+| int16:nullable | [0, 1, None]@int16
| [0, 1,
<NA>]@Series[int16[pyarrow]]
| [0, 1,
<NA>]@Series[int16[pyarrow]]
| zero-copy |
+| int16:empty | []@int16
|
[]@Series[int16[pyarrow]]
|
[]@Series[int16[pyarrow]]
| zero-copy
|
+| int32:standard | [0, 1, -1, 2147483647,
-2147483648]@int32 | [0, 1,
-1, 2147483647, -2147483648]@Series[int32[pyarrow]]
| [0, 1, -1, 2147483647,
-2147483648]@Series[int32[pyarrow]]
| zero-copy |
+| int32:nullable | [0, 1, None]@int32
| [0, 1,
<NA>]@Series[int32[pyarrow]]
| [0, 1,
<NA>]@Series[int32[pyarrow]]
| zero-copy |
+| int32:empty | []@int32
|
[]@Series[int32[pyarrow]]
|
[]@Series[int32[pyarrow]]
| zero-copy
|
+| int64:standard | [0, 1, -1, 9223372036854775807,
-9223372036854775808]@int64 | [0, 1, -1,
9223372036854775807, -9223372036854775808]@Series[int64[pyarrow]]
| [0, 1, -1,
9223372036854775807, -9223372036854775808]@Series[int64[pyarrow]]
| zero-copy |
+| int64:nullable | [0, 1, None]@int64
| [0, 1,
<NA>]@Series[int64[pyarrow]]
| [0, 1,
<NA>]@Series[int64[pyarrow]]
| zero-copy |
+| int64:empty | []@int64
|
[]@Series[int64[pyarrow]]
|
[]@Series[int64[pyarrow]]
| zero-copy
|
+| uint8:standard | [0, 1, 255]@uint8
| [0, 1,
255]@Series[uint8[pyarrow]]
| [0, 1,
255]@Series[uint8[pyarrow]]
| zero-copy |
+| uint8:nullable | [0, 1, None]@uint8
| [0, 1,
<NA>]@Series[uint8[pyarrow]]
| [0, 1,
<NA>]@Series[uint8[pyarrow]]
| zero-copy |
+| uint8:empty | []@uint8
|
[]@Series[uint8[pyarrow]]
|
[]@Series[uint8[pyarrow]]
| zero-copy
|
+| uint16:standard | [0, 1, 65535]@uint16
| [0, 1,
65535]@Series[uint16[pyarrow]]
| [0, 1,
65535]@Series[uint16[pyarrow]]
| zero-copy |
+| uint16:nullable | [0, 1, None]@uint16
| [0, 1,
<NA>]@Series[uint16[pyarrow]]
| [0, 1,
<NA>]@Series[uint16[pyarrow]]
| zero-copy |
+| uint16:empty | []@uint16
|
[]@Series[uint16[pyarrow]]
|
[]@Series[uint16[pyarrow]]
| zero-copy
|
+| uint32:standard | [0, 1, 4294967295]@uint32
| [0, 1,
4294967295]@Series[uint32[pyarrow]]
| [0, 1,
4294967295]@Series[uint32[pyarrow]]
| zero-copy |
+| uint32:nullable | [0, 1, None]@uint32
| [0, 1,
<NA>]@Series[uint32[pyarrow]]
| [0, 1,
<NA>]@Series[uint32[pyarrow]]
| zero-copy |
+| uint32:empty | []@uint32
|
[]@Series[uint32[pyarrow]]
|
[]@Series[uint32[pyarrow]]
| zero-copy
|
+| uint64:standard | [0, 1, 18446744073709551615]@uint64
| [0, 1,
18446744073709551615]@Series[uint64[pyarrow]]
| [0, 1,
18446744073709551615]@Series[uint64[pyarrow]]
| zero-copy |
+| uint64:nullable | [0, 1, None]@uint64
| [0, 1,
<NA>]@Series[uint64[pyarrow]]
| [0, 1,
<NA>]@Series[uint64[pyarrow]]
| zero-copy |
+| uint64:empty | []@uint64
|
[]@Series[uint64[pyarrow]]
|
[]@Series[uint64[pyarrow]]
| zero-copy
|
+| float32:standard | [0.0, 1.5, -1.5]@float32
| [0.0, 1.5,
-1.5]@Series[float[pyarrow]]
| [0.0, 1.5,
-1.5]@Series[float[pyarrow]]
| zero-copy |
+| float32:nullable | [0.0, 1.5, None]@float32
| [0.0, 1.5,
<NA>]@Series[float[pyarrow]]
| [0.0, 1.5,
<NA>]@Series[float[pyarrow]]
| zero-copy |
+| float32:empty | []@float32
|
[]@Series[float[pyarrow]]
|
[]@Series[float[pyarrow]]
| zero-copy
|
+| float64:standard | [0.0, 1.5, -1.5]@float64
| [0.0, 1.5,
-1.5]@Series[double[pyarrow]]
| [0.0, 1.5,
-1.5]@Series[double[pyarrow]]
| zero-copy |
+| float64:nullable | [0.0, 1.5, None]@float64
| [0.0, 1.5,
<NA>]@Series[double[pyarrow]]
| [0.0, 1.5,
<NA>]@Series[double[pyarrow]]
| zero-copy |
+| float64:special | [nan, inf, -inf]@float64
| [nan, inf,
-inf]@Series[double[pyarrow]]
| [nan, inf,
-inf]@Series[double[pyarrow]]
| zero-copy |
+| float64:empty | []@float64
|
[]@Series[double[pyarrow]]
|
[]@Series[double[pyarrow]]
| zero-copy
|
+| bool:standard | [True, False, True]@bool
| [True, False,
True]@Series[bool[pyarrow]]
| [True, False,
True]@Series[bool[pyarrow]]
| zero-copy |
+| bool:nullable | [True, False, None]@bool
| [True, False,
<NA>]@Series[bool[pyarrow]]
| [True, False,
<NA>]@Series[bool[pyarrow]]
| zero-copy |
+| bool:empty | []@bool
|
[]@Series[bool[pyarrow]]
|
[]@Series[bool[pyarrow]]
| zero-copy
|
+| string:standard | [hello, world, ]@string
| ['hello', 'world',
'']@Series[string[pyarrow]]
| ['hello', 'world',
'']@Series[string[pyarrow]]
| zero-copy |
+| string:nullable | [hello, None, world]@string
| ['hello', <NA>,
'world']@Series[string[pyarrow]]
| ['hello', <NA>,
'world']@Series[string[pyarrow]]
| zero-copy |
+| string:empty | []@string
|
[]@Series[string[pyarrow]]
|
[]@Series[string[pyarrow]]
| zero-copy
|
+| large_string:standard | [hello, world]@large_string
| ['hello',
'world']@Series[large_string[pyarrow]]
| ['hello',
'world']@Series[large_string[pyarrow]]
| zero-copy |
+| large_string:nullable | [hello, None]@large_string
| ['hello',
<NA>]@Series[large_string[pyarrow]]
| ['hello',
<NA>]@Series[large_string[pyarrow]]
| zero-copy |
+| large_string:empty | []@large_string
|
[]@Series[large_string[pyarrow]]
|
[]@Series[large_string[pyarrow]]
| zero-copy
|
+| binary:standard | [b'hello', b'world']@binary
| [b'hello',
b'world']@Series[binary[pyarrow]]
| [b'hello',
b'world']@Series[binary[pyarrow]]
| zero-copy |
+| binary:nullable | [b'hello', None]@binary
| [b'hello',
<NA>]@Series[binary[pyarrow]]
| [b'hello',
<NA>]@Series[binary[pyarrow]]
| zero-copy |
+| binary:empty | []@binary
|
[]@Series[binary[pyarrow]]
|
[]@Series[binary[pyarrow]]
| zero-copy
|
+| large_binary:standard | [b'hello', b'world']@large_binary
| [b'hello',
b'world']@Series[large_binary[pyarrow]]
| [b'hello',
b'world']@Series[large_binary[pyarrow]]
| zero-copy |
+| large_binary:nullable | [b'hello', None]@large_binary
| [b'hello',
<NA>]@Series[large_binary[pyarrow]]
| [b'hello',
<NA>]@Series[large_binary[pyarrow]]
| zero-copy |
+| large_binary:empty | []@large_binary
|
[]@Series[large_binary[pyarrow]]
|
[]@Series[large_binary[pyarrow]]
| zero-copy
|
+| decimal128:standard | [1.23, 4.56, -7.89]@decimal128(5,
2) | [Decimal('1.23'),
Decimal('4.56'), Decimal('-7.89')]@Series[decimal128(5, 2)[pyarrow]]
| [Decimal('1.23'), Decimal('4.56'),
Decimal('-7.89')]@Series[decimal128(5, 2)[pyarrow]]
| zero-copy |
+| decimal128:nullable | [1.23, None, 4.56]@decimal128(5, 2)
| [Decimal('1.23'),
<NA>, Decimal('4.56')]@Series[decimal128(5, 2)[pyarrow]]
| [Decimal('1.23'), <NA>,
Decimal('4.56')]@Series[decimal128(5, 2)[pyarrow]]
| zero-copy |
+| decimal128:empty | []@decimal128(5, 2)
|
[]@Series[decimal128(5, 2)[pyarrow]]
|
[]@Series[decimal128(5, 2)[pyarrow]]
| zero-copy
|
+| date32:standard | [2024-01-01,
2024-06-15]@date32[day]
| [datetime.date(2024, 1, 1), datetime.date(2024, 6,
15)]@Series[date32[day][pyarrow]]
| [datetime.date(2024, 1, 1), datetime.date(2024, 6,
15)]@Series[date32[day][pyarrow]]
| zero-copy |
+| date32:nullable | [2024-01-01, None]@date32[day]
|
[datetime.date(2024, 1, 1), <NA>]@Series[date32[day][pyarrow]]
|
[datetime.date(2024, 1, 1), <NA>]@Series[date32[day][pyarrow]]
| zero-copy
|
+| date32:empty | []@date32[day]
|
[]@Series[date32[day][pyarrow]]
|
[]@Series[date32[day][pyarrow]]
| zero-copy
|
+| date64:standard | [2024-01-01, 2024-06-15]@date64[ms]
|
[datetime.date(2024, 1, 1), datetime.date(2024, 6,
15)]@Series[date64[ms][pyarrow]]
| [datetime.date(2024, 1, 1), datetime.date(2024, 6,
15)]@Series[date64[ms][pyarrow]]
| zero-copy |
+| date64:nullable | [2024-01-01, None]@date64[ms]
|
[datetime.date(2024, 1, 1), <NA>]@Series[date64[ms][pyarrow]]
|
[datetime.date(2024, 1, 1), <NA>]@Series[date64[ms][pyarrow]]
| zero-copy
|
+| date64:empty | []@date64[ms]
|
[]@Series[date64[ms][pyarrow]]
|
[]@Series[date64[ms][pyarrow]]
| zero-copy
|
+| timestamp[s]:standard | [2024-01-01 12:00:00, 2024-06-15
18:30:00]@timestamp[s] |
[Timestamp('2024-01-01 12:00:00'), Timestamp('2024-06-15
18:30:00')]@Series[timestamp[s][pyarrow]]
| [Timestamp('2024-01-01 12:00:00'), Timestamp('2024-06-15
18:30:00')]@Series[timestamp[s][pyarrow]]
| zero-copy |
+| timestamp[s]:nullable | [2024-01-01 12:00:00,
None]@timestamp[s] |
[Timestamp('2024-01-01 12:00:00'), <NA>]@Series[timestamp[s][pyarrow]]
|
[Timestamp('2024-01-01 12:00:00'), <NA>]@Series[timestamp[s][pyarrow]]
| zero-copy
|
+| timestamp[s]:empty | []@timestamp[s]
|
[]@Series[timestamp[s][pyarrow]]
|
[]@Series[timestamp[s][pyarrow]]
| zero-copy
|
+| timestamp[ms]:standard | [2024-01-01 12:00:00, 2024-06-15
18:30:00]@timestamp[ms] |
[Timestamp('2024-01-01 12:00:00'), Timestamp('2024-06-15
18:30:00')]@Series[timestamp[ms][pyarrow]]
| [Timestamp('2024-01-01 12:00:00'), Timestamp('2024-06-15
18:30:00')]@Series[timestamp[ms][pyarrow]]
| zero-copy |
+| timestamp[ms]:nullable | [2024-01-01 12:00:00,
None]@timestamp[ms] |
[Timestamp('2024-01-01 12:00:00'), <NA>]@Series[timestamp[ms][pyarrow]]
|
[Timestamp('2024-01-01 12:00:00'), <NA>]@Series[timestamp[ms][pyarrow]]
| zero-copy
|
+| timestamp[ms]:empty | []@timestamp[ms]
|
[]@Series[timestamp[ms][pyarrow]]
|
[]@Series[timestamp[ms][pyarrow]]
| zero-copy
|
+| timestamp[us]:standard | [2024-01-01 12:00:00, 2024-06-15
18:30:00]@timestamp[us] |
[Timestamp('2024-01-01 12:00:00'), Timestamp('2024-06-15
18:30:00')]@Series[timestamp[us][pyarrow]]
| [Timestamp('2024-01-01 12:00:00'), Timestamp('2024-06-15
18:30:00')]@Series[timestamp[us][pyarrow]]
| zero-copy |
+| timestamp[us]:nullable | [2024-01-01 12:00:00,
None]@timestamp[us] |
[Timestamp('2024-01-01 12:00:00'), <NA>]@Series[timestamp[us][pyarrow]]
|
[Timestamp('2024-01-01 12:00:00'), <NA>]@Series[timestamp[us][pyarrow]]
| zero-copy
|
+| timestamp[us]:empty | []@timestamp[us]
|
[]@Series[timestamp[us][pyarrow]]
|
[]@Series[timestamp[us][pyarrow]]
| zero-copy
|
+| timestamp[ns]:standard | [2024-01-01 12:00:00, 2024-06-15
18:30:00]@timestamp[ns] |
[Timestamp('2024-01-01 12:00:00'), Timestamp('2024-06-15
18:30:00')]@Series[timestamp[ns][pyarrow]]
| [Timestamp('2024-01-01 12:00:00'), Timestamp('2024-06-15
18:30:00')]@Series[timestamp[ns][pyarrow]]
| zero-copy |
+| timestamp[ns]:nullable | [2024-01-01 12:00:00,
None]@timestamp[ns] |
[Timestamp('2024-01-01 12:00:00'), <NA>]@Series[timestamp[ns][pyarrow]]
|
[Timestamp('2024-01-01 12:00:00'), <NA>]@Series[timestamp[ns][pyarrow]]
| zero-copy
|
+| timestamp[ns]:empty | []@timestamp[ns]
|
[]@Series[timestamp[ns][pyarrow]]
|
[]@Series[timestamp[ns][pyarrow]]
| zero-copy
|
+| timestamp[us,tz=UTC]:standard | [2024-01-01 12:00:00+00:00,
2024-06-15 18:30:00+00:00]@timestamp[us, tz=UTC] |
[Timestamp('2024-01-01 12:00:00+0000', tz='UTC'), Timestamp('2024-06-15
18:30:00+0000', tz='UTC')]@Series[timestamp[us, tz=UTC][pyarrow]] |
[Timestamp('2024-01-01 12:00:00+0000', tz='UTC'), Timestamp('2024-06-15
18:30:00+0000', tz='UTC')]@Series[timestamp[us, tz=UTC][pyarrow]] | zero-copy
|
+| timestamp[us,tz=UTC]:nullable | [2024-01-01 12:00:00+00:00,
None]@timestamp[us, tz=UTC] |
[Timestamp('2024-01-01 12:00:00+0000', tz='UTC'), <NA>]@Series[timestamp[us,
tz=UTC][pyarrow]] |
[Timestamp('2024-01-01 12:00:00+0000', tz='UTC'), <NA>]@Series[timestamp[us,
tz=UTC][pyarrow]] | zero-copy
|
+| timestamp[us,tz=UTC]:empty | []@timestamp[us, tz=UTC]
|
[]@Series[timestamp[us, tz=UTC][pyarrow]]
|
[]@Series[timestamp[us, tz=UTC][pyarrow]]
| zero-copy
|
+| duration[s]:standard | [1 day, 0:00:00,
2:30:00]@duration[s] |
[Timedelta('1 days 00:00:00'), Timedelta('0 days
02:30:00')]@Series[duration[s][pyarrow]]
| [Timedelta('1 days 00:00:00'), Timedelta('0 days
02:30:00')]@Series[duration[s][pyarrow]]
| zero-copy |
+| duration[s]:nullable | [1 day, 0:00:00, None]@duration[s]
| [Timedelta('1 days
00:00:00'), <NA>]@Series[duration[s][pyarrow]]
| [Timedelta('1 days 00:00:00'),
<NA>]@Series[duration[s][pyarrow]]
| zero-copy |
+| duration[s]:empty | []@duration[s]
|
[]@Series[duration[s][pyarrow]]
|
[]@Series[duration[s][pyarrow]]
| zero-copy
|
+| duration[ms]:standard | [1 day, 0:00:00,
2:30:00]@duration[ms] |
[Timedelta('1 days 00:00:00'), Timedelta('0 days
02:30:00')]@Series[duration[ms][pyarrow]]
| [Timedelta('1 days 00:00:00'), Timedelta('0 days
02:30:00')]@Series[duration[ms][pyarrow]]
| zero-copy |
+| duration[ms]:nullable | [1 day, 0:00:00, None]@duration[ms]
| [Timedelta('1 days
00:00:00'), <NA>]@Series[duration[ms][pyarrow]]
| [Timedelta('1 days 00:00:00'),
<NA>]@Series[duration[ms][pyarrow]]
| zero-copy |
+| duration[ms]:empty | []@duration[ms]
|
[]@Series[duration[ms][pyarrow]]
|
[]@Series[duration[ms][pyarrow]]
| zero-copy
|
+| duration[us]:standard | [1 day, 0:00:00,
2:30:00]@duration[us] |
[Timedelta('1 days 00:00:00'), Timedelta('0 days
02:30:00')]@Series[duration[us][pyarrow]]
| [Timedelta('1 days 00:00:00'), Timedelta('0 days
02:30:00')]@Series[duration[us][pyarrow]]
| zero-copy |
+| duration[us]:nullable | [1 day, 0:00:00, None]@duration[us]
| [Timedelta('1 days
00:00:00'), <NA>]@Series[duration[us][pyarrow]]
| [Timedelta('1 days 00:00:00'),
<NA>]@Series[duration[us][pyarrow]]
| zero-copy |
+| duration[us]:empty | []@duration[us]
|
[]@Series[duration[us][pyarrow]]
|
[]@Series[duration[us][pyarrow]]
| zero-copy
|
+| duration[ns]:standard | [1 days 00:00:00, 0 days
02:30:00]@duration[ns] |
[Timedelta('1 days 00:00:00'), Timedelta('0 days
02:30:00')]@Series[duration[ns][pyarrow]]
| [Timedelta('1 days 00:00:00'), Timedelta('0 days
02:30:00')]@Series[duration[ns][pyarrow]]
| zero-copy |
+| duration[ns]:nullable | [1 days 00:00:00,
None]@duration[ns] |
[Timedelta('1 days 00:00:00'), <NA>]@Series[duration[ns][pyarrow]]
| [Timedelta('1 days
00:00:00'), <NA>]@Series[duration[ns][pyarrow]]
| zero-copy |
+| duration[ns]:empty | []@duration[ns]
|
[]@Series[duration[ns][pyarrow]]
|
[]@Series[duration[ns][pyarrow]]
| zero-copy
|
+| time32[s]:standard | [12:30:00, 18:45:30]@time32[s]
| [datetime.time(12,
30), datetime.time(18, 45, 30)]@Series[time32[s][pyarrow]]
| [datetime.time(12, 30),
datetime.time(18, 45, 30)]@Series[time32[s][pyarrow]]
| zero-copy |
+| time32[s]:nullable | [12:30:00, None]@time32[s]
| [datetime.time(12,
30), <NA>]@Series[time32[s][pyarrow]]
| [datetime.time(12, 30),
<NA>]@Series[time32[s][pyarrow]]
| zero-copy |
+| time32[s]:empty | []@time32[s]
|
[]@Series[time32[s][pyarrow]]
|
[]@Series[time32[s][pyarrow]]
| zero-copy
|
+| time32[ms]:standard | [12:30:00, 18:45:30]@time32[ms]
| [datetime.time(12,
30), datetime.time(18, 45, 30)]@Series[time32[ms][pyarrow]]
| [datetime.time(12, 30),
datetime.time(18, 45, 30)]@Series[time32[ms][pyarrow]]
| zero-copy |
+| time32[ms]:nullable | [12:30:00, None]@time32[ms]
| [datetime.time(12,
30), <NA>]@Series[time32[ms][pyarrow]]
| [datetime.time(12, 30),
<NA>]@Series[time32[ms][pyarrow]]
| zero-copy |
+| time32[ms]:empty | []@time32[ms]
|
[]@Series[time32[ms][pyarrow]]
|
[]@Series[time32[ms][pyarrow]]
| zero-copy
|
+| time64[us]:standard | [12:30:00, 18:45:30]@time64[us]
| [datetime.time(12,
30), datetime.time(18, 45, 30)]@Series[time64[us][pyarrow]]
| [datetime.time(12, 30),
datetime.time(18, 45, 30)]@Series[time64[us][pyarrow]]
| zero-copy |
+| time64[us]:nullable | [12:30:00, None]@time64[us]
| [datetime.time(12,
30), <NA>]@Series[time64[us][pyarrow]]
| [datetime.time(12, 30),
<NA>]@Series[time64[us][pyarrow]]
| zero-copy |
+| time64[us]:empty | []@time64[us]
|
[]@Series[time64[us][pyarrow]]
|
[]@Series[time64[us][pyarrow]]
| zero-copy
|
+| time64[ns]:standard | [12:30:00, 18:45:30]@time64[ns]
| [datetime.time(12,
30), datetime.time(18, 45, 30)]@Series[time64[ns][pyarrow]]
| [datetime.time(12, 30),
datetime.time(18, 45, 30)]@Series[time64[ns][pyarrow]]
| zero-copy |
+| time64[ns]:nullable | [12:30:00, None]@time64[ns]
| [datetime.time(12,
30), <NA>]@Series[time64[ns][pyarrow]]
| [datetime.time(12, 30),
<NA>]@Series[time64[ns][pyarrow]]
| zero-copy |
+| time64[ns]:empty | []@time64[ns]
|
[]@Series[time64[ns][pyarrow]]
|
[]@Series[time64[ns][pyarrow]]
| zero-copy
|
+| null:standard | [None, None, None]@null
| [<NA>, <NA>,
<NA>]@Series[null[pyarrow]]
| [<NA>, <NA>,
<NA>]@Series[null[pyarrow]]
| zero-copy |
+| null:empty | []@null
|
[]@Series[null[pyarrow]]
|
[]@Series[null[pyarrow]]
| zero-copy
|
+| list<int64>:standard | [[1, 2], [3, 4, 5]]@list<item:
int64> | [[1, 2], [3,
4, 5]]@Series[list<item: int64>[pyarrow]]
| [[1, 2], [3, 4,
5]]@Series[list<item: int64>[pyarrow]]
| zero-copy |
+| list<int64>:nullable | [[1, 2], None, [3]]@list<item:
int64> | [[1, 2], <NA>,
[3]]@Series[list<item: int64>[pyarrow]]
| [[1, 2], <NA>,
[3]]@Series[list<item: int64>[pyarrow]]
| zero-copy |
+| list<int64>:empty | []@list<item: int64>
|
[]@Series[list<item: int64>[pyarrow]]
|
[]@Series[list<item: int64>[pyarrow]]
| zero-copy
|
+| list<string>:standard | [['a', 'b'], ['c']]@list<item:
string> | [['a', 'b'],
['c']]@Series[list<item: string>[pyarrow]]
| [['a', 'b'],
['c']]@Series[list<item: string>[pyarrow]]
| zero-copy |
+| large_list<int64>:standard | [[1, 2], [3, 4]]@large_list<item:
int64> | [[1, 2], [3,
4]]@Series[large_list<item: int64>[pyarrow]]
| [[1, 2], [3,
4]]@Series[large_list<item: int64>[pyarrow]]
| zero-copy |
+| large_list<int64>:empty | []@large_list<item: int64>
|
[]@Series[large_list<item: int64>[pyarrow]]
|
[]@Series[large_list<item: int64>[pyarrow]]
| zero-copy
|
+| fixed_size_list<int64>[3]:standard | [[1, 2, 3], [4, 5,
6]]@fixed_size_list<item: int64>[3] |
[[1, 2, 3], [4, 5, 6]]@Series[fixed_size_list<item: int64>[3][pyarrow]]
| [[1, 2, 3], [4, 5,
6]]@Series[fixed_size_list<item: int64>[3][pyarrow]]
| zero-copy |
+| fixed_size_list<int64>[3]:empty | []@fixed_size_list<item: int64>[3]
|
[]@Series[fixed_size_list<item: int64>[3][pyarrow]]
|
[]@Series[fixed_size_list<item: int64>[3][pyarrow]]
| zero-copy
|
+| struct:standard | [[('x', 1), ('y', 'a')], [('x', 2),
('y', 'b')]]@struct<x: int64, y: string> | [{'x': 1, 'y':
'a'}, {'x': 2, 'y': 'b'}]@Series[struct<x: int64, y: string>[pyarrow]]
| [{'x': 1, 'y': 'a'}, {'x': 2, 'y':
'b'}]@Series[struct<x: int64, y: string>[pyarrow]]
| zero-copy |
+| struct:nullable | [[('x', 1), ('y', 'a')],
None]@struct<x: int64, y: string> | [{'x':
1, 'y': 'a'}, <NA>]@Series[struct<x: int64, y: string>[pyarrow]]
| [{'x': 1, 'y': 'a'},
<NA>]@Series[struct<x: int64, y: string>[pyarrow]]
| zero-copy |
+| struct:empty | []@struct<x: int64, y: string>
| []@Series[struct<x:
int64, y: string>[pyarrow]]
| []@Series[struct<x: int64, y:
string>[pyarrow]]
| zero-copy |
+| map<string,int64>:standard | [[('a', 1), ('b', 2)], [('c',
3)]]@map<string, int64> | [[('a', 1),
('b', 2)], [('c', 3)]]@Series[map<string, int64>[pyarrow]]
| [[('a', 1), ('b', 2)], [('c',
3)]]@Series[map<string, int64>[pyarrow]]
| zero-copy |
+| map<string,int64>:empty | []@map<string, int64>
|
[]@Series[map<string, int64>[pyarrow]]
|
[]@Series[map<string, int64>[pyarrow]]
| zero-copy
|
+| list<list<int64>>:standard | [[[1, 2], [3]], [[4, 5,
6]]]@list<item: list<item: int64>> | [[[1,
2], [3]], [[4, 5, 6]]]@Series[list<item: list<item: int64>>[pyarrow]]
| [[[1, 2], [3]], [[4, 5,
6]]]@Series[list<item: list<item: int64>>[pyarrow]]
| zero-copy |
+| list<struct>:standard | [[{'x': 1}, {'x': 2}], [{'x':
3}]]@list<item: struct<x: int64>> | [[{'x': 1},
{'x': 2}], [{'x': 3}]]@Series[list<item: struct<x: int64>>[pyarrow]]
| [[{'x': 1}, {'x': 2}], [{'x':
3}]]@Series[list<item: struct<x: int64>>[pyarrow]]
| zero-copy |
+| list<map<string,int64>>:standard | [[[('a', 1)], [('b', 2)]], [[('c',
3)]]]@list<item: map<string, int64>> | [[[('a', 1)],
[('b', 2)]], [[('c', 3)]]]@Series[list<item: map<string, int64>>[pyarrow]]
| [[[('a', 1)], [('b', 2)]],
[[('c', 3)]]]@Series[list<item: map<string, int64>>[pyarrow]]
| zero-copy |
+| struct<struct>:standard | [[('outer', {'inner': 1})],
[('outer', {'inner': 2})]]@struct<outer: struct<inner: int64>> | [{'outer':
{'inner': 1}}, {'outer': {'inner': 2}}]@Series[struct<outer: struct<inner:
int64>>[pyarrow]] | [{'outer': {'inner': 1}},
{'outer': {'inner': 2}}]@Series[struct<outer: struct<inner: int64>>[pyarrow]]
| zero-copy |
+| struct<list<int64>>:standard | [[('items', [1, 2, 3])], [('items',
[4, 5])]]@struct<items: list<item: int64>> | [{'items': [1, 2,
3]}, {'items': [4, 5]}]@Series[struct<items: list<item: int64>>[pyarrow]]
| [{'items': [1, 2, 3]}, {'items': [4,
5]}]@Series[struct<items: list<item: int64>>[pyarrow]]
| zero-copy |
+| struct<map<string,int64>>:standard | [[('mapping', [('a', 1)])],
[('mapping', [('b', 2)])]]@struct<mapping: map<string, int64>> |
[{'mapping': [('a', 1)]}, {'mapping': [('b', 2)]}]@Series[struct<mapping:
map<string, int64>>[pyarrow]] | [{'mapping':
[('a', 1)]}, {'mapping': [('b', 2)]}]@Series[struct<mapping: map<string,
int64>>[pyarrow]] | zero-copy |
+| map<string,list<int64>>:standard | [[('a', [1, 2]), ('b', [3])],
[('c', [4, 5, 6])]]@map<string, list<item: int64>> | [[('a', [1,
2]), ('b', [3])], [('c', [4, 5, 6])]]@Series[map<string, list<item:
int64>>[pyarrow]] | [[('a', [1, 2]),
('b', [3])], [('c', [4, 5, 6])]]@Series[map<string, list<item:
int64>>[pyarrow]] | zero-copy
|
+| map<string,struct>:standard | [[('a', {'v': 1}), ('b', {'v':
2})], [('c', {'v': 3})]]@map<string, struct<v: int64>> | [[('a', {'v':
1}), ('b', {'v': 2})], [('c', {'v': 3})]]@Series[map<string, struct<v:
int64>>[pyarrow]] | [[('a', {'v': 1}), ('b',
{'v': 2})], [('c', {'v': 3})]]@Series[map<string, struct<v: int64>>[pyarrow]]
| zero-copy |
+| map<string,map<string,int64>>:standard | [[('a', [('x', 1)]), ('b', [('y',
2)])], [('c', [('z', 3)])]]@map<string, map<string, int64>> | [[('a', [('x',
1)]), ('b', [('y', 2)])], [('c', [('z', 3)])]]@Series[map<string, map<string,
int64>>[pyarrow]] | [[('a', [('x', 1)]), ('b',
[('y', 2)])], [('c', [('z', 3)])]]@Series[map<string, map<string,
int64>>[pyarrow]] | zero-copy |
+| dictionary<int32,string>:standard | [a, b, a,
b]@dictionary<values=string, indices=int32, ordered=0>
| ['a', 'b', 'a', 'b']@Series[dictionary<values=string, indices=int32,
ordered=0>[pyarrow]] | ['a',
'b', 'a', 'b']@Series[dictionary<values=string, indices=int32,
ordered=0>[pyarrow]] |
zero-copy |
+| dictionary<int32,string>:nullable | [a, b, None,
a]@dictionary<values=string, indices=int32, ordered=0>
| ['a', 'b', <NA>, 'a']@Series[dictionary<values=string, indices=int32,
ordered=0>[pyarrow]] | ['a',
'b', <NA>, 'a']@Series[dictionary<values=string, indices=int32,
ordered=0>[pyarrow]] | zero-copy
|
+| dictionary<int32,string>:empty | []@dictionary<values=string,
indices=int32, ordered=0> |
[]@Series[dictionary<values=string, indices=int32, ordered=0>[pyarrow]]
|
[]@Series[dictionary<values=string, indices=int32, ordered=0>[pyarrow]]
| zero-copy
|
+| int64:sliced | [2, 3, 4]@int64
| [2, 3,
4]@Series[int64[pyarrow]]
| [2, 3,
4]@Series[int64[pyarrow]]
| zero-copy |
+| int64:sliced-with-null | [2, None, 4]@int64
| [2, <NA>,
4]@Series[int64[pyarrow]]
| [2, <NA>,
4]@Series[int64[pyarrow]]
| zero-copy |
+| int64:single-chunk | [1, 2, 3]@int64
| [1, 2,
3]@Series[int64[pyarrow]]
| [1, 2,
3]@Series[int64[pyarrow]]
| zero-copy |
+| int64:multi-chunk | [1, 2, 3, 4]@int64
| [1, 2, 3,
4]@Series[int64[pyarrow]]
| [1, 2, 3,
4]@Series[int64[pyarrow]]
| zero-copy |
\ No newline at end of file
diff --git
a/python/pyspark/tests/upstream/pyarrow/test_pyarrow_arrow_to_pandas_non_default.py
b/python/pyspark/tests/upstream/pyarrow/test_pyarrow_arrow_to_pandas_non_default.py
index 742b7db7389f..7e0dc3a0823a 100644
---
a/python/pyspark/tests/upstream/pyarrow/test_pyarrow_arrow_to_pandas_non_default.py
+++
b/python/pyspark/tests/upstream/pyarrow/test_pyarrow_arrow_to_pandas_non_default.py
@@ -384,6 +384,13 @@ class
PyArrowArrayToPandasZeroCopyTests(_PyArrowToPandasTestBase):
so both golden files pin the same types, and appends the layout variants
that
only matter for zero-copy: sliced (offset) arrays and single- vs
multi-chunk
ChunkedArrays.
+
+ A second test method repeats those rows with
``types_mapper=pd.ArrowDtype``, which
+ asks pandas to keep pointing at the Arrow buffers instead of materializing
them
+ into NumPy -- avoiding the copy is the point of that backend. Its own
golden file
+ records the result, so the two can be read side by side. PySpark takes
this path
+ in ``ArrowArrayToPandasConversion.convert_numpy``
+ (``python/pyspark/sql/conversion.py``).
"""
def _build_source_arrays(self):
@@ -492,6 +499,49 @@ class
PyArrowArrayToPandasZeroCopyTests(_PyArrowToPandasTestBase):
overrides=overrides,
)
+ # Output columns for the Arrow-backed conversion. Both zero_copy_only
states are
+ # recorded even though the flag is expected to make no difference here
+ COL_ARROW_ZERO_COPY_OFF = "types_mapper=pd.ArrowDtype,
zero_copy_only=False"
+ COL_ARROW_ZERO_COPY_ON = "types_mapper=pd.ArrowDtype, zero_copy_only=True"
+ COL_ARROW_VERIFIED = "verified zero-copy"
+
+ def test_to_pandas_zero_copy_only_arrow_backed(self):
+ """Test pa.Array.to_pandas(types_mapper=pd.ArrowDtype) against golden
file."""
+ sources = self._build_source_arrays()
+ row_names = list(sources.keys())
+ col_names = [
+ "pyarrow array",
+ self.COL_ARROW_ZERO_COPY_OFF,
+ self.COL_ARROW_ZERO_COPY_ON,
+ self.COL_ARROW_VERIFIED,
+ ]
+
+ # Version-specific expected values go here, keyed by (row, col), when a
+ # newer pandas/PyArrow/NumPy legitimately changes a cell's output.
+ overrides: dict[tuple[str, str], str] = {}
+
+ def compute_cell(row_name, col_name):
+ arr = sources[row_name]
+ if col_name == "pyarrow array":
+ return self.repr_value(arr, max_len=0)
+ elif col_name == self.COL_ARROW_ZERO_COPY_OFF:
+ return self._to_pandas_cell(arr, types_mapper=pd.ArrowDtype,
zero_copy_only=False)
+ elif col_name == self.COL_ARROW_ZERO_COPY_ON:
+ return self._to_pandas_cell(arr, types_mapper=pd.ArrowDtype,
zero_copy_only=True)
+ elif col_name == self.COL_ARROW_VERIFIED:
+ return self._verify_zero_copy(arr, types_mapper=pd.ArrowDtype)
+ else:
+ raise ValueError(f"unknown column: {col_name}")
+
+ self.compare_or_generate_golden_matrix(
+ row_names=row_names,
+ col_names=col_names,
+ compute_cell=compute_cell,
+
golden_file_prefix="golden_pyarrow_arrow_to_pandas_zero_copy_arrow_backed",
+ index_name="test case",
+ overrides=overrides,
+ )
+
@unittest.skipIf(
not have_pyarrow or not have_pandas or not have_numpy,
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]