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 bd9b251076 [python] Fix Blob.from_bytes use wrong FileIO (#8489)
bd9b251076 is described below
commit bd9b251076169224fd6cf8f6514cc9c67c3f101b
Author: umi <[email protected]>
AuthorDate: Tue Jul 7 14:48:43 2026 +0800
[python] Fix Blob.from_bytes use wrong FileIO (#8489)
---
paimon-python/pypaimon/table/row/blob.py | 13 +++++--
paimon-python/pypaimon/tests/blob_test.py | 61 +++++++++++++++++++++++++++++++
2 files changed, 70 insertions(+), 4 deletions(-)
diff --git a/paimon-python/pypaimon/table/row/blob.py
b/paimon-python/pypaimon/table/row/blob.py
index eb2f00b764..d1e2e262f6 100644
--- a/paimon-python/pypaimon/table/row/blob.py
+++ b/paimon-python/pypaimon/table/row/blob.py
@@ -391,7 +391,9 @@ class Blob(ABC):
return BlobView(view_struct)
@staticmethod
- def from_bytes(data: Optional[bytes], file_io=None, allow_blob_data: bool
= True) -> Optional['Blob']:
+ def from_bytes(
+ data: Optional[bytes], file_io=None, uri_reader_factory=None,
allow_blob_data: bool = True,
+ ) -> Optional['Blob']:
if data is None:
return None
if not isinstance(data, (bytes, bytearray)):
@@ -405,10 +407,13 @@ class Blob(ABC):
"Expected BlobDescriptor bytes, got raw bytes
(allow_blob_data=False)"
)
if is_descriptor:
- if file_io is None:
- raise ValueError("file_io is required to resolve
BlobDescriptor bytes")
descriptor = BlobDescriptor.deserialize(data)
- uri_reader = file_io.uri_reader_factory.create(descriptor.uri)
+ if uri_reader_factory is None:
+ if file_io is None:
+ raise ValueError("file_io is required to resolve
BlobDescriptor bytes")
+ uri_reader = UriReader.from_file(file_io)
+ else:
+ uri_reader = uri_reader_factory.create(descriptor.uri)
return BlobRef(uri_reader, descriptor)
return BlobData(data)
diff --git a/paimon-python/pypaimon/tests/blob_test.py
b/paimon-python/pypaimon/tests/blob_test.py
index ac31c399f3..14508d1e10 100644
--- a/paimon-python/pypaimon/tests/blob_test.py
+++ b/paimon-python/pypaimon/tests/blob_test.py
@@ -156,6 +156,67 @@ class BlobTest(unittest.TestCase):
self.assertIsInstance(blob, BlobRef)
self.assertEqual(blob.to_data(), data)
+ def test_from_bytes_descriptor_uses_explicit_uri_reader_factory(self):
+ data = b"factory blob content"
+ descriptor = BlobDescriptor("custom://bucket/blob.bin", 0, len(data))
+
+ class RecordingUriReader:
+ def __init__(self):
+ self.opened_uris = []
+
+ def new_input_stream(self, uri):
+ self.opened_uris.append(uri)
+ return io.BytesIO(data)
+
+ class RecordingFactory:
+ def __init__(self, reader):
+ self.reader = reader
+ self.created_uris = []
+
+ def create(self, uri):
+ self.created_uris.append(uri)
+ return self.reader
+
+ class FactoryFileIO:
+ def __init__(self, factory):
+ self.uri_reader_factory = factory
+
+ def new_input_stream(self, path):
+ raise AssertionError("from_bytes should use the explicit
uri_reader_factory")
+
+ reader = RecordingUriReader()
+ factory = RecordingFactory(reader)
+ blob = Blob.from_bytes(
+ descriptor.serialize(), FactoryFileIO(factory),
uri_reader_factory=factory)
+
+ self.assertEqual(blob.to_data(), data)
+ self.assertEqual(factory.created_uris, [descriptor.uri])
+ self.assertEqual(reader.opened_uris, [descriptor.uri])
+
+ def test_from_bytes_descriptor_can_force_file_io_reader(self):
+ data = b"file backed blob content"
+ descriptor = BlobDescriptor("file-backed/blob.bin", 0, len(data))
+
+ class FailingFactory:
+ def create(self, uri):
+ raise AssertionError("Explicit uri_reader_factory=None should
skip factory")
+
+ class FileBackedIO:
+ def __init__(self):
+ self.uri_reader_factory = FailingFactory()
+ self.opened_paths = []
+
+ def new_input_stream(self, path):
+ self.opened_paths.append(path)
+ return io.BytesIO(data)
+
+ file_io = FileBackedIO()
+ blob = Blob.from_bytes(
+ descriptor.serialize(), file_io, uri_reader_factory=None)
+
+ self.assertEqual(blob.to_data(), data)
+ self.assertEqual(file_io.opened_paths, [descriptor.uri])
+
def test_from_bytes_descriptor_without_file_io_raises(self):
descriptor = BlobDescriptor("/tmp/fake", 0, 10)
serialized = descriptor.serialize()