From 57ab29d408795cabab8d8ce221ac4fb5a9086f3f Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Tue, 17 Dec 2024 10:03:35 -0500 Subject: [PATCH 01/14] 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 From 2f4b816bcb0e6ba0b0919e2b689edd321e4245f6 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Wed, 18 Dec 2024 14:33:36 -0500 Subject: [PATCH 02/14] FIX: Move --- tvtk/vtk_parser.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/tvtk/vtk_parser.py b/tvtk/vtk_parser.py index 43ef23cb..7f95c6e9 100644 --- a/tvtk/vtk_parser.py +++ b/tvtk/vtk_parser.py @@ -653,9 +653,6 @@ def _find_get_set_methods(self, klass, methods): # These hang on Windows (and maybe Fedora 34) elif (klass_name in ('vtkDataEncoder', 'vtkWebApplication')): continue - # This crashes on VTK version 9.4.0 - elif (klass_name == 'vtkGenericCell' and method[3:] == 'CellFaces'): - continue # we can actually process it elif ('Get' + method[3:]) in methods: key = method[3:] From 90e81bccb6fbe7c7f06cfeb0f3756df715a2c59b Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Wed, 18 Dec 2024 15:00:32 -0500 Subject: [PATCH 03/14] MAINT: 9+ --- README.rst | 13 +++--- mayavi/components/ui/actor.py | 16 ++----- mayavi/tests/test_builtin_image.py | 3 -- mayavi/tools/figure.py | 11 +---- pyproject.toml | 2 +- tvtk/common.py | 5 -- tvtk/tests/test_class_tree.py | 13 +----- tvtk/tests/test_vtk_parser.py | 41 ++++++----------- tvtk/vtk_parser.py | 73 ++++++++++-------------------- tvtk/wrapper_gen.py | 46 +++++++++---------- 10 files changed, 73 insertions(+), 150 deletions(-) diff --git a/README.rst b/README.rst index 5d842670..f6829bd1 100644 --- a/README.rst +++ b/README.rst @@ -86,12 +86,11 @@ By itself Mayavi is not a difficult package to install but its dependencies are unfortunately rather heavy. However, many of these dependencies are now available as wheels on PyPI. The two critical dependencies are, - 1. VTK_ - 2. A GUI toolkit, either PyQt4_, PySide_, PySide2_, PyQt5_ or wxPython_. + 1. VTK_ >= 9.0 + 2. A GUI toolkit, either PySide6_, PyQt6_, PySide2_, PyQt5_, or wxPython_. The latest VTK wheels are available on all the major platforms (Windows, -MacOS, and Linux), but only for 64 bit machines. Python 3.x is fully supported -on all these operating systems and Python 2.7.x on MacOS and Linux. If you are +MacOS, and Linux). If you are out of luck, and your platform is not supported then you will need to install VTK yourself using your particular distribution as discussed in the `General Build and Installation instructions @@ -133,7 +132,7 @@ If you are unable to do this, read the documentation above and find a way to install VTK and a suitable UI toolkit and then repeat the above. If you are interested in the jupyter notebook support as well, do the -following (after ensuring that you have jupyter installed of course. +following (after ensuring that you have jupyter installed of course. **Note, the Jupyter notebook function is only supported starting mayavi version 4.5.0**):: @@ -200,8 +199,8 @@ Test suite The basic test suites for tvtk and mayavi can be run using nose:: - nosetests -v tvtk/tests - nosetests -v mayavi + pytest -v tvtk/tests + pytest -v mayavi The integration tests:: diff --git a/mayavi/components/ui/actor.py b/mayavi/components/ui/actor.py index d5838674..7f0c6899 100644 --- a/mayavi/components/ui/actor.py +++ b/mayavi/components/ui/actor.py @@ -14,8 +14,6 @@ from traitsui.api import (View, Group, Item, InstanceEditor, DropEditor, Tabbed) -from tvtk.api import tvtk -from tvtk.common import vtk_major_version # The properties view group. _prop_base_group = Group(Item(name='representation'), @@ -48,16 +46,10 @@ ) # The Texture's view group -if vtk_major_version > 7: - _texture_group = Group(Item(name='interpolate'), - Item(name='color_mode'), - Item(name='repeat'), - show_border=True) -else: - _texture_group = Group(Item(name='interpolate'), - Item(name='map_color_scalars_through_lookup_table'), - Item(name='repeat'), - show_border=True) +_texture_group = Group(Item(name='interpolate'), + Item(name='color_mode'), + Item(name='repeat'), + show_border=True) # The Actor's view group. _actor_base_group = Group(Item(name='visibility')) diff --git a/mayavi/tests/test_builtin_image.py b/mayavi/tests/test_builtin_image.py index 0af5a6ec..0ba01507 100644 --- a/mayavi/tests/test_builtin_image.py +++ b/mayavi/tests/test_builtin_image.py @@ -11,7 +11,6 @@ from numpy import array # Enthought library imports. -from tvtk.common import vtk_major_version from mayavi.core.null_engine import NullEngine from mayavi.sources.builtin_image import BuiltinImage from mayavi.modules.surface import Surface @@ -40,8 +39,6 @@ def setUp(self): image_data.data_source.radius = array([80., 80., 80.]) image_data.data_source.center = array([150., 150., 0.]) image_data.data_source.whole_extent = array([10, 245, 10, 245, 0, 0]) - if vtk_major_version < 8: - image_data.data_source.set_update_extent_to_whole_extent() self.e = e self.scene = e.current_scene diff --git a/mayavi/tools/figure.py b/mayavi/tools/figure.py index a196f1fd..8890b470 100644 --- a/mayavi/tools/figure.py +++ b/mayavi/tools/figure.py @@ -19,7 +19,6 @@ # imports from tvtk.api import tvtk -from tvtk.common import vtk_major_version from mayavi.core.scene import Scene from mayavi.core.registry import registry from .camera import view @@ -313,19 +312,13 @@ def screenshot(figure=None, mode='rgb', antialiased=False): out = tvtk.UnsignedCharArray() shape = (y, x, 3) pixel_getter = figure.scene.render_window.get_pixel_data - if vtk_major_version > 7: - pg_args = (0, 0, x - 1, y - 1, 1, out, 0) - else: - pg_args = (0, 0, x - 1, y - 1, 1, out) + pg_args = (0, 0, x - 1, y - 1, 1, out, 0) elif mode == 'rgba': out = tvtk.FloatArray() shape = (y, x, 4) pixel_getter = figure.scene.render_window.get_rgba_pixel_data - if vtk_major_version > 7: - pg_args = (0, 0, x - 1, y - 1, 1, out, 0) - else: - pg_args = (0, 0, x - 1, y - 1, 1, out) + pg_args = (0, 0, x - 1, y - 1, 1, out, 0) else: raise ValueError('mode type not understood') diff --git a/pyproject.toml b/pyproject.toml index 9da1141e..d6cad3fe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,6 +2,6 @@ requires = [ "numpy>=2.0.0rc2,<3", "setuptools", - "vtk", + "vtk>=9", "wheel" ] diff --git a/tvtk/common.py b/tvtk/common.py index bad08ce2..0b6a618d 100644 --- a/tvtk/common.py +++ b/tvtk/common.py @@ -5,7 +5,6 @@ # License: BSD Style. from contextlib import contextmanager -import string import re import vtk @@ -67,10 +66,6 @@ def _sanitize_name(name): return name -def is_version_9(): - return vtk_major_version > 8 - - def configure_connection(obj, inp): """ Configure topology for vtk pipeline obj.""" if hasattr(inp, 'output_port'): diff --git a/tvtk/tests/test_class_tree.py b/tvtk/tests/test_class_tree.py index f71e43bf..5d9ad976 100644 --- a/tvtk/tests/test_class_tree.py +++ b/tvtk/tests/test_class_tree.py @@ -7,7 +7,6 @@ """ import builtins -import sys import unittest from contextlib import contextmanager @@ -19,8 +18,6 @@ _cache = class_tree.ClassTree(vtk) _cache.create() -vtk_major_version = vtk.vtkVersion.GetVTKMajorVersion() - def get_level(klass): """Gets the inheritance level of a given class.""" if not klass.__bases__: @@ -54,15 +51,7 @@ def test_basic_vtk(self): if (hasattr(vtk, 'vtkTuple')): names = [x.name for x in t.tree[0]] names.sort() - if vtk_major_version == 7: - expect = ['object', 'vtkColor3', 'vtkColor4', 'vtkDenseArray', - 'vtkQuaternion', 'vtkRect', - 'vtkSparseArray', 'vtkTuple', - 'vtkTypedArray','vtkVector', - 'vtkVector2', 'vtkVector3'] - else: - self.assertGreaterEqual(vtk_major_version, 8) - expect = ['object'] + expect = ['object'] self.assertEqual(names, expect) elif (hasattr(vtk, 'vtkVector')): self.assertEqual(len(t.tree[0]), 11) diff --git a/tvtk/tests/test_vtk_parser.py b/tvtk/tests/test_vtk_parser.py index c97635f5..ce88ce9e 100644 --- a/tvtk/tests/test_vtk_parser.py +++ b/tvtk/tests/test_vtk_parser.py @@ -16,6 +16,7 @@ import unittest from tvtk import vtk_parser from tvtk import vtk_module as vtk +from tvtk.common import vtk_major_version, vtk_minor_version import time # Only used when timing. import sys # Only used when debugging. @@ -23,8 +24,7 @@ # This is a little expensive to create so we cache it. _cache = vtk_parser.VTKMethodParser() -vtk_major_version = vtk.vtkVersion.GetVTKMajorVersion() -vtk_minor_version = vtk.vtkVersion.GetVTKMinorVersion() + class TestVTKParser(unittest.TestCase): def setUp(self): @@ -44,13 +44,9 @@ def test_parse(self): p = self.p # Simple case of a vtkObject. p.parse(vtk.vtkObject()) - if vtk_major_version < 9: - self.assertEqual(p.get_toggle_methods(), - {'Debug': 0, 'GlobalWarningDisplay': 1}) - else: - self.assertIn(p.get_toggle_methods(), - [{'Debug': False, 'GlobalWarningDisplay': 1}, - {'Debug': False, 'GlobalWarningDisplay': 0}]) + self.assertIn(p.get_toggle_methods(), + [{'Debug': False, 'GlobalWarningDisplay': 1}, + {'Debug': False, 'GlobalWarningDisplay': 0}]) self.assertEqual(p.get_state_methods(), {}) self.assertEqual(p.get_get_methods(), ['GetCommand', 'GetMTime']) @@ -87,8 +83,7 @@ def test_parse(self): ['Gouraud', 1], ['Phong', 2]], 'Representation': [['Surface', 2], ['Points', 0], ['Surface', 2], ['Wireframe', 1]]} - if vtk_major_version >= 9: - res['Interpolation'].insert(-1, ['PBR', 3]) + res['Interpolation'].insert(-1, ['PBR', 3]) self.assertEqual(p.get_state_methods(), res) self.assertEqual(p.state_meths, p.get_state_methods()) @@ -118,15 +113,13 @@ def test_parse(self): 'SpecularPower': (1.0, (0.0, 100.0))} if ('ReferenceCount' not in p.get_get_set_methods()): del res['ReferenceCount'] - if vtk_major_version > 7: - res['MaterialName'] = (None, None) - res['VertexColor'] = ((0.5, 1.0, 0.5), None) - if vtk_major_version >= 9: - res['EmissiveFactor'] = ((1.0, 1.0, 1.0), None) - res['Metallic'] = (0., float_max) - res['NormalScale'] = (1., None) - res['OcclusionStrength'] = (1., float_max) - res['Roughness'] = (0.5, float_max) + res['MaterialName'] = (None, None) + res['VertexColor'] = ((0.5, 1.0, 0.5), None) + res['EmissiveFactor'] = ((1.0, 1.0, 1.0), None) + res['Metallic'] = (0., float_max) + res['NormalScale'] = (1., None) + res['OcclusionStrength'] = (1., float_max) + res['Roughness'] = (0.5, float_max) if (vtk_major_version, vtk_minor_version) >= (9, 1): res['Anisotropy'] = (0.0, (0.0, 1.0)) res['AnisotropyRotation'] = (0.0, (0.0, 1.0)) @@ -180,12 +173,8 @@ def test_parse(self): res = ['AddShaderVariable', 'BackfaceRender', 'DeepCopy', 'ReleaseGraphicsResources', 'RemoveAllTextures', 'RemoveTexture', 'Render'] - if (vtk_major_version >= 7 or vtk_minor_version >= 2) and \ - vtk_major_version < 9: - res.append('VTKTextureUnit') - if vtk_major_version == 9: - res.extend(['SetBaseColorTexture', 'SetEmissiveTexture', - 'SetNormalTexture', 'SetORMTexture']) + res.extend(['SetBaseColorTexture', 'SetEmissiveTexture', + 'SetNormalTexture', 'SetORMTexture']) if vtk_major_version == 9 and vtk_minor_version > 0: res.extend([ 'ComputeIORFromReflectance', 'ComputeReflectanceFromIOR', diff --git a/tvtk/vtk_parser.py b/tvtk/vtk_parser.py index 7f95c6e9..b14b197d 100644 --- a/tvtk/vtk_parser.py +++ b/tvtk/vtk_parser.py @@ -631,18 +631,6 @@ def _find_get_set_methods(self, klass, methods): # vtkProp.Get/SetAllocatedRenderTime is private and # SetAllocatedRenderTime takes two args, don't wrap it. continue - elif vtk_major_version < 9 and ( - (klass_name == 'vtkGenericAttributeCollection' and - method[3:] == 'AttributesToInterpolate') or - (klass_name == 'vtkOverlappingAMR' and - method[3:] == 'Origin') or - (klass_name == 'vtkOrientationMarkerWidget' and - method[3:] in ['OutlineColor', 'Viewport']) or - (klass_name == 'vtkImageDataGeometryFilter' and - method[3:] == 'Extent') or - (klass_name == 'vtkVolumeMapper' and - method[3:] == 'CroppingRegionPlanes')): - continue elif (klass_name == 'vtkContextMouseEvent' and method[3:] == 'Interactor'): continue @@ -668,55 +656,40 @@ def _find_get_set_methods(self, klass, methods): # Find the default and range of the values. if gsm: obj = self._get_instance(klass) - # print('got instance', obj.__class__) if obj: for key, value in gsm.items(): - 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')): + # Broken in <= 9.3 + if ( + (vtk_major_version, vtk_minor_version) <= (9, 3) + and f"{klass_name}.Get{key}" in ( + "vtkGenericAttributeCollection.GetAttributesToInterpolate", + "vtkPlotBar.GetLookupTable", + "vtkLagrangianParticleTracker.GetIntegrationModel", + ) + ): default = None - elif vtk_major_version < 9 and ( - klass_name == 'vtkHyperOctree' and - key == 'Dimension'): - # This class breaks standard VTK conventions. - gsm[key] = (3, (1, 3)) - continue - # On VTK 9.0.0 vtkHigherOrderTetra.GetParametricCorods - # segfauts when uninitialized, see: - # - # https://gitlab.kitware.com/vtk/vtk/-/merge_requests/6729#note_732848 # noqa: E501 - # - # vtkGenericAttributeCollection.GetAttributesToInterpolate - # might only be a problem if VTK is built in debug mode, - # but let's keep it just to be safe. - elif vtk_major_version == 9 and ( - # Still broken on 9.4 - (klass_name == 'vtkHigherOrderTetra' and - key == 'ParametricCoords' and vtk_minor_version < 5) or - (klass_name == 'vtkGenericAttributeCollection' and - key == 'AttributesToInterpolate' and vtk_minor_version < 4) or - (klass_name == 'vtkPlotBar' and - key == 'LookupTable' and vtk_minor_version < 4) or - (klass_name == 'vtkLagrangianParticleTracker' and - key == 'IntegrationModel' and vtk_minor_version < 4) or - False): # just to simplify indentation/updates + # Broken in <= 9.4 + # https://gitlab.kitware.com/vtk/vtk/-/merge_requests/6729#note_732848 + elif ( + (vtk_major_version, vtk_minor_version) <= (9, 4) + and f"{klass_name}.Get{key}" in ( + "vtkHigherOrderTetra.GetParametricCoords", + ) + ): default = None - - # vtkGenericCell().GetCellFaces() segfaults on 9.4 + # Broken in 9.4 elif ( (vtk_major_version, vtk_minor_version) == (9, 4) - and klass_name == "vtkGenericCell" - and key == "CellFaces" + and f"{klass_name}.Get{key}" in ( + "vtkGenericCell.GetCellFaces", + ) ): default = None else: try: if self._verbose: - print(f' Calling {klass_name}.Get{key}()') - default = getattr(obj, 'Get%s' % key)() + print(f" Calling {klass_name}.Get{key}()") + default = getattr(obj, f"Get{key}")() except TypeError: default = None diff --git a/tvtk/wrapper_gen.py b/tvtk/wrapper_gen.py index b9314e4e..1c3cb0d2 100644 --- a/tvtk/wrapper_gen.py +++ b/tvtk/wrapper_gen.py @@ -17,7 +17,8 @@ # Local imports (these are relative imports because the package is not # installed when these modules are imported). -from .common import get_tvtk_name, camel2enthought, vtk_major_version, _sanitize_name +from .common import get_tvtk_name, camel2enthought, _sanitize_name +from .common import vtk_major_version, vtk_minor_version from . import vtk_parser from . import indenter @@ -1836,12 +1837,12 @@ def _write_smart_volume_mapper_vector_component(self, klass, out, default, rng = self.parser.get_get_set_methods()[vtk_attr_name] - if vtk_major_version >= 8: - message = ("vtkSmartVolumeMapper: " - "VectorComponent not updatable " - "(VTK 8.x bug - value not properly initialized)") - print(message) - default = rng[0] + # TODO: Still an issue in 9.x? + message = ("vtkSmartVolumeMapper: " + "VectorComponent not updatable " + "(VTK 8.x bug - value not properly initialized)") + print(message) + default = rng[0] t_def = ('traits.Trait({default}, traits.Range{rng}, ' 'enter_set=True, auto_set=False)').format(default=default, rng=rng) @@ -1857,12 +1858,6 @@ def _write_span_space_resolution(self, klass, out, default, rng = self.parser.get_get_set_methods()[vtk_attr_name] - if vtk_major_version == 7: - message = ("vtkSpanSpace: " - "Resolution not updatable " - "(VTK 7.x bug - value not properly initialized)") - print(message) - default = rng[0] t_def = ('traits.Trait({default}, traits.Range{rng}, ' 'enter_set=True, auto_set=False)').format(default=default, rng=rng) @@ -1876,11 +1871,11 @@ def _write_hyper_tree_grid_cell_centers_vertex_cells(self, klass, out, raise RuntimeError("Not sure why you ask for me! " "I only deal with VertexCells. Panicking.") - if vtk_major_version >= 8: - message = ("vtkHyperTreeGridCellCenters: " - "VertexCells not updatable " - "(VTK 8.x bug - value not properly initialized)") - print(message) + # TODO: Still an issue in 9.x? + message = ("vtkHyperTreeGridCellCenters: " + "VertexCells not updatable " + "(VTK 8.x bug - value not properly initialized)") + print(message) t_def = 'tvtk_base.true_bool_trait' @@ -1897,12 +1892,13 @@ def _write_euclidean_cluster_extraction_radius( default, rng = self.parser.get_get_set_methods()[vtk_attr_name] - if vtk_major_version >= 8: - message = ("vtkEuclideanClusterExtraction: " - "Radius not updatable " - "(VTK 9.1 bug - value not properly initialized)") - print(message) - default = rng[0] + # TODO: Still an issue in 9.x? + message = ("vtkEuclideanClusterExtraction: " + "Radius not updatable " + "(VTK 9.1 bug - value not properly initialized)") + print(message) + default = rng[0] + t_def = ('traits.Trait({default}, traits.Range{rng}, ' 'enter_set=True, auto_set=False)').format(default=default, rng=rng) @@ -1950,7 +1946,7 @@ def _write_cylinder_source_lat_long_tessellation( ): if vtk_attr_name != 'LatLongTessellation': raise RuntimeError(f"Wrong attribute name: {vtk_attr_name}") - if vtk_major_version >= 9: + if (vtk_major_version, vtk_minor_version) <= (9, 3): message = ("vtkCylinderSource: " "LatLongTesselation not updatable " "(VTK 9.3 bug - value not properly initialized)") From 831e09df4b8c8b8a8e0e47844d30ead597854766 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Wed, 18 Dec 2024 15:05:39 -0500 Subject: [PATCH 04/14] CI: Test older --- .github/workflows/run-mayavi-tests.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/run-mayavi-tests.yml b/.github/workflows/run-mayavi-tests.yml index cd0c3510..6bf54fb2 100644 --- a/.github/workflows/run-mayavi-tests.yml +++ b/.github/workflows/run-mayavi-tests.yml @@ -58,6 +58,15 @@ jobs: os: windows-latest vtk: 'vtk==9.3' numpy: 'numpy==1.26.4' + # Older Python and VTKs + - python-version: '3.10' + qt-api: 'pyqt5' + os: ubuntu-latest + vtk: 'vtk==9.2' + - python-version: '3.9' + qt-api: 'pyqt5' + os: ubuntu-latest + vtk: 'vtk==9.1' fail-fast: false defaults: run: From ba56f80ad42b19c48e8577bd3759092dee2220f1 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Wed, 18 Dec 2024 15:06:21 -0500 Subject: [PATCH 05/14] FIX: Ancient --- .github/workflows/run-mayavi-tests.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/run-mayavi-tests.yml b/.github/workflows/run-mayavi-tests.yml index 6bf54fb2..22e96176 100644 --- a/.github/workflows/run-mayavi-tests.yml +++ b/.github/workflows/run-mayavi-tests.yml @@ -67,6 +67,10 @@ jobs: qt-api: 'pyqt5' os: ubuntu-latest vtk: 'vtk==9.1' + - python-version: '3.9' + qt-api: 'pyqt5' + os: ubuntu-latest + vtk: 'vtk==9.0' fail-fast: false defaults: run: From a642f05d1e5590c378ad06e9dcd525b359d1e003 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Wed, 18 Dec 2024 15:09:42 -0500 Subject: [PATCH 06/14] FIX: More --- .github/dependabot.yml | 10 ++++++++++ .github/workflows/run-mayavi-tests.yml | 11 ++++++----- 2 files changed, 16 insertions(+), 5 deletions(-) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..d57929b9 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" + groups: + actions: + patterns: + - "*" diff --git a/.github/workflows/run-mayavi-tests.yml b/.github/workflows/run-mayavi-tests.yml index 22e96176..f4e1eca8 100644 --- a/.github/workflows/run-mayavi-tests.yml +++ b/.github/workflows/run-mayavi-tests.yml @@ -63,14 +63,15 @@ jobs: qt-api: 'pyqt5' os: ubuntu-latest vtk: 'vtk==9.2' + # In practice this one segfaults :( + # - python-version: '3.9' + # qt-api: 'pyqt5' + # os: ubuntu-latest + # vtk: 'vtk~=9.1' - python-version: '3.9' qt-api: 'pyqt5' os: ubuntu-latest - vtk: 'vtk==9.1' - - python-version: '3.9' - qt-api: 'pyqt5' - os: ubuntu-latest - vtk: 'vtk==9.0' + vtk: 'vtk==9.0.2' fail-fast: false defaults: run: From 832453bdfbe5cea604c1dee22ed989b959e6bca4 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Wed, 18 Dec 2024 15:09:59 -0500 Subject: [PATCH 07/14] FIX: Pt 2 --- .github/workflows/run-mayavi-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-mayavi-tests.yml b/.github/workflows/run-mayavi-tests.yml index f4e1eca8..e3f173f5 100644 --- a/.github/workflows/run-mayavi-tests.yml +++ b/.github/workflows/run-mayavi-tests.yml @@ -62,7 +62,7 @@ jobs: - python-version: '3.10' qt-api: 'pyqt5' os: ubuntu-latest - vtk: 'vtk==9.2' + vtk: 'vtk==9.2.2' # In practice this one segfaults :( # - python-version: '3.9' # qt-api: 'pyqt5' From 47bc677d3cb4c042af7381f10474e69627d59d1c Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Wed, 18 Dec 2024 15:10:15 -0500 Subject: [PATCH 08/14] FIX: pt0 --- .github/workflows/run-mayavi-tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run-mayavi-tests.yml b/.github/workflows/run-mayavi-tests.yml index e3f173f5..e05ac52b 100644 --- a/.github/workflows/run-mayavi-tests.yml +++ b/.github/workflows/run-mayavi-tests.yml @@ -59,10 +59,10 @@ jobs: vtk: 'vtk==9.3' numpy: 'numpy==1.26.4' # Older Python and VTKs - - python-version: '3.10' + - python-version: '3.9' qt-api: 'pyqt5' os: ubuntu-latest - vtk: 'vtk==9.2.2' + vtk: 'vtk==9.2.0' # In practice this one segfaults :( # - python-version: '3.9' # qt-api: 'pyqt5' From 82572cbd0968cc9384e3c0aee637d474b686156f Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Wed, 18 Dec 2024 15:11:51 -0500 Subject: [PATCH 09/14] FIX: 2 --- .github/workflows/run-mayavi-tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/run-mayavi-tests.yml b/.github/workflows/run-mayavi-tests.yml index e05ac52b..5660479c 100644 --- a/.github/workflows/run-mayavi-tests.yml +++ b/.github/workflows/run-mayavi-tests.yml @@ -59,10 +59,10 @@ jobs: vtk: 'vtk==9.3' numpy: 'numpy==1.26.4' # Older Python and VTKs - - python-version: '3.9' + - python-version: '3.10' qt-api: 'pyqt5' os: ubuntu-latest - vtk: 'vtk==9.2.0' + vtk: 'vtk==9.2.2' # In practice this one segfaults :( # - python-version: '3.9' # qt-api: 'pyqt5' @@ -71,7 +71,7 @@ jobs: - python-version: '3.9' qt-api: 'pyqt5' os: ubuntu-latest - vtk: 'vtk==9.0.2' + vtk: 'vtk==9.0.2' # oldest available on 3.9 which is the oldest Python we support fail-fast: false defaults: run: From dd6f64cac849e3c31f9913759ecc466e42526484 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Wed, 18 Dec 2024 15:19:36 -0500 Subject: [PATCH 10/14] FIX: Lets see it --- .github/workflows/run-mayavi-tests.yml | 9 ++++----- tvtk/tests/test_tvtk.py | 12 +++++++++++- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/.github/workflows/run-mayavi-tests.yml b/.github/workflows/run-mayavi-tests.yml index 5660479c..7424f992 100644 --- a/.github/workflows/run-mayavi-tests.yml +++ b/.github/workflows/run-mayavi-tests.yml @@ -63,11 +63,10 @@ jobs: qt-api: 'pyqt5' os: ubuntu-latest vtk: 'vtk==9.2.2' - # In practice this one segfaults :( - # - python-version: '3.9' - # qt-api: 'pyqt5' - # os: ubuntu-latest - # vtk: 'vtk~=9.1' + - python-version: '3.9' + qt-api: 'pyqt5' + os: ubuntu-latest + vtk: 'vtk~=9.1' - python-version: '3.9' qt-api: 'pyqt5' os: ubuntu-latest diff --git a/tvtk/tests/test_tvtk.py b/tvtk/tests/test_tvtk.py index bb37bee2..6ea05862 100644 --- a/tvtk/tests/test_tvtk.py +++ b/tvtk/tests/test_tvtk.py @@ -8,6 +8,7 @@ # Copyright (c) 2004-2020, Enthought, Inc. # License: BSD Style. +import os import unittest import pickle import weakref @@ -43,6 +44,9 @@ from tvtk.tvtk_classes import tvtk_helper +on_gha = os.getenv("GITHUB_ACTION", None) is not None + + def mysum(arr): val = arr while type(val) == numpy.ndarray: @@ -818,14 +822,20 @@ def setUpClass(cls): def test_all_instantiable(self): """Test if all the TVTK classes can be instantiated""" errors = [] + if on_gha: + print("\n::group::Instantiating TVTK classes") for name in self.names: tvtk_name = get_tvtk_name(name) tvtk_klass = getattr(tvtk, tvtk_name, None) + if on_gha: + print(f"{tvtk_name} ({name})") try: - obj = tvtk_klass() + tvtk_klass() # TypeError: super(type, obj): obj must be an instance or subtype of type except (TraitError, KeyError, TypeError): errors.append(f"\n{name}:\n{indent(traceback.format_exc(), ' ')}") + if on_gha: + print("\n::endgroup::") if len(errors) > 0: message = "Not all classes could be instantiated:\n{0}\n" raise AssertionError(message.format(''.join(errors))) From f716072ceaed7aadeff5ba543ebcb9699e695807 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Wed, 18 Dec 2024 15:21:57 -0500 Subject: [PATCH 11/14] FIX: Fine --- .github/workflows/run-mayavi-tests.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run-mayavi-tests.yml b/.github/workflows/run-mayavi-tests.yml index 7424f992..807e72b8 100644 --- a/.github/workflows/run-mayavi-tests.yml +++ b/.github/workflows/run-mayavi-tests.yml @@ -66,7 +66,7 @@ jobs: - python-version: '3.9' qt-api: 'pyqt5' os: ubuntu-latest - vtk: 'vtk~=9.1' + vtk: 'vtk==9.1.0' - python-version: '3.9' qt-api: 'pyqt5' os: ubuntu-latest @@ -87,8 +87,10 @@ jobs: steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - name: Install Linux packages for Qt5/Qt6 support and start Xvfb - uses: pyvista/setup-headless-display-action@main + uses: pyvista/setup-headless-display-action@v3 with: qt: true if: startsWith(matrix.os, 'ubuntu') From 07a11fbf2e71811c3760a029a2d56c162bae2e78 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Wed, 18 Dec 2024 15:26:34 -0500 Subject: [PATCH 12/14] FIX: Got another --- tvtk/tests/test_tvtk.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tvtk/tests/test_tvtk.py b/tvtk/tests/test_tvtk.py index 6ea05862..8de94a5a 100644 --- a/tvtk/tests/test_tvtk.py +++ b/tvtk/tests/test_tvtk.py @@ -828,7 +828,7 @@ def test_all_instantiable(self): tvtk_name = get_tvtk_name(name) tvtk_klass = getattr(tvtk, tvtk_name, None) if on_gha: - print(f"{tvtk_name} ({name})") + print(tvtk_name) try: tvtk_klass() # TypeError: super(type, obj): obj must be an instance or subtype of type @@ -865,6 +865,8 @@ def get_min_max_value(vtk_klass, vtk_attr_name): except AttributeError: return None, None + if on_gha: + print("\n::group::TVTK trait ranges") for name in self.names: vtk_klass = getattr(vtk, name) tvtk_klass_name = get_tvtk_name(name) @@ -875,11 +877,15 @@ def get_min_max_value(vtk_klass, vtk_attr_name): 'OpenGLRenderWindow', 'RenderWindow']: continue + + if on_gha: + print(tvtk_klass_name) + try: obj = getattr(tvtk, tvtk_klass_name)() except Exception: # testing for instantiation is above - pass + continue for trait_name in obj.editable_traits(): if trait_name in ['_in_set', '_vtk_obj']: From 932db26abf47424bcab22d31dbcdc59ed9c7dd09 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Wed, 18 Dec 2024 15:29:48 -0500 Subject: [PATCH 13/14] FIX: Dont buffer --- .github/workflows/run-mayavi-tests.yml | 1 + tvtk/tests/test_tvtk.py | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/run-mayavi-tests.yml b/.github/workflows/run-mayavi-tests.yml index 807e72b8..2c9a7f7d 100644 --- a/.github/workflows/run-mayavi-tests.yml +++ b/.github/workflows/run-mayavi-tests.yml @@ -84,6 +84,7 @@ jobs: QT_API: ${{ matrix.qt-api }} TVTK_VERBOSE: 'true' VTK_PARSER_VERBOSE: 'true' + PYTHONUNBUFFERED: '1' steps: - uses: actions/checkout@v4 diff --git a/tvtk/tests/test_tvtk.py b/tvtk/tests/test_tvtk.py index 8de94a5a..8fe7f5b6 100644 --- a/tvtk/tests/test_tvtk.py +++ b/tvtk/tests/test_tvtk.py @@ -910,6 +910,8 @@ def get_min_max_value(vtk_klass, vtk_attr_name): setattr(obj, trait_name, (min_value-1, max_value)) with self.assertRaises(TraitError): setattr(obj, trait_name, (min_value, max_value+1)) + if on_gha: + print("::endgroup::") def test_no_trait_has_ptr_address_as_value(self): '''Test if none of the TVTK classes' traits has a value of "*_p_void" @@ -954,7 +956,7 @@ def test_all_traits_can_be_obtained(self): obj = getattr(tvtk, tvtk_klass_name)() except Exception: # testing for instantiation is above - pass + continue for trait_name in obj._full_traitnames_list_: try: From a48872ba308aedc49cddda2abfe7c21d2ae82793 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Wed, 18 Dec 2024 15:35:43 -0500 Subject: [PATCH 14/14] FIX: Skip for now --- .github/workflows/run-mayavi-tests.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/run-mayavi-tests.yml b/.github/workflows/run-mayavi-tests.yml index 2c9a7f7d..af346583 100644 --- a/.github/workflows/run-mayavi-tests.yml +++ b/.github/workflows/run-mayavi-tests.yml @@ -63,10 +63,11 @@ jobs: qt-api: 'pyqt5' os: ubuntu-latest vtk: 'vtk==9.2.2' - - python-version: '3.9' - qt-api: 'pyqt5' - os: ubuntu-latest - vtk: 'vtk==9.1.0' + # TVTK tests intermittently segfault on 9.1 + # - python-version: '3.9' + # qt-api: 'pyqt5' + # os: ubuntu-latest + # vtk: 'vtk==9.1.0' - python-version: '3.9' qt-api: 'pyqt5' os: ubuntu-latest