Skip to content

PERF: Minimal, correct Python wrapping DEPENDS under lazy VTK loading - #71

Merged
pieper merged 3 commits into
Slicer:mainfrom
pieper:fix-lazy-vtk-wrapped-inheritance
Aug 7, 2026
Merged

PERF: Minimal, correct Python wrapping DEPENDS under lazy VTK loading#71
pieper merged 3 commits into
Slicer:mainfrom
pieper:fix-lazy-vtk-wrapped-inheritance

Conversation

@pieper

@pieper pieper commented Jul 30, 2026

Copy link
Copy Markdown
Member

Three related fixes to vtkMacroKitPythonWrap so Slicer's wrapped C++ classes
work correctly and load minimally under the lazy vtk shim
(Slicer/Slicer#9306).

1. Restore base-class module imports (BUG)

The eager import vtk used to load every VTK module up front. With lazy VTK
loading it does not, so a wrapped subclass whose VTK base class had not been
imported was built without that base and silently lost the inherited methods
(e.g. vtkOrientedGridTransform.SetDisplacementGridData). Emit, for each VTK
dependency, the <python_package>.<library_name> module in the generated
DEPENDS, following VTK's own vtkModuleWrapPython.

2. Prune DEPENDS to base-class providers (PERF)

Those DEPENDS are seeded from the whole ${VTK_LIBRARIES} link line, so every
vtk*Init.data gained a DEPENDS on all ~125 VTK Python modules — importing any
wrapped module would pull in all of VTK at startup, defeating lazy loading. A
wrapped subclass only needs its direct base class registered first (each VTK
module imports its own bases transitively), so SlicerPrunePythonModuleDepends.py
reads the merged wrapping hierarchy and keeps only the vtkmodules.* DEPENDS
whose module owns a direct base class of a wrapped class. Cross-module Slicer
dependencies are preserved unchanged; conservative (only spurious VTK modules
are removed) with a fallback to the unpruned file when the hierarchy or a Python
interpreter is unavailable.

Applied across Slicer the DEPENDS drop from 7223 to 520 entries over 54 modules,
none left empty; vtkAddonPython goes from 125 to 7 direct DEPENDS (16 vs 125
modules loaded transitively).

3. Wire hierarchy build-order dependencies generically (BUG)

VTK_WRAP_HIERARCHY only added a build-order dependency on the hierarchy
targets it wraps against when those targets already existed at configure time.
A dependency configured later in the subdirectory order (a forward reference)
had no target yet, so on the Makefiles generator the edge was silently dropped
and parallel builds could race with vtkWrapHierarchy: couldn't open file ...Hierarchy.txt. Defer the wiring with cmake_language(DEFER) to the end of
the top-level directory, where all hierarchy targets exist. This makes the
per-project workaround in Slicer/Slicer#9305 unnecessary.

Verification

isinstance/mro restored across all wrapped subclasses; a full from-scratch
Release build loads 29 of 125 VTK Python modules at GUI startup (none of the
rendering/IO/Charts tail); warm startup ~5.9s → 4.9s on an M-series Mac.

Pairs with the Slicer-side change that removes the now-obsolete hardcoded
base-class import list.

pieper and others added 3 commits July 31, 2026 11:42
…ed methods

The Python module initialization generated by vtkMacroKitPythonWrap listed a
library's Slicer dependencies in the init's vtkPythonUtil::ImportModule list
but deliberately excluded its VTK dependencies. That worked only because
importing "vtk" eagerly loaded every VTK module, so a wrapped subclass's VTK
base class was always registered by the time its Python type was built.

With lazy VTK loading (a "vtk" shim that imports vtkmodules submodules on
demand), the VTK base module is no longer guaranteed to be loaded first. A
wrapped subclass such as vtkOrientedGridTransform (whose base vtkGridTransform
lives in vtkmodules.vtkFiltersHybrid) is then built with base "object" and
silently loses every inherited method -- for example SetDisplacementGridData --
until something else happens to import the base module.

Emit the VTK dependencies in the module init's import list too, mirroring VTK's
own module wrapper (vtkModuleWrapPython): skip modules marked exclude_wrap and
name the rest "<python_package>.<library_name>" (e.g. vtkmodules.vtkFiltersHybrid).
Each library wrapped with this macro -- vtkAddon, and every Slicer or extension
library using it -- then imports its VTK base modules when it loads, so lazy VTK
loading no longer breaks cross-module inheritance.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
vtk_wrap_python seeds each wrapped module's Python dependencies from the whole
VTK link line (${VTK_LIBRARIES}), so every "vtk*Init.data" gains a DEPENDS on
all ~125 VTK Python modules. Importing a base class before its wrapped subclass
is built only requires the module that *provides* that base (each VTK module
imports its own bases transitively), so listing the full link line is both
unnecessary and, with lazy VTK loading, harmful: importing any wrapped module
would pull in all of VTK at startup.

Add SlicerPrunePythonModuleDepends.py and run it as a build step: it reads the
merged wrapping hierarchy (Class : Super ; header ; owning_module) and keeps
only the vtkmodules.* DEPENDS whose module owns a direct base class of one of
the module's wrapped classes, passing cross-module Slicer dependencies through
unchanged. The pruning is conservative -- only spurious VTK modules are removed
-- and falls back to the unpruned data file when the hierarchy file or a Python
interpreter is unavailable.

Applied across the tree the DEPENDS drop from 7223 to 520 entries over 54
modules with no module left empty. vtkAddonPython goes from 125 to 7 direct
DEPENDS (16 vs 125 modules loaded transitively), while the inheritance fix is
preserved: vtkOrientedGridTransform keeps its full method resolution order and
SetDisplacementGridData. This makes the restored base-class DEPENDS coexist
with lazy VTK loading instead of defeating it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A module's Python wrapping hierarchy must be generated after the hierarchies of
the modules it wraps against. VTK_WRAP_HIERARCHY collected those as target-level
dependencies (OTHER_HIERARCHY_TARGETS) only for hierarchy targets that already
existed when the module was configured. A dependency configured *later* in the
subdirectory order -- a forward reference, e.g. Transforms and Segmentations
wrapping against Markups MRML -- had no "<dep>Hierarchy" target yet, so on the
Makefiles generator that build-order edge was silently dropped and parallel
builds could race with:

  vtkWrapHierarchy: couldn't open file .../vtkSlicerMarkupsModuleMRMLHierarchy.txt

Incremental build trees masked it (the file was left over from a prior build);
fresh parallel trees hit it depending on scheduling.

Defer the dependency wiring with cmake_language(DEFER) to the end of the
top-level directory, where every module has been configured and all hierarchy
targets exist, and add each edge there (guarded on both targets existing). This
handles forward and backward references uniformly and removes the need for
projects to re-add known forward dependencies by hand after configuring all
their modules.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@pieper
pieper force-pushed the fix-lazy-vtk-wrapped-inheritance branch from 6a34fe7 to 98958af Compare July 31, 2026 15:55
@pieper pieper changed the title BUG: Import VTK dependency modules so wrapped subclasses keep inherited methods PERF: Minimal, correct Python wrapping DEPENDS under lazy VTK loading Jul 31, 2026
@pieper
pieper marked this pull request as ready for review August 5, 2026 19:40
@pieper
pieper requested a review from lassoan August 5, 2026 19:40
@pieper

pieper commented Aug 5, 2026

Copy link
Copy Markdown
Member Author

If you can merge this and some others I'll take out of draft then I'll check the builds tomorrow and chase down any issues.

@lassoan lassoan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

While I haven't fully reviewed all the details, the changes overall make sense, and if issues come up then we can tweak these further.

@pieper
pieper merged commit 9644d27 into Slicer:main Aug 7, 2026
2 checks passed
@pieper
pieper deleted the fix-lazy-vtk-wrapped-inheritance branch August 7, 2026 12:22
@ebrahimebrahim

ebrahimebrahim commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

This may have broken then Linux preview build: https://slicer.cdash.org/builds/4351371/build

vtkAddonMathUtilitiesPython.cxx:(.text+0x6b): undefined reference to `PyTuple_Size'

UPDATE: I am wrong, #67 is responsible!

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

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants