This is an automated email from the ASF dual-hosted git repository.

AlenkaF pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow.git


The following commit(s) were added to refs/heads/main by this push:
     new f52076ca064 GH-48344: [Python] Fix Table.from_struct_array for empty 
ChunkedArray (#49869)
f52076ca064 is described below

commit f52076ca0646aac885093ffda201cbe7224eaf25
Author: Stefan Wang <[email protected]>
AuthorDate: Fri Aug 28 01:14:37 2026 -0400

    GH-48344: [Python] Fix Table.from_struct_array for empty ChunkedArray 
(#49869)
    
    ### Rationale for this change
    
    Round-tripping an empty table through `to_struct_array()` and
    `Table.from_struct_array()` raises
    `ValueError: Must pass schema, or at least one RecordBatch`.
    `to_struct_array()` returns a zero-chunk struct `ChunkedArray`, so
    `from_struct_array()` has no batch from which to recover the schema.
    
    For a zero-chunk input, `from_struct_array()` now calls `combine_chunks()` 
and
    converts the resulting empty struct array into a `RecordBatch`. Nonempty 
inputs
    keep the existing path. An empty non-struct `ChunkedArray` still raises the
    expected `TypeError`.
    
    ### What changes are included in this PR?
    
    The empty `ChunkedArray` path creates one empty batch before calling
    `Table.from_batches`. The regression coverage checks the round-trip result,
    schema preservation, and the invalid non-struct input.
    
    ### Are these changes tested?
    
    I ran this script against PyArrow 25.0.1 and the compiled branch:
    
    ```bash
    python - <<'PY'
    import pyarrow as pa
    
    value = pa.chunked_array(
        [], type=pa.struct([("ints", pa.int32()), ("floats", pa.float32())])
    )
    print(f"pyarrow={pa.__version__}, chunks={value.num_chunks}")
    try:
        result = pa.Table.from_struct_array(value)
        print(f"rows={result.num_rows}, schema={result.schema}")
    except Exception as error:
        print(f"{type(error).__name__}: {error}")
    PY
    ```
    
    Before:
    
    ```text
    pyarrow=25.0.1, chunks=0
    ValueError: Must pass schema, or at least one RecordBatch
    ```
    
    After:
    
    ```text
    pyarrow=26.0.0.dev175+ge4ad179ae, chunks=0
    rows=0, schema=ints: int32
    floats: float
    ```
    
    The focused regression suite also runs against the compiled branch:
    
    ```text
    $ python -m pytest python/pyarrow/tests/test_table.py -k from_struct_array 
-q
    .......                                                                  
[100%]
    7 passed, 208 deselected in 0.51s
    ```
    
    ### Are there any user-facing changes?
    
    `Table.from_struct_array()` now returns the expected empty table for a
    zero-chunk struct `ChunkedArray` instead of raising `ValueError`.
    
    * GitHub Issue: https://github.com/apache/arrow/issues/48344
    
    * GitHub Issue: #48344
    
    Lead-authored-by: 1fanwang <[email protected]>
    Co-authored-by: Alenka Frim <[email protected]>
    Signed-off-by: AlenkaF <[email protected]>
---
 python/pyarrow/table.pxi           | 11 +++++++----
 python/pyarrow/tests/test_table.py | 20 ++++++++++++++++++++
 2 files changed, 27 insertions(+), 4 deletions(-)

diff --git a/python/pyarrow/table.pxi b/python/pyarrow/table.pxi
index 1abe4235c41..cb6a2e0acb4 100644
--- a/python/pyarrow/table.pxi
+++ b/python/pyarrow/table.pxi
@@ -4912,10 +4912,13 @@ cdef class Table(_Tabular):
         if isinstance(struct_array, Array):
             return 
Table.from_batches([RecordBatch.from_struct_array(struct_array)])
         else:
-            return Table.from_batches([
-                RecordBatch.from_struct_array(chunk)
-                for chunk in struct_array.chunks
-            ])
+            chunks = struct_array.chunks or [struct_array.combine_chunks()]
+            return Table.from_batches(
+                [
+                    RecordBatch.from_struct_array(chunk)
+                    for chunk in chunks
+                ]
+            )
 
     def to_struct_array(self, max_chunksize=None):
         """
diff --git a/python/pyarrow/tests/test_table.py 
b/python/pyarrow/tests/test_table.py
index bf6e5773ddf..cfe47e4ed05 100644
--- a/python/pyarrow/tests/test_table.py
+++ b/python/pyarrow/tests/test_table.py
@@ -913,6 +913,11 @@ def test_table_from_struct_array_invalid():
         pa.Table.from_struct_array(pa.array(range(5)))
 
 
+def test_table_from_struct_array_empty_chunked_array_invalid():
+    with pytest.raises(TypeError, match="Argument 'struct_array' has incorrect 
type"):
+        pa.Table.from_struct_array(pa.chunked_array([], type=pa.int64()))
+
+
 def test_table_from_struct_array():
     struct_array = pa.array(
         [{"ints": 1}, {"floats": 1.0}],
@@ -941,6 +946,21 @@ def test_table_from_struct_array_chunked_array():
     ))
 
 
+def test_table_from_struct_array_for_empty_chunked_array():
+    # GH-48344
+    struct_type = pa.struct([("ints", pa.int32()), ("floats", pa.float32())])
+    empty_chunked_struct_array = pa.chunked_array([], type=struct_type)
+    result = pa.Table.from_struct_array(empty_chunked_struct_array)
+    expected = pa.Table.from_arrays(
+        [
+            pa.array([], type=pa.int32()),
+            pa.array([], type=pa.float32()),
+        ], ["ints", "floats"]
+    )
+    assert result.equals(expected)
+    assert result.schema == expected.schema
+
+
 def test_table_to_struct_array():
     table = pa.Table.from_arrays(
         [

Reply via email to