From 0031d47559f79a2fb13af706d95912ac53434d29 Mon Sep 17 00:00:00 2001 From: Lazizbek Ergashev Date: Mon, 24 Aug 2026 23:18:05 +0500 Subject: [PATCH 1/3] Use borrowed data when exporting an image created by Image.fromarrow --- Tests/test_pyarrow.py | 20 ++++++++++++++++++++ src/libImaging/Arrow.c | 4 ++++ 2 files changed, 24 insertions(+) diff --git a/Tests/test_pyarrow.py b/Tests/test_pyarrow.py index f282f2c0059..0e322e9511e 100644 --- a/Tests/test_pyarrow.py +++ b/Tests/test_pyarrow.py @@ -218,6 +218,26 @@ def test_fromarray(mode: str, data_tp: DataShape, mask: list[int] | None) -> Non _test_img_equals_pyarray(img, arr, mask, elts_per_pixel) +@pytest.mark.parametrize( + "mode, data_tp, mask", + ( + ("L", DataShape(pyarrow.uint8(), 3, 1), None), + ("RGBA", UINT_ARR, None), + ), +) +def test_fromarray_to_array( + mode: str, data_tp: DataShape, mask: list[int] | None +) -> None: + dtype, elt, elts_per_pixel = data_tp + + ct_pixels = TEST_IMAGE_SIZE[0] * TEST_IMAGE_SIZE[1] + arr = pyarrow.array([elt] * (ct_pixels * elts_per_pixel), type=dtype) + img = Image.fromarrow(arr, mode, TEST_IMAGE_SIZE) + + exported = pyarrow.array(img) # type: ignore[call-overload] + _test_img_equals_pyarray(img, exported, mask, elts_per_pixel) + + @pytest.mark.parametrize( "mode, data_tp, mask", ( diff --git a/src/libImaging/Arrow.c b/src/libImaging/Arrow.c index 3ca227d4f8e..b061dae4099 100644 --- a/src/libImaging/Arrow.c +++ b/src/libImaging/Arrow.c @@ -324,6 +324,8 @@ export_single_channel_array(Imaging im, struct ArrowArray *array) { if (im->block) { array->buffers[1] = im->block; + } else if (im->arrow_array_capsule) { + array->buffers[1] = im->image[0]; } else { array->buffers[1] = im->blocks[0].ptr; } @@ -407,6 +409,8 @@ export_fixed_pixel_array(Imaging im, struct ArrowArray *array) { if (im->block) { array->children[0]->buffers[1] = im->block; + } else if (im->arrow_array_capsule) { + array->children[0]->buffers[1] = im->image[0]; } else { array->children[0]->buffers[1] = im->blocks[0].ptr; } From 88588cb15532b87099975cf90c2d6e1081f5992d Mon Sep 17 00:00:00 2001 From: Lazizbek Ergashev Date: Mon, 24 Aug 2026 23:30:01 +0500 Subject: [PATCH 2/3] Test exporting borrowed images in more modes --- Tests/test_pyarrow.py | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/Tests/test_pyarrow.py b/Tests/test_pyarrow.py index 0e322e9511e..ec12effd870 100644 --- a/Tests/test_pyarrow.py +++ b/Tests/test_pyarrow.py @@ -219,23 +219,35 @@ def test_fromarray(mode: str, data_tp: DataShape, mask: list[int] | None) -> Non @pytest.mark.parametrize( - "mode, data_tp, mask", + "mode, dtype, mask", ( - ("L", DataShape(pyarrow.uint8(), 3, 1), None), - ("RGBA", UINT_ARR, None), + ("L", pyarrow.uint8(), None), + ("I", pyarrow.int32(), None), + ("F", pyarrow.float32(), None), + ("LA", fl_uint8_4_type, [0, 3]), + ("RGB", fl_uint8_4_type, [0, 1, 2]), + ("RGBA", fl_uint8_4_type, None), ), ) def test_fromarray_to_array( - mode: str, data_tp: DataShape, mask: list[int] | None + mode: str, dtype: pyarrow.DataType, mask: list[int] | None ) -> None: - dtype, elt, elts_per_pixel = data_tp + img = hopper(mode) - ct_pixels = TEST_IMAGE_SIZE[0] * TEST_IMAGE_SIZE[1] - arr = pyarrow.array([elt] * (ct_pixels * elts_per_pixel), type=dtype) - img = Image.fromarrow(arr, mode, TEST_IMAGE_SIZE) + borrowed = Image.fromarrow(pyarrow.array(img), mode, img.size) # type: ignore[call-overload] + + arr = pyarrow.array(borrowed) # type: ignore[call-overload] + _test_img_equals_pyarray(img, arr, mask) + assert arr.type == dtype + + +def test_fromarray_interleaved_to_array() -> None: + img = hopper("RGBA") + + arr = pyarrow.array(list(img.tobytes()), type=pyarrow.uint8()) + borrowed = Image.fromarrow(arr, "RGBA", img.size) - exported = pyarrow.array(img) # type: ignore[call-overload] - _test_img_equals_pyarray(img, exported, mask, elts_per_pixel) + _test_img_equals_pyarray(img, pyarrow.array(borrowed), None) # type: ignore[call-overload] @pytest.mark.parametrize( From bd55c4120b36b84263313c167ad2141d0d119bb4 Mon Sep 17 00:00:00 2001 From: Lazizbek Ergashev Date: Sat, 29 Aug 2026 13:17:17 +0500 Subject: [PATCH 3/3] Extend test_fromarray_to_array to all test_to_array modes --- Tests/test_pyarrow.py | 40 +++++++++++++++------------------------- 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/Tests/test_pyarrow.py b/Tests/test_pyarrow.py index ec12effd870..2dfa440d181 100644 --- a/Tests/test_pyarrow.py +++ b/Tests/test_pyarrow.py @@ -84,21 +84,21 @@ def _test_img_equals_int32_pyarray( ).type -@pytest.mark.parametrize( - "mode, dtype, mask", - ( - ("L", pyarrow.uint8(), None), - ("I", pyarrow.int32(), None), - ("F", pyarrow.float32(), None), - ("LA", fl_uint8_4_type, [0, 3]), - ("RGB", fl_uint8_4_type, [0, 1, 2]), - ("RGBA", fl_uint8_4_type, None), - ("RGBX", fl_uint8_4_type, None), - ("CMYK", fl_uint8_4_type, None), - ("YCbCr", fl_uint8_4_type, [0, 1, 2]), - ("HSV", fl_uint8_4_type, [0, 1, 2]), - ), +mode_dtype_mask = ( + ("L", pyarrow.uint8(), None), + ("I", pyarrow.int32(), None), + ("F", pyarrow.float32(), None), + ("LA", fl_uint8_4_type, [0, 3]), + ("RGB", fl_uint8_4_type, [0, 1, 2]), + ("RGBA", fl_uint8_4_type, None), + ("RGBX", fl_uint8_4_type, None), + ("CMYK", fl_uint8_4_type, None), + ("YCbCr", fl_uint8_4_type, [0, 1, 2]), + ("HSV", fl_uint8_4_type, [0, 1, 2]), ) + + +@pytest.mark.parametrize("mode, dtype, mask", mode_dtype_mask) def test_to_array(mode: str, dtype: pyarrow.DataType, mask: list[int] | None) -> None: img = hopper(mode) @@ -218,17 +218,7 @@ def test_fromarray(mode: str, data_tp: DataShape, mask: list[int] | None) -> Non _test_img_equals_pyarray(img, arr, mask, elts_per_pixel) -@pytest.mark.parametrize( - "mode, dtype, mask", - ( - ("L", pyarrow.uint8(), None), - ("I", pyarrow.int32(), None), - ("F", pyarrow.float32(), None), - ("LA", fl_uint8_4_type, [0, 3]), - ("RGB", fl_uint8_4_type, [0, 1, 2]), - ("RGBA", fl_uint8_4_type, None), - ), -) +@pytest.mark.parametrize("mode, dtype, mask", mode_dtype_mask) def test_fromarray_to_array( mode: str, dtype: pyarrow.DataType, mask: list[int] | None ) -> None: