COMP: Build all ITK Python wrapping as abi3 (stable ABI) - #6715
COMP: Build all ITK Python wrapping as abi3 (stable ABI)#6715hjmjohnson wants to merge 9 commits into
Conversation
dzenanz
left a comment
There was a problem hiding this comment.
Looks good on a glance. Matt and Brad should review this.
|
Force-push
Root cause
It failed on exactly one configuration because three things have to line up: an undefined CMake variable expands to empty without error, ELF shared objects tolerate undefined symbols, and static archives never resolve them at all. Only a shared macOS build links Verified by configuring |
This comment was marked as resolved.
This comment was marked as resolved.
Exchange pointers with VTK's Python layer through the `__this__` and `Addr=0x...` encodings using only Limited API calls, instead of through vtkPythonUtil, whose header chain accesses PyTypeObject members that Py_LIMITED_API hides. Dropping VTK::WrappingPythonCore from the wrapping link interface is required for correctness, not tidiness: that library is built against one libpython, so an extension linking it cannot be version-agnostic. Neither encoding is documented VTK API, so PythonVtkGlueABI3EncodingTest asserts both still hold and PythonVtkGlueRoundTripTest exercises the typemaps end to end. Closes: InsightSoftwareConsortium#6711
The abi3 floor and the Python wrapping floor are both 3.11, so the non-SABI branch was unreachable for every supported interpreter.
The Python3::Module fallback was reachable only without the limited API. A missing SABIModule target is now an explicit configure error rather than a silent downgrade.
Python wrapping requires 3.11, which is also the abi3 floor, so the option could only ever be on. Every wrapped module is now built as an abi3 extension and the CMake >= 3.26 requirement applies whenever ITK_WRAP_PYTHON is enabled.
Development.SABIModule requires CMake >= 3.26. Builds without ITK_WRAP_PYTHON keep the older floor.
Every wrapped module is an abi3 extension built against Python3::SABIModule; nothing refers to Python3::Module.
The component is no longer requested, so it can neither be missing nor inform the remedy text.
Two independent 3.11 literals could drift apart even though abi3-only wrapping makes them necessarily equal.
The component list no longer depends on a discovered version, so the range search and the exact-version re-search collapse into one call. NumPy now follows BUILD_TESTING consistently: the first search had always demanded it, while only the second was checked.
c046803 to
d542b02
Compare
|
Two force-pushes just landed, split by concern: |
|
What are the implications of this for building against the Python Free Thread ABI. Currently Free Threads are not stable and there is expected to be a separate stable ABI for 3.15: |
|
I read this, and the requirements for |
|
I do think this is a mandatory step toward abi3t, but I am not an expert on those details. The primary motivator was to remove complexity in the cmake options, and remove unnecessary dependance on non-abi3 code. |
Makes
ITKVtkGlue's Python wrapping stable-ABI clean, which removes the last blocker to building all ITK Python wrapping asabi3. With that gone,ITK_USE_PYTHON_LIMITED_APIhad only one reachable value, so it and the surrounding machinery are deleted (CMake/ITKSetPython3Vars.cmake: 33 added, 100 removed).Closes #6711. Also lowers the cost of #4656 (weekly PyPI wheels): abi3-only means one wheel per platform instead of one per Python version.
Verified on macOS 26 / arm64 only. No Linux or Windows verification, and no Slicer test result — see "Environments verified" for exactly what was and was not exercised.
The ITKVtkGlue fix, and a defect in the approach #6711 proposed
VtkGlue.iexchanged pointers with VTK throughvtkPythonUtil, whose header chain reachesPyVTKObject.hand touchesPyTypeObjectmembers thatPy_LIMITED_APIhides. The typemaps now use Limited API calls only, parsing VTK's__this__encoding and reconstructing throughAddr=0x....VTK::WrappingPythonCoreis dropped from the wrapping link interface. This is a correctness requirement rather than tidying: that library is built against onelibpython, so an extension linking it cannot be version-agnostic.Defect in the sketch from #6711. The issue proposed
PyObject_CallFunction(cls, "s", addr), i.e.vtkImageData("Addr=0x…"). On VTK 9.6.2 this fails, but only forvtkImageDataandvtkPolyData, and only in the pointer-to-object direction. The cause is not ABI-related:vtkmodules/util/data_model.pyregisters@vtkImageData.overridewith a Python subclass whose__init__(self, **kwargs)is keyword-only, so the positional address string never reaches VTK's C-level reconstruction.vtkImageExport,vtkImageImport,vtkObjectandvtkPointshave no override and were unaffected.The fix is to call
__new__explicitly: it bypasses the override's__init__while still returning the enhancedImageDatatype, with pointer identity preserved.Neither
__this__norAddr=0x...is documented VTK API, soPythonVtkGlueABI3EncodingTestexists to fail loudly and specifically if VTK changes either one.Why removing the option is safe rather than merely convenient
ITK_WRAP_PYTHON_MINIMUM_VERSIONand the abi3 floor are both 3.11, and the option auto-enabled whenever the interpreter met the abi3 floor. Since wrapping already hard-errors below 3.11, that condition was true for every supported interpreter. The non-SABI branch was reachable only by setting the optionOFFexplicitly, and the only documented reason to do so was ITKVtkGlue'sFATAL_ERROR.Empirically: removing the option changed 524 build targets, not 8237. The
python3_add_library()arguments were byte-identical before and after, so only relinking occurred.FindPython3suppliesDevelopment.Moduleimplicitly alongsideDevelopment.SABIModule— the configure log reports it as found even though it is no longer requested. That is why dropping the explicit request is safe.Environments verified (and what was not)
Platform: macOS 26 / arm64, Apple clang via conda-forge toolchain.
VTK:
github.com/slicer/VTKat6181bb1223bbc499a340a1644f5356e7e152c318— the SHA Slicer'sSuperBuild/External_VTK.cmakepins for its 9.6 series — built with Qt6 6.9.1, full rendering, and Python wrapping.Build and test
ctest -R PythonPythonVtkGlueABI3EncodingTestPythonVtkGlueRoundTripTestFAILED:pre-commit run --all-filesArtifact audit
abi3modules_ITKVtkGluePython.abi3.soundefinedPy*symbolsPyVTK*/vtkPythonUtilsymbolslibvtkWrappingPythonCorelinkedlibpythonlinkedMulti-interpreter. Extensions were built against pixi Python 3.13.9; the others are Homebrew builds, a different distribution. VTK's Python wrappers were built separately for cp311/cp312/cp313/cp314 against the same VTK C++ libraries, so each row pairs the same ITK
.abi3.sowith a different version-specific VTK.The 3.11 and 3.14 rows are the substantive ones: that
.abi3.sowas compiled against cp313 VTK headers yet drove full round-trips against cp311 and cp314 VTK modules. Under the previous design, which linkedVTK::WrappingPythonCore, those pairings were structurally impossible.Configure matrix
BUILD_TESTING=OFFDownstream
ITK_WRAP_PYTHON=OFF,Module_ITKVtkGlue=ON: 2730/2730 targets, 0 errors.-isystem $CONDA_PREFIX/includepresent, conda'siconv.h(#define iconv_open libiconv_open) is paired with the macOS SDK'slibiconv.tbd, giving undefined_libiconv_open; removing that flag instead breaks Slicer's symbol-prefixed zlib with undefined_crc32. This is an environment issue, not a code one, and is independent of this branch: the diff is wrapping-only and Slicer's ITK isITK_WRAP_PYTHON=OFF, which makes this branch a no-op for it.Not verified: Linux, Windows, any non-arm64 target, and any completed Slicer test suite.
Three things that look simplifiable under abi3 but are not
Recorded so they are not "fixed" later:
ITKSetPython3Vars.cmakegoverns which headers and import library the compile uses. abi3 relaxes which interpreters the result runs on — a different guarantee.WITH_SOABIis what produces the.abi3.sosuffix underUSE_SABI.unset(Python3_FIND_ABI)is an unrelated FindPython component-discovery workaround.Also note the per-Python-version wheel matrix that SABI-only would collapse lives in ITKPythonPackage, not this repository. This branch enables that follow-up; it does not perform it.
That follow-up is what makes #4656 (weekly builds published to PyPI) materially cheaper. ITK supports Python 3.11-3.14 over roughly five platform targets (linux x86_64/aarch64, macOS x86_64/arm64, Windows), so a release today is on the order of 20 wheels; abi3-only reduces that to about 5 — one per platform, valid for every supported interpreter. For a weekly cadence that is a 4x cut in build jobs, upload volume, and failure surface, which is the difference between a schedule that is sustainable and one that is not.
This does not resolve #4656 — the scheduled workflow and the PyPI publishing still have to be built in ITKPythonPackage. It only removes the combinatorial part of the cost.