diff --git a/.github/workflows/headless-tests.yml b/.github/workflows/headless-tests.yml index 96c0ca2fd..8ed9362a8 100644 --- a/.github/workflows/headless-tests.yml +++ b/.github/workflows/headless-tests.yml @@ -31,10 +31,10 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip setuptools wheel - python -m pip install numpy "vtk<9.3" pillow pytest traitsui + python -m pip install numpy "vtk<9.3" pillow pytest pytest-timeout traitsui - name: Install mayavi and tvtk - run: python -m pip install -v . - - name: Test tvtk package - run: pytest -v --pyargs tvtk + run: python -m pip install --no-build-isolation -v . - name: Test Mayavi package - run: pytest -v --pyargs mayavi + run: pytest -v --timeout=10 --pyargs mayavi + - name: Test tvtk package + run: pytest -v --timeout=60 --pyargs tvtk diff --git a/.github/workflows/run-mayavi-tests.yml b/.github/workflows/run-mayavi-tests.yml index 8d4553fca..587adc46f 100644 --- a/.github/workflows/run-mayavi-tests.yml +++ b/.github/workflows/run-mayavi-tests.yml @@ -43,6 +43,22 @@ jobs: qt-api: 'pyside6' os: macos-13 vtk: 'vtk<9.3' + # Some old NumPys + - python-version: '3.12' + qt-api: 'pyside6' + os: ubuntu-latest + 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' + numpy: 'numpy==1.26.4' fail-fast: false defaults: run: @@ -69,14 +85,13 @@ jobs: with: python-version: ${{ matrix.python-version }} - name: Install dependencies - shell: bash run: | set -exo pipefail python -m pip install --upgrade pip setuptools wheel - python -m pip install --upgrade "${{ matrix.qt-api }}" numpy "${{ matrix.vtk }}" pillow pytest traits traitsui + python -m pip install --upgrade "${{ matrix.qt-api }}" "${{ matrix.numpy || 'numpy' }}" "${{ matrix.vtk }}" pillow pytest pytest-timeout traits traitsui --only-binary="numpy,vtk" - name: Install mayavi and tvtk - run: python -um pip install -ve .[app] + run: python -um pip install --no-build-isolation -ve .[app] - name: Test Mayavi package - run: pytest -v mayavi + run: pytest -v --timeout=10 mayavi - name: Test tvtk package - run: pytest -sv tvtk + run: pytest -sv --timeout=60 tvtk diff --git a/.gitignore b/.gitignore index a57949113..3b903dad4 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,5 @@ docs/build/ docs/html.zip tvtk/tvtk_classes.zip +tvtk/tvtk_classes/ mayavi/images/m2_about.jpg diff --git a/mayavi/__init__.py b/mayavi/__init__.py index 8e89a722c..27c028128 100644 --- a/mayavi/__init__.py +++ b/mayavi/__init__.py @@ -9,6 +9,7 @@ __requires__ = [ 'apptools', + 'configobj', 'envisage', 'numpy', 'pyface>=6.1.1', @@ -16,6 +17,7 @@ 'traits>=6.0.0', 'traitsui>=7.0.0', 'packaging', + 'importlib_resources; python_version<"3.11"', 'vtk' ] diff --git a/mayavi/preferences/preference_manager.py b/mayavi/preferences/preference_manager.py index efe68ef9e..bf27a046d 100644 --- a/mayavi/preferences/preference_manager.py +++ b/mayavi/preferences/preference_manager.py @@ -21,7 +21,7 @@ # Standard library imports from os.path import join -import pkg_resources +import importlib.resources # Enthought library imports. from traits.etsconfig.api import ETSConfig @@ -106,12 +106,11 @@ def _load_preferences(self): for pkg in ('mayavi.preferences', 'tvtk.plugins.scene'): pref = 'preferences.ini' - pref_file = pkg_resources.resource_stream(pkg, pref) - + pref_file = importlib.resources.files(pkg).joinpath(pref) preferences = self.preferences default = preferences.node('default/') - default.load(pref_file) - pref_file.close() + with open(pref_file, 'rb') as fid: + default.load(fid) finally: # Set back the application home. ETSConfig.application_home = app_home @@ -126,4 +125,3 @@ def _preferences_changed(self, preferences): # A Global preference manager that all other modules can use. preference_manager = PreferenceManager() - diff --git a/mayavi/tests/test_csv_sniff.py b/mayavi/tests/test_csv_sniff.py index 30e2802d2..f50a573fe 100644 --- a/mayavi/tests/test_csv_sniff.py +++ b/mayavi/tests/test_csv_sniff.py @@ -12,7 +12,7 @@ import tempfile from unittest import SkipTest -from numpy import array, ndarray +from numpy import array, ndarray, isnan from mayavi.tools.data_wizards.csv_sniff import \ Sniff, loadtxt, loadtxt_unknown, array2dict @@ -33,8 +33,8 @@ def assertAllClose(self, x, y): def assertClose(self, a, b): if isinstance(a, (int, float)): - if repr(a) == 'nan': - self.assertTrue(repr(b) == 'nan') + if isnan(a): + self.assertTrue(isnan(b), '%r != %r' % (a ,b)) else: self.assertTrue(abs(a - b) < 1e-6 * max(1, abs(a)), '%r != %r %r' % (a, b, abs(a - b))) diff --git a/mayavi/tests/test_mlab_source.py b/mayavi/tests/test_mlab_source.py index 49c6e87c2..ca574e1dd 100644 --- a/mayavi/tests/test_mlab_source.py +++ b/mayavi/tests/test_mlab_source.py @@ -171,7 +171,7 @@ def test_set(self): self.check_traits() self.check_dataset() - def test_strange_shape(self): + def test_basic_strange_shape(self): " Test the MGlyphSource with strange shapes for the arguments " x, y, z, v, s, src = self.get_data() x = y = z = v = s = 0 diff --git a/mayavi/tools/data_wizards/loadtxt.py b/mayavi/tools/data_wizards/loadtxt.py index 80702b0e0..b4677dee8 100644 --- a/mayavi/tools/data_wizards/loadtxt.py +++ b/mayavi/tools/data_wizards/loadtxt.py @@ -23,7 +23,7 @@ def _getconv(dtype): return lambda x: int(float(x)) elif issubclass(typ, np.floating): return float - elif issubclass(typ, np.complex_): + elif issubclass(typ, np.complexfloating): return complex else: return str diff --git a/setup.cfg b/setup.cfg index 212a94acb..699717f82 100644 --- a/setup.cfg +++ b/setup.cfg @@ -4,5 +4,7 @@ addopts = filterwarnings = # Currently unsatisfiable ignore:Workbench will be moved from pyface:PendingDeprecationWarning + # Should be fixed in traits + ignore: module 'sre_.+' is deprecated:DeprecationWarning # We call deprecated methods and classes in our tests, and there are many variants for how the parentheticals are formatted ignore:Call to deprecated .*. \((This|Use|Please|Deprecated|Deprecating|Removed|Part|no|renamed) .*\) -- Deprecated since version.*:DeprecationWarning diff --git a/setup.py b/setup.py index 2b81501c2..6f978b4c4 100644 --- a/setup.py +++ b/setup.py @@ -130,8 +130,8 @@ def example_files(self): mlab_ref_dir = join(DEFAULT_INPUT_DIR, 'mayavi', 'auto') source_path = join('examples', 'mayavi') - sources = '(\.py)|(\.rst)$' - excluded_dirs = '^\.' + sources = r'(\.py)|(\.rst)$' + excluded_dirs = r'^\.' target_path = mlab_ref_dir target_time = self.latest_modified(target_path, ignore_dirs=excluded_dirs)[0] diff --git a/tvtk/array_handler.py b/tvtk/array_handler.py index 6f42b2e89..1e1a1db68 100644 --- a/tvtk/array_handler.py +++ b/tvtk/array_handler.py @@ -197,9 +197,9 @@ def get_vtk_array_type(numeric_array_type): numpy.dtype(ULONG_TYPE_CODE): vtkConstants.VTK_UNSIGNED_LONG, numpy.dtype(LONG_TYPE_CODE): vtkConstants.VTK_LONG, } - for t in _extra: + for t, val in _extra.items(): if t not in _arr_vtk: - _arr_vtk[t] = _extra[t] + _arr_vtk[t] = val try: return _arr_vtk[numeric_array_type] diff --git a/tvtk/code_gen.py b/tvtk/code_gen.py index 8ad3ab6a9..03f7fc8f8 100644 --- a/tvtk/code_gen.py +++ b/tvtk/code_gen.py @@ -1,13 +1,16 @@ """This module generates tvtk (Traited VTK) classes from the VTK-Python API. -This can be evoked for example by: +This can be evoked for example by running from the ``mayavi`` root: ..code-block:: console - $ python -ic "from tvtk.code_gen import main; main()" -szv + $ 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. + +Exceptions to behaviors based on VTK versions and bugs etc. live in ``wrapper_gen.py`` +and ``tvtk_parser.py``. """ # Author: Prabhu Ramachandran # Copyright (c) 2004-2020, Enthought, Inc. diff --git a/tvtk/tests/test_array_ext.py b/tvtk/tests/test_array_ext.py index cbb03c743..4320267e8 100644 --- a/tvtk/tests/test_array_ext.py +++ b/tvtk/tests/test_array_ext.py @@ -5,12 +5,16 @@ # Copyright (c) 2005, Enthought, Inc. # License: BSD Style. +import pytest import unittest import numpy from tvtk.array_handler import ID_TYPE_CODE, set_id_type_array_py -from tvtk.array_ext import set_id_type_array +try: + from tvtk.array_ext import set_id_type_array +except ModuleNotFoundError: # not compiled + pytest.skip("array_ext not found", allow_module_level=True) class TestArrayExt(unittest.TestCase): diff --git a/tvtk/tests/test_array_handler.py b/tvtk/tests/test_array_handler.py index 556ad0483..bc4588dec 100644 --- a/tvtk/tests/test_array_handler.py +++ b/tvtk/tests/test_array_handler.py @@ -29,6 +29,7 @@ def mysum(arr): class TestArrayHandler(unittest.TestCase): def _check_arrays(self, arr, vtk_arr): self.assertEqual(vtk_arr.GetNumberOfTuples(), len(arr)) + msg = f"\n{vtk_arr}" if len(arr.shape) == 2: dim1 = arr.shape[1] self.assertEqual(vtk_arr.GetNumberOfComponents(), dim1) @@ -45,8 +46,7 @@ def _check_arrays(self, arr, vtk_arr): self.assertEqual(chr(int(vtk_arr.GetTuple1(i))), arr[i]) else: for i in range(len(arr)): - self.assertEqual(vtk_arr.GetTuple1(i), arr[i]) - + self.assertEqual(vtk_arr.GetTuple1(i), arr[i], msg=msg) def test_array2vtk(self): """Test Numeric array to VTK array conversion and vice-versa.""" @@ -63,6 +63,7 @@ def test_array2vtk(self): t_z.append(numpy.array([-2147483648, 0, 2147483647], numpy.int32)) t_z.append(numpy.array([ -9223372036854775808, 0, 9223372036854775807], numpy.int64)) + assert t_z[-1][0] == -9223372036854775808 t_z.append(numpy.array([0, 255], numpy.uint8)) t_z.append(numpy.array([0, 65535], numpy.uint16)) t_z.append(numpy.array([0, 4294967295], numpy.uint32)) @@ -160,13 +161,16 @@ def test_array2vtk(self): self.assertEqual(vtk_arr.GetValue(2), 0) self.assertEqual(vtk_arr.GetValue(3), 1) - # Make sure the code at least runs for all the non-complex - # numerical dtypes in numpy. - float_types = [x for x in numpy.sctypes['float'] - if x().dtype.name not in ('float16', 'float128')] - for dtype in (numpy.sctypes['int'] + numpy.sctypes['uint'] + - float_types): - array_handler.array2vtk(numpy.zeros((1,), dtype=dtype)) + # Make sure the code at least runs for all + # numerical dtypes in numpy + # except for half, longdouble and complexfloating + int_types = ['byte', 'short', 'int', 'intc', 'int_', 'long', 'longlong'] + uint_types = ['ubyte', 'ushort', 'uintc', 'uint', 'ulong', + 'ulonglong'] + float_types = ['single', 'double'] + for dtype in int_types + uint_types + float_types: + array_handler.array2vtk(numpy.zeros((1,), + dtype=numpy.dtype(dtype))) def test_arr2cell_array(self): """Test Numeric array to vtkCellArray conversion.""" @@ -201,8 +205,7 @@ def test_arr2cell_array(self): cells = array_handler.array2vtkCellArray(a) arr = array_handler.vtk2array(cells.GetData()) expect = numpy.array([3, 0, 1, 2]*3, int) - self.assertEqual(numpy.alltrue(numpy.equal(arr, expect)), - True) + self.assertTrue(numpy.all(numpy.equal(arr, expect))) self.assertEqual(cells.GetNumberOfCells(), N) # Test if a list of Numeric arrays of different cell lengths works. @@ -210,8 +213,7 @@ def test_arr2cell_array(self): cells = array_handler.array2vtkCellArray(l_a) arr = array_handler.vtk2array(cells.GetData()) expect = numpy.array([1, 0]*3 + [3, 0, 1, 2]*3 + [2, 0,1]*2, int) - self.assertEqual(numpy.alltrue(numpy.equal(arr, expect)), - True) + self.assertTrue(numpy.all(numpy.equal(arr, expect))) self.assertEqual(cells.GetNumberOfCells(), N*2 + 2) # This should not take a long while. This merely tests if a diff --git a/tvtk/wrapper_gen.py b/tvtk/wrapper_gen.py index 95b9810ed..7b3afe02f 100644 --- a/tvtk/wrapper_gen.py +++ b/tvtk/wrapper_gen.py @@ -679,6 +679,10 @@ def _gen_state_methods(self, klass, out): if not vtk_val: default = self._reform_name(meths[m][0][0]) + # Weirdness on NumPy 2.1 and vtk >= 9.3 that this does not show up as + # an option and creates problems + if klass.__name__ == "vtkPoints" and m == "DataType" and sys.platform == "win32": + d["int32"] = vtk.VTK_ID_TYPE if extra_val is None: t_def = """tvtk_base.RevPrefixMap(%(d)s, default_value='%(default)s')""" % locals() elif hasattr(extra_val, '__iter__'): @@ -1591,22 +1595,22 @@ def _write_trait_with_range(self, klass, out, vtk_attr_name): # the code for this trait, # i.e. getattr(self, name_of_method)(...) special_traits = { - '[a-zA-Z0-9]+\.Output$': ( + r'[a-zA-Z0-9]+\.Output$': ( False, False, '_write_any_output'), - '[a-zA-Z0-9]+\.Source$': ( + r'[a-zA-Z0-9]+\.Source$': ( False, False, '_write_any_source'), - '[a-zA-Z0-9]+\.ScalarType$': ( + r'[a-zA-Z0-9]+\.ScalarType$': ( False, False, '_write_any_scalar_type'), # In VTK > 4.5, Set/GetInput have multiple signatures - '[a-zA-Z0-9]+\.Input$': ( + r'[a-zA-Z0-9]+\.Input$': ( False, False, '_write_any_input'), - '[a-zA-Z0-9]+\.InputConnection$': ( + r'[a-zA-Z0-9]+\.InputConnection$': ( False, False, '_write_any_input_connection'), - '[a-zA-Z0-9\.]+FileName$': ( + r'[a-zA-Z0-9\.]+FileName$': ( True, False, '_write_any_something_file_name'), - '[a-zA-Z0-9\.]+FilePrefix$': ( + r'[a-zA-Z0-9\.]+FilePrefix$': ( True, False, '_write_any_something_file_prefix'), 'vtkImageReader2.HeaderSize$': ( True, False, '_write_image_reader2_header_size'),