From 57ab29d408795cabab8d8ce221ac4fb5a9086f3f Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Tue, 17 Dec 2024 10:03:35 -0500 Subject: [PATCH] MAINT: Compat for vtk 9.4 --- .github/workflows/run-mayavi-tests.yml | 47 +++++++++++++------------- tvtk/code_gen.py | 6 ++-- tvtk/vtk_parser.py | 39 +++++++++++++-------- 3 files changed, 51 insertions(+), 41 deletions(-) diff --git a/.github/workflows/run-mayavi-tests.yml b/.github/workflows/run-mayavi-tests.yml index 587adc46..cd0c3510 100644 --- a/.github/workflows/run-mayavi-tests.yml +++ b/.github/workflows/run-mayavi-tests.yml @@ -10,54 +10,53 @@ jobs: tests: strategy: matrix: - os: [ubuntu-latest, windows-latest, macos-13] - python-version: ['3.9'] - qt-api: ['pyqt5'] - vtk: ['vtk<9.3'] + # Always test against the latest VTK, NumPy, and Qt bindings + os: [ubuntu-latest, windows-latest, macos-13, macos-15] + python-version: ['3.12'] + qt-api: ['pyside6'] # prefer the modern bindings + vtk: ['vtk>=9.4'] # always prefer the latest + # Then add some backward compat checks include: - - python-version: '3.12' + # PyQt6 and vtk 9.3 + - python-version: '3.10' qt-api: 'pyqt6' os: ubuntu-latest - vtk: 'vtk>=9.3' - - python-version: '3.12' + vtk: 'vtk==9.3' + - python-version: '3.10' qt-api: 'pyqt6' os: macos-14 # arm64 - vtk: 'vtk>=9.3' - - python-version: '3.12' + vtk: 'vtk==9.3' + - python-version: '3.10' qt-api: 'pyqt6' os: windows-latest - vtk: 'vtk>=9.3' + vtk: 'vtk==9.3' - python-version: '3.11' qt-api: 'pyqt6' os: ubuntu-latest - vtk: 'vtk>=9.3' - - python-version: '3.10' - qt-api: 'pyside6' + vtk: 'vtk==9.3' + # PyQt5 and vtk 9.2 (and one oldest Python) + - python-version: '3.9' + qt-api: 'pyqt5' os: ubuntu-latest - vtk: 'vtk<9.3' + vtk: 'vtk==9.2.5' - python-version: '3.10' - qt-api: 'pyside6' + qt-api: 'pyqt5' os: windows-latest - vtk: 'vtk<9.3' + vtk: 'vtk==9.2.5' - python-version: '3.10' qt-api: 'pyside6' os: macos-13 - vtk: 'vtk<9.3' + vtk: 'vtk==9.2.5' # Some old NumPys - python-version: '3.12' qt-api: 'pyside6' os: ubuntu-latest - vtk: 'vtk>=9.3' + vtk: 'vtk==9.3' numpy: 'numpy==1.26.4' - python-version: '3.12' qt-api: 'pyside6' os: windows-latest - vtk: 'vtk>=9.3' - numpy: 'numpy==2.0.2' - - python-version: '3.12' - qt-api: 'pyside6' - os: windows-latest - vtk: 'vtk>=9.3' + vtk: 'vtk==9.3' numpy: 'numpy==1.26.4' fail-fast: false defaults: diff --git a/tvtk/code_gen.py b/tvtk/code_gen.py index 03f7fc8f..adfb92aa 100644 --- a/tvtk/code_gen.py +++ b/tvtk/code_gen.py @@ -4,10 +4,12 @@ ..code-block:: console - $ python -m tvtk.code_gen -szvno $PWD/tvtk + $ VTK_PARSER_VERBOSE=1 python -m tvtk.code_gen -szvno $PWD/tvtk On failures you can then for example do ``import pdb; pdb.pm()`` to do -post-mortem debugging. +post-mortem debugging. If there are segfaults, the VTK_PARSER_VERBOSE=1 should help +point to the culprit, which often needs to be worked around in +``vtk_parser.py::VTKMethodParser._find_get_set_methods``. Exceptions to behaviors based on VTK versions and bugs etc. live in ``wrapper_gen.py`` and ``tvtk_parser.py``. diff --git a/tvtk/vtk_parser.py b/tvtk/vtk_parser.py index 031af8f3..7f95c6e9 100644 --- a/tvtk/vtk_parser.py +++ b/tvtk/vtk_parser.py @@ -8,13 +8,12 @@ import collections.abc import re -import types import os # Local imports (these are relative imports for a good reason). from . import class_tree from . import vtk_module as vtk -from .common import is_version_9 +from .common import vtk_major_version, vtk_minor_version class VTKMethodParser: @@ -632,7 +631,7 @@ def _find_get_set_methods(self, klass, methods): # vtkProp.Get/SetAllocatedRenderTime is private and # SetAllocatedRenderTime takes two args, don't wrap it. continue - elif (not is_version_9()) and ( + elif vtk_major_version < 9 and ( (klass_name == 'vtkGenericAttributeCollection' and method[3:] == 'AttributesToInterpolate') or (klass_name == 'vtkOverlappingAMR' and @@ -668,20 +667,18 @@ def _find_get_set_methods(self, klass, methods): # Find the default and range of the values. if gsm: - if self._verbose: - print(f'Instantiating {klass}') obj = self._get_instance(klass) # print('got instance', obj.__class__) if obj: for key, value in gsm.items(): - if not is_version_9() and ( + if vtk_major_version < 9 and ( # Evil hack, these classes segfault! (klass_name in ['vtkPolyData', 'vtkContext2D']) or # On VTK 8.1.0 this segfaults when uninitialized. (klass_name == 'vtkContextMouseEvent' and key == 'Interactor')): default = None - elif not is_version_9() and ( + elif vtk_major_version < 9 and ( klass_name == 'vtkHyperOctree' and key == 'Dimension'): # This class breaks standard VTK conventions. @@ -695,17 +692,26 @@ def _find_get_set_methods(self, klass, methods): # vtkGenericAttributeCollection.GetAttributesToInterpolate # might only be a problem if VTK is built in debug mode, # but let's keep it just to be safe. - elif is_version_9() and ( + elif vtk_major_version == 9 and ( + # Still broken on 9.4 (klass_name == 'vtkHigherOrderTetra' and - key == 'ParametricCoords') or + key == 'ParametricCoords' and vtk_minor_version < 5) or (klass_name == 'vtkGenericAttributeCollection' and - key == 'AttributesToInterpolate') or + key == 'AttributesToInterpolate' and vtk_minor_version < 4) or (klass_name == 'vtkPlotBar' and - key == 'LookupTable') or + key == 'LookupTable' and vtk_minor_version < 4) or (klass_name == 'vtkLagrangianParticleTracker' and - key == 'IntegrationModel') or + key == 'IntegrationModel' and vtk_minor_version < 4) or False): # just to simplify indentation/updates default = None + + # vtkGenericCell().GetCellFaces() segfaults on 9.4 + elif ( + (vtk_major_version, vtk_minor_version) == (9, 4) + and klass_name == "vtkGenericCell" + and key == "CellFaces" + ): + default = None else: try: if self._verbose: @@ -715,7 +721,7 @@ def _find_get_set_methods(self, klass, methods): default = None # If we don't turn these into integers, they won't instantiate - if is_version_9(): + if vtk_major_version == 9: if klass_name == "vtkAxisActor": if key in ( "AxisOnOrigin", "Use2DMode", "UseTextActor3D", @@ -765,7 +771,7 @@ def _find_get_methods(self, klass, methods): meths.remove(method) return meths - def _get_instance(self, klass): + def _get_instance(self, klass, *, do_print=True): """Given a VTK class, `klass`, returns an instance of the class. @@ -774,6 +780,8 @@ def _get_instance(self, klass): the 'state' methods and the ranges for the Get/Set methods. """ + if self._verbose and do_print: + print(f'Instantiating {klass}') obj = None try: obj = klass() @@ -783,7 +791,8 @@ def _get_instance(self, klass): n = t.get_node(klass.__name__) if n is not None: for c in n.children: - obj = self._get_instance(t.get_class(c.name)) + obj = self._get_instance(t.get_class(c.name), do_print=False) if obj: + print(f" Using super {t.get_class(c.name)} instead of {klass}") break return obj