Skip to content

ENH: Implement PEP 688 buffer protocol for ITK Python#5673

Draft
Copilot wants to merge 7 commits intomainfrom
copilot/fix-failing-tests-itk-pr-5665
Draft

ENH: Implement PEP 688 buffer protocol for ITK Python#5673
Copilot wants to merge 7 commits intomainfrom
copilot/fix-failing-tests-itk-pr-5665

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Dec 5, 2025

Description

Implements PEP 688 buffer protocol support for ITK Images and ImportImageContainers, enabling zero-copy NumPy interoperability in Python 3.12+. Based on #5665 by @blowekamp with fixes for test failures and implementation refinements.

Key changes:

  • C++ infrastructure: Added PyBuffer::_GetMemoryViewFromImportImageContainer() with overflow checking
  • Python __buffer__() methods: Implemented for Image and ImportImageContainer classes
    • Reuses existing PyBuffer._GetArrayViewFromImage()
    • Extracts component types for composite pixels (RGB, Vector, etc.)
    • Handles shape conversion to NumPy C-order (channels-last)
    • Fallback _get_formatstring() for robustness
  • Build system: Wrapping for ImportImageContainer, CMake macro application
  • Backward compatibility: __array__() uses buffer protocol for Python 3.12+, falls back to array_from_image() otherwise

Usage:

import itk
import numpy as np

image = itk.Image[itk.UC, 2].New()
image.SetRegions([100, 100])
image.Allocate()

# Python 3.12+: zero-copy conversion via buffer protocol
array = np.asarray(image)  # Direct memory access

# Multi-component images follow channels-last convention
rgb_image = itk.Image[itk.RGBPixel[itk.UC], 2].New()
rgb_array = np.asarray(rgb_image)  # Shape: [height, width, 3]

PR Checklist

  • No API changes were made (or the changes have been approved)
  • No major design changes were made (or the changes have been approved)
  • Added test (or behavior not changed)
  • Updated API documentation (or API not changed)
  • Added license to new files (if any)
  • Added Python wrapping to new files (if any) as described in ITK Software Guide Section 9.5
  • Added ITK examples for all new major features (if any)

Related:

Co-authored-by: Bradley Lowekamp blowekamp@users.noreply.github.com

Original prompt

Take ITK PR #5665 and push it forward in a new PR that you cerate, addressing failing tests and refine the implementation and tests as appropriate.

We are currently getting test failures:

mac

itkImagePythonTest

Traceback (most recent call last):
File "/Users/runner/work/1/s/Modules/Core/Common/wrapping/test/itkImageTest.py", line 37, in
array = np.array(image.buffer())
File "/Users/runner/work/1/s-build/Wrapping/Generators/Python/itk/itkImagePython.py", line 27853, in buffer
memview = memoryview(self.GetPixelContainer())
TypeError: memoryview: a bytes-like object is required, not 'SwigPyObject'
itkTestDriver: Process exited with return value: 1

itkTransformSerializationTest

Traceback (most recent call last):
File "/Users/runner/work/1/s/Modules/Core/Transform/wrapping/test/itkTransformSerializationTest.py", line 167, in
assert np.array_equal(np.array(resampled_image1), np.array(resampled_image2))
AssertionError

PyImageFilterTest

Traceback (most recent call last):
File "/Users/runner/work/1/s/Wrapping/Generators/Python/Tests/PyImageFilterTest.py", line 47, in
assert np.all(np.asarray(output_image) == 42)
AssertionError
itkTestDriver: Process exited with return value: 1

PythonExtrasTest

Traceback (most recent call last):
File "/Users/runner/work/1/s/Wrapping/Generators/Python/Tests/extras.py", line 176, in
assert np.sum(comparison) == 0.0
AssertionError

And similar test failures on Linux.

See also:


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 6 commits December 5, 2025 20:53
Co-authored-by: thewtex <25432+thewtex@users.noreply.github.com>
Co-authored-by: thewtex <25432+thewtex@users.noreply.github.com>
Co-authored-by: thewtex <25432+thewtex@users.noreply.github.com>
Co-authored-by: thewtex <25432+thewtex@users.noreply.github.com>
…king

Co-authored-by: thewtex <25432+thewtex@users.noreply.github.com>
Co-authored-by: thewtex <25432+thewtex@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix failing tests and refine implementation for ITK PR #5665 ENH: Implement PEP 688 buffer protocol for ITK Python Dec 5, 2025
Copilot AI requested a review from thewtex December 5, 2025 21:15

# Get the memoryview from PyBuffer using the existing C++ method
# This returns a 1-D memoryview of the raw buffer
raw_memview = PyBufferType._GetArrayViewFromImage(self)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original PR, got a buffer from the import image container. This added a daisy chained reference to the object which actually holds the C buffer. This may help keep the Python buffer valid in some cases.

If this is not used, then perhaps the ImportImageContainer buffer interface should be removed. As I recall it was not properly wrapped for complex/composite pixel types.

