This is an automated email from the ASF dual-hosted git repository.
JingsongLi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/paimon.git
The following commit(s) were added to refs/heads/master by this push:
new bb4a3e4f20 [python] Implement write_row in HdfsNativeFileIO (#10192)
bb4a3e4f20 is described below
commit bb4a3e4f20b3339e9edfc327e49f0b453ae8ae72
Author: jackylee <[email protected]>
AuthorDate: Sat Oct 3 17:10:19 2026 +0800
[python] Implement write_row in HdfsNativeFileIO (#10192)
---
.../pypaimon/filesystem/hdfs_native_file_io.py | 16 +++++
.../pypaimon/tests/hdfs_native_write_row_test.py | 74 ++++++++++++++++++++++
2 files changed, 90 insertions(+)
diff --git a/paimon-python/pypaimon/filesystem/hdfs_native_file_io.py
b/paimon-python/pypaimon/filesystem/hdfs_native_file_io.py
index 78cad2140e..e06b683ef7 100644
--- a/paimon-python/pypaimon/filesystem/hdfs_native_file_io.py
+++ b/paimon-python/pypaimon/filesystem/hdfs_native_file_io.py
@@ -617,6 +617,22 @@ class HdfsNativeFileIO(FileIO):
fastavro.writer(output_stream, avro_schema,
record_generator(), codec=codec, **kwargs)
+ def write_row(self, path: str, data: pyarrow.Table, fields=None,
+ zstd_level: int = 1, **kwargs):
+ try:
+ from pypaimon.write.writer.format_row_writer import FormatRowWriter
+
+ if fields is None:
+ fields = PyarrowFieldParser.to_paimon_schema(data.schema)
+
+ with self.new_output_stream(path) as output_stream:
+ writer = FormatRowWriter(output_stream, fields,
zstd_level=zstd_level)
+ writer.write_table(data)
+ writer.close()
+ except Exception as e:
+ self.delete_quietly(path)
+ raise RuntimeError(f"Failed to write row file {path}: {e}") from e
+
def write_blob(self, path: str, data: pyarrow.Table, **kwargs):
try:
if data.num_columns != 1:
diff --git a/paimon-python/pypaimon/tests/hdfs_native_write_row_test.py
b/paimon-python/pypaimon/tests/hdfs_native_write_row_test.py
new file mode 100644
index 0000000000..a46d4d9aa0
--- /dev/null
+++ b/paimon-python/pypaimon/tests/hdfs_native_write_row_test.py
@@ -0,0 +1,74 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+import os
+import tempfile
+import unittest
+
+import pyarrow as pa
+
+from pypaimon.filesystem.hdfs_native_file_io import HdfsNativeFileIO
+from pypaimon.read.reader.format_row_reader import FormatRowReader
+from pypaimon.schema.data_types import AtomicType, DataField
+
+
+class _LocalReadIO:
+ def get_file_size(self, path):
+ return os.path.getsize(path)
+
+ def new_input_stream(self, path):
+ return open(path, 'rb')
+
+
+class HdfsNativeWriteRowTest(unittest.TestCase):
+ """HdfsNativeFileIO is the default hdfs:// backend and must implement
+ write_row; before this it inherited FileIO.write_row's NotImplementedError,
+ so FILE_FORMAT_ROW / data-evolution sidecar writes on native HDFS
crashed."""
+
+ def test_write_row_roundtrips(self):
+ fields = [DataField(0, "id", AtomicType("INT")),
+ DataField(1, "name", AtomicType("STRING"))]
+ table = pa.table({"id": pa.array([1, 2, 3], pa.int32()),
+ "name": ["a", "b", "c"]})
+
+ # Bypass the HDFS __init__; write_row only needs new_output_stream
+ # (and delete_quietly on failure), which we point at the local FS.
+ fio = object.__new__(HdfsNativeFileIO)
+ fio.new_output_stream = lambda p: open(p, 'wb')
+ fio.delete_quietly = lambda p: None
+
+ with tempfile.TemporaryDirectory() as d:
+ path = os.path.join(d, "data.row")
+ fio.write_row(path, table, fields=fields)
+
+ reader = FormatRowReader(_LocalReadIO(), path,
+ [f.name for f in fields], fields, None)
+ batches = []
+ while True:
+ batch = reader.read_arrow_batch()
+ if batch is None:
+ break
+ batches.append(batch)
+ reader.close()
+ result = pa.Table.from_batches(batches)
+
+ self.assertEqual(result.column("id").to_pylist(), [1, 2, 3])
+ self.assertEqual(result.column("name").to_pylist(), ["a", "b", "c"])
+
+
+if __name__ == '__main__':
+ unittest.main()