From c1bfdf2a8436f0b304fe830739daa92ababb1e96 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Thu, 8 Feb 2024 15:17:22 -0500 Subject: [PATCH] FIX: Major refactoring of setup.py --- setup.py | 224 ++++++++++------------------------- tvtk/{setup.py => _setup.py} | 54 +-------- 2 files changed, 64 insertions(+), 214 deletions(-) rename tvtk/{setup.py => _setup.py} (57%) diff --git a/setup.py b/setup.py index 35d82fedf..61a97cc45 100644 --- a/setup.py +++ b/setup.py @@ -3,25 +3,10 @@ # Copyright (c) 2008-2022 by Enthought, Inc. # All rights reserved. -# NOTE: Setuptools must be imported BEFORE numpy.distutils or else -# numpy.distutils does the Wrong(TM) thing. -import setuptools -from setuptools import Command +from setuptools import Command, Extension, setup, find_packages +from setuptools.command.build_py import build_py +from setuptools.command.develop import develop -try: - import numpy - from numpy.distutils.command import build, install_data, build_src - from numpy.distutils.core import setup - my_build_src_super = build_src.build_src - build_src_run = build_src.build_src.run - del build_src - HAS_NUMPY = True -except ImportError: - HAS_NUMPY = False - from distutils.command import build, install_data - from distutils.core import setup - my_build_src_super = object - build_src_run = lambda *args, **kwargs: None import io import os import time @@ -29,14 +14,12 @@ import shutil import re import sys -import traceback from os.path import (abspath, basename, dirname, exists, getmtime, isdir, join, split) -from distutils.command import clean -from distutils import log -from setuptools.command import develop - +sys.path.append(join(os.path.dirname(__file__), 'tvtk')) +from _setup import can_compile_extensions, gen_tvtk_classes_zip +sys.path.pop(-1) MODE = 'normal' if len(sys.argv) >= 2 and \ @@ -65,7 +48,7 @@ class GenDocs(Command): ] def latest_modified(self, the_path, filetypes='', ignore_dirs=''): - """Traverses a path looking for the most recently modified file + """Traverse a path looking for the most recently modified file. Parameters ---------- @@ -84,12 +67,7 @@ def latest_modified(self, the_path, filetypes='', ignore_dirs=''): Modification time of latest_path. latest_path : string Most recently modified file. - - Description - ----------- - """ - file_re = re.compile(filetypes) dir_re = re.compile(ignore_dirs) @@ -120,8 +98,7 @@ def latest_modified(self, the_path, filetypes='', ignore_dirs=''): return getmtime(the_path), the_path def mlab_reference(self): - """ If mayavi is installed, run the mlab_reference generator. - """ + """If mayavi is installed, run the mlab_reference generator.""" # XXX: This is really a hack: the script is not made to be used # for different projects, but it ended up being. This part is # mayavi-specific. @@ -145,12 +122,11 @@ def mlab_reference(self): from mayavi.tools import auto_doc print("Generating the mlab reference documentation") os.system('python mlab_reference.py') - except: + except Exception: pass def example_files(self): - """ Generate the documentation files for the examples. - """ + """Generate the documentation files for the examples.""" mlab_ref_dir = join(DEFAULT_INPUT_DIR, 'mayavi', 'auto') source_path = join('examples', 'mayavi') @@ -270,133 +246,52 @@ def build_tvtk_classes_zip(): return else: print("Building tvtk_classes.zip") - sys.path.insert(0, MY_DIR) - import tvtk - tvtk_dir = 'tvtk' - sys.path.insert(0, tvtk_dir) - from setup import gen_tvtk_classes_zip gen_tvtk_classes_zip() - sys.path.remove(tvtk_dir) - sys.path.remove(MY_DIR) - - -class MyBuild(build.build): - """ A build hook to generate the documentation. - - We sub-class numpy.distutils' build command because we're relying on - numpy.distutils' setup method to build python extensions. - - """ - - def run(self): - build_tvtk_classes_zip() - build.build.run(self) -class MyBuildSrc(my_build_src_super): - """Build hook to generate the TVTK ZIP files. - - We do it here also because for editable installs, setup.py build is not - called. - """ +class MyBuildPy(build_py): + """A build hook to generate the documentation.""" def run(self): build_tvtk_classes_zip() - build_src_run(self) - + super().run() -class MyDevelop(develop.develop): - """ A hook to build the TVTK ZIP file on develop. - Subclassing setuptools' command because numpy.distutils doesn't - have an implementation. - - """ +class MyDevelop(develop): + """A hook to build the TVTK ZIP file on develop.""" def run(self): # Make sure that the 'build_src' command will # always be inplace when we do a 'develop'. - self.reinitialize_command('build_src', inplace=1) + self.reinitialize_command('build_ext', inplace=1) # tvtk_classes.zip always need to be created on 'develop'. build_tvtk_classes_zip() - develop.develop.run(self) - - -class MyInstallData(install_data.install_data): - """ An install hook to copy the generated documentation. - - We subclass numpy.distutils' command because we're relying on - numpy.distutils' setup method to build python extensions. - - """ - - def run(self): - install_data_command = self.get_finalized_command('install_data') - for project in list_doc_projects(): - install_data_command.data_files.extend( - list_docs_data_files(project)) - - # make sure tvtk_classes.zip always get created before putting it - # in the install data. - build_tvtk_classes_zip() - tvtk_dir = 'tvtk' - install_data_command.data_files.append( - (tvtk_dir, [join(tvtk_dir, 'tvtk_classes.zip')])) - - install_data.install_data.run(self) - - -class MyClean(clean.clean): - """Reimplements to remove the extension module array_ext to guarantee a - fresh rebuild every time. The module hanging around could introduce - problems when doing develop for a different vtk version.""" - def run(self): - MY_DIR = os.path.dirname(__file__) - - ext_file = os.path.join( - MY_DIR, - "tvtk", - "array_ext" + (".pyd" if sys.platform == "win32" else ".so") - ) - - if os.path.exists(ext_file): - print("Removing in-place array extensions {}".format(ext_file)) - os.unlink(ext_file) - - clean.clean.run(self) + super().run() # Configure our extensions to Python -def configuration(parent_package=None, top_path=None): - from numpy.distutils.misc_util import Configuration - config = Configuration(None, parent_package, top_path) - config.set_options( - ignore_setup_xxx_py=True, - assume_default_configuration=True, - delegate_options_to_subpackages=True, - quiet=True, - ) - - config.add_subpackage('tvtk') - config.add_data_dir('mayavi/core/lut') - config.add_data_dir('mayavi/tests/data') - config.add_data_dir('mayavi/tests/csv_files') - config.add_data_dir('mayavi/tools/static') - - # Image files. - for pkgdir in ('mayavi', 'tvtk'): - for root, dirs, files in os.walk(pkgdir): - if split(root)[-1] == 'images': - config.add_data_dir(root) - - # *.ini files. - config.add_data_dir('tvtk/plugins/scene') - config.add_data_dir('mayavi/preferences') - - return config - +package_data = dict() +package_data["mayavi"] = [ + 'mayavi/core/lut', + 'mayavi/tests/data', + 'mayavi/tests/csv_files', + 'mayavi/tools/static', + 'mayavi/preferences', +] +# Image files. +for pkgdir in ('mayavi', 'tvtk'): + for root, dirs, files in os.walk(pkgdir): + if split(root)[-1] == 'images': + package_data["mayavi"].append(root) +package_data["tvtk"] = [ + "tvtk/plugins/scene", + "tvtk/tvtk_classes.zip", + "tvtk/pipeline/images", + "tvtk/pyface/images", + "tvtk/tools/images", +] ########################################################################### # Similar to package_data, but installed before build @@ -408,21 +303,14 @@ def configuration(parent_package=None, top_path=None): target_path = package.replace('.', os.sep) for filename in files: shutil.copy(filename, target_path) -########################################################################### -# Build the full set of packages by appending any found by setuptools' -# find_packages to those discovered by numpy.distutils. -if HAS_NUMPY: - config = configuration().todict() +try: + import numpy as np +except Exception: + HAS_NUMPY = False else: - # This is just a dummy so the egg_info command works. - config = {'packages': []} -packages = setuptools.find_packages(exclude=config['packages'] + - ['docs', 'examples']) -config['packages'] += packages - - -if MODE != 'info' and not HAS_NUMPY and sys.version_info < (3, 12): + HAS_NUMPY = True +if not HAS_NUMPY and MODE != 'info': msg = ''' Numpy is required to build Mayavi correctly, please install it first. ''' @@ -431,9 +319,22 @@ def configuration(parent_package=None, top_path=None): print('*'*80) raise RuntimeError(msg) +########################################################################### # The actual setup call if __name__ == '__main__': + ext_modules = list() + if can_compile_extensions(): + import numpy as np + ext_modules.append( + Extension( + "tvtk.array_ext", + sources=[join("tvtk", "src", "array_ext.c")], + depends=[join("tvtk", "src", "array_ext.pyx")], + include_dirs=[np.get_include()], + ) + ) + setup( name='mayavi', version=info['__version__'], @@ -460,14 +361,8 @@ def configuration(parent_package=None, top_path=None): Topic :: Software Development :: Libraries """.splitlines() if len(c.split()) > 0], cmdclass={ - # Work around a numpy distutils bug by forcing the use of the - # setuptools' sdist command. - 'sdist': setuptools.command.sdist.sdist, - 'build': MyBuild, - 'build_src': MyBuildSrc, - 'clean': MyClean, + 'build_py': MyBuildPy, 'develop': MyDevelop, - 'install_data': MyInstallData, 'gen_docs': GenDocs, 'build_docs': BuildDocs, }, @@ -493,11 +388,12 @@ def configuration(parent_package=None, top_path=None): ] }, extras_require=info['__extras_require__'], - include_package_data=True, + packages=find_packages(exclude=["docs", "examples"]), + package_data=package_data, + ext_modules=ext_modules, install_requires=info['__requires__'], license="BSD", long_description=io.open('README.rst', encoding='utf-8').read(), platforms=["Windows", "Linux", "Mac OS-X", "Unix", "Solaris"], zip_safe=False, - **config ) diff --git a/tvtk/setup.py b/tvtk/_setup.py similarity index 57% rename from tvtk/setup.py rename to tvtk/_setup.py index 79e347671..767ab638d 100755 --- a/tvtk/setup.py +++ b/tvtk/_setup.py @@ -6,14 +6,14 @@ import os import sys +from setuptools import Distribution + def can_compile_extensions(): try: import numpy # noqa except Exception: return False # our extension needs numpy/arrayobject.h - from distutils.dist import Distribution - from distutils.errors import DistutilsError sargs = {'script_name': None, 'script_args': ["--build-ext"]} d = Distribution(sargs) cfg = d.get_command_obj('config') @@ -29,59 +29,13 @@ def can_compile_extensions(): include_dirs=build_ext.include_dirs, lang='c' ) - except DistutilsError: + except Exception as exc: + print(f"Compilation failed, assuming no C compiler: {exc}") return False else: return result -def configuration(parent_package=None, top_path=None): - from os.path import join - from numpy.distutils.misc_util import Configuration - config = Configuration('tvtk', parent_package, top_path) - config.set_options(ignore_setup_xxx_py=True, - assume_default_configuration=True, - delegate_options_to_subpackages=True, - quiet=True) - - config.add_subpackage('custom') - config.add_subpackage('pipeline') - config.add_subpackage('pyface') - config.add_subpackage('pyface.*') - config.add_subpackage('pyface.*.*') - config.add_subpackage('view') - - config.add_data_dir('pipeline/images') - config.add_data_dir('pyface/images') - config.add_data_dir('tools/images') - - config.add_subpackage('plugins') - config.add_subpackage('plugins.*') - config.add_subpackage('plugins.*.*') - - config.add_subpackage('tools') - config.add_subpackage('util') - - config.add_subpackage('tests') - - # Add any extensions. These are optional. - if can_compile_extensions(): - import numpy as np - config.add_extension( - 'array_ext', - sources=[join('src', 'array_ext.c')], - depends=[join('src', 'array_ext.pyx')], - include_dirs=[np.get_include()], - ) - - tvtk_classes_zip_depends = config.paths( - 'code_gen.py', 'wrapper_gen.py', 'special_gen.py', - 'tvtk_base.py', 'indenter.py', 'vtk_parser.py' - ) - - return config - - def gen_tvtk_classes_zip(): MY_DIR = os.path.dirname(__file__) sys.path.append(MY_DIR)