@github-actions github-actions bot added area:Python wrapping Python bindings for a class type:Testing Ensure that the purpose of a class is met/the results on a wide set of test cases are correct area:Bridge Issues affecting the Bridge module area:Core Issues affecting the Core module labels Dec 8, 2025
@hjmjohnson
Copy link
Copy Markdown
Member

Status Update and Analysis (2026-04-07)

Current State

This PR (copilot-generated, based on @blowekamp's #5665) implements PEP 688 buffer protocol support for ITK Python. It has been open since 2025-12-05 with no human review activity. The upstream `main` branch has no PEP 688 support — `buffer` does not exist anywhere in the codebase today.

Relationship to Other PRs

PR Author Status Relationship
#5665 @blowekamp WIP/Draft Original exploration — blowekamp stated "I don't have any intention of taking this change further... This PR can be closed without merging"
#5673 copilot Draft This PR — built on #5665, added overflow checks, fallback `_get_formatstring`, composite pixel handling
#5653 copilot Draft Unrelated — wraps `ObjectToObjectMultiMetricv4` with explicit TVirtualImage (not PEP 688)

Review of @blowekamp's Feedback

blowekamp commented on this PR:

"The original PR got a buffer from the import image container. This added a daisy chained reference to the object which actually holds the C buffer. This may help keep the Python buffer valid in some cases. If this is not used, then perhaps the ImportImageContainer buffer interface should be removed. As I recall it was not properly wrapped for complex/composite pixel types."

This is a valid concern about memory lifetime safety. The key difference:

SimpleITK's implementation (SimpleITK#2447) solved this properly by creating a CPython ImageBuffer class that maintains a reference to the Python Image instance, preventing garbage collection of the underlying image while buffers are active.

Key Issues in This PR

  1. Memory safety: The `buffer` implementation uses `_GetArrayViewFromImage` which returns a raw memoryview without holding a Python reference to the image. If the image is garbage collected while the memoryview is alive, it becomes a dangling pointer. blowekamp's approach of going through `ImportImageContainer` was safer.

  2. `release_buffer` not implemented: PEP 688 specifies that if `buffer` exists, `release_buffer` should handle cleanup. Neither WIP: PEP 688 demonstration #5665 nor this PR implements it.

  3. Composite pixel types: The `_get_formatstring` mapping only handles scalar types and `PF2`/`PF3`. Missing: `RGBPixel`, `RGBAPixel`, `Vector`, `CovariantVector`, `SymmetricSecondRankTensor`, etc.

  4. Test quality: Tests use `try/except: pass` patterns that silently swallow failures — not appropriate for regression testing.

  5. `ImportImageContainer` wrapping: The new `.wrap` file only wraps scalar types but the `buffer` on `ImportImageContainer` could be called with composite container types.

  6. Debug code in WIP: PEP 688 demonstration #5665: The transform serialization test changes in WIP: PEP 688 demonstration #5665 add debug `print` statements and a redundant `field.Update()` call — these are development artifacts.

PEP 688 Requirements (Python 3.12+)

Per PEP 688:

  • `buffer(self, flags: int, /) -> memoryview` — creates buffer, must return memoryview
  • `release_buffer(self, buffer: memoryview, /)` — optional cleanup
  • `memoryview()` constructor invokes `buffer`
  • The returned memoryview must match the requested `flags` (from `inspect.BufferFlags`)
  • The object must keep its buffer valid as long as any memoryview referencing it exists

Recommendations

  1. Do not merge this PR as-is — the memory safety issue needs to be resolved first, following SimpleITK's approach of maintaining proper reference chains.

  2. The preferred path forward is a new human-authored PR that:

    • Follows blowekamp's approach (WIP: PEP 688 demonstration #5665) of going through `ImportImageContainer` for reference safety
    • Incorporates SimpleITK's pattern of an intermediary class maintaining image references
    • Properly handles composite pixel types
    • Implements `release_buffer` or documents why it's not needed
    • Has real assertions in tests (no `try/except: pass`)
    • Requires Python 3.12+ (which ITK already effectively requires via ENH: Update to minimum Python support to py310 #6012)
  3. Close WIP: PEP 688 demonstration #5665 — author has stated it should be closed

  4. This PR (ENH: Implement PEP 688 buffer protocol for ITK Python #5673) can serve as reference material but should not be merged without significant rework addressing the memory safety concern

@dzenanz
Copy link
Copy Markdown
Member

dzenanz commented Apr 7, 2026

Summary of recommendation: some human should implement this 😄

@hjmjohnson
Copy link
Copy Markdown
Member

Superseded by #6020 which addresses the memory safety concerns raised here. The new implementation uses existing PyBuffer._GetArrayViewFromImage() and pure Python reshaping, with __array_interface__ gated behind an opt-in itk.SIMULATE_PEP688 flag to preserve backward compatibility.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:Bridge Issues affecting the Bridge module area:Core Issues affecting the Core module area:Python wrapping Python bindings for a class type:Testing Ensure that the purpose of a class is met/the results on a wide set of test cases are correct

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants