1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
|
r"""
Currently, this package is experimental and may change in the future.
"""
from __future__ import absolute_import
import sys
import importlib.util
from pathlib import Path
from importlib.abc import MetaPathFinder
from importlib.machinery import ExtensionFileLoader
LOADING_STACK = []
LIB_EXTS = [".pyd", ".so"]
def find_lib_path(paths, vtk_module_name):
for ext in LIB_EXTS:
for base_path in paths:
# vtk-wheel => vtkCommonCore.cpython-310-darwin.so
# paraview => vtkCommonCore.so
# Caution: vtkIOXML vs vtkIOXMLParser
for f in Path(base_path).glob(f"{vtk_module_name}[.-]*"):
resolved_file = f.resolve()
if resolved_file.is_file() and ext in resolved_file.suffixes:
return str(resolved_file)
class VTKMetaHook(MetaPathFinder):
"""Attach a custom loaded for vtk native library loading to defer loading of pure python dependencies"""
def find_spec(self, fullname, path, target=None):
if fullname.startswith("vtkmodules.vtk"):
vtk_module_name = fullname.split(".")[1]
module_path = find_lib_path(path, vtk_module_name)
if module_path is None:
return None
LOADING_STACK.append(fullname)
return importlib.util.spec_from_file_location(fullname, module_path, loader=VTKLoader(fullname, module_path))
return None
class VTKLoader(ExtensionFileLoader):
"""Flush any pending dependency load once initialize() phase is done"""
def exec_module(self, module):
super().exec_module(module)
# Process pending dependencies only if the module match the first load request
if len(LOADING_STACK) and LOADING_STACK[0] == module.__name__:
LOADING_STACK.clear()
on_vtk_module_init_completed()
# Register our hook for vtk library loader
sys.meta_path.insert(0, VTKMetaHook())
def _windows_dll_path():
import os
_vtk_python_path = '@VTK_PYTHON_SITE_PACKAGES_SUFFIX@/vtkmodules'
_vtk_dll_path = '@CMAKE_INSTALL_BINDIR@'
# Compute the DLL path based on the location of the file and traversing up
# the installation prefix to append the DLL path.
_vtk_dll_directory = os.path.dirname(os.path.abspath(__file__))
# Loop while we have components to remove.
while _vtk_python_path not in ('', '.', '/'):
# Strip a directory away.
_vtk_python_path = os.path.dirname(_vtk_python_path)
_vtk_dll_directory = os.path.dirname(_vtk_dll_directory)
_vtk_dll_directory = os.path.join(_vtk_dll_directory, _vtk_dll_path)
if os.path.exists(_vtk_dll_directory):
# We never remove this path; it is required for VTK to work and there's
# no scope where we can easily remove the directory again.
_ = os.add_dll_directory(_vtk_dll_directory)
# Build tree support.
try:
from . import _build_paths
# Add any paths needed for the build tree.
for path in _build_paths.paths:
if os.path.exists(path):
_ = os.add_dll_directory(path)
except ImportError:
# Relocatable install tree (or non-Windows).
pass
# CPython 3.8 added behaviors which modified the DLL search path on Windows to
# only search "blessed" paths. When importing SMTK, ensure that SMTK's DLLs are
# in this set of "blessed" paths.
if sys.version_info >= (3, 8) and sys.platform == 'win32':
_windows_dll_path()
#------------------------------------------------------------------------------
# this little trick is for static builds of VTK. In such builds, if
# the user imports this Python package in a non-statically linked Python
# interpreter i.e. not of the of the VTK-python executables, then we import the
# static components importer module.
def _load_vtkmodules_static():
if 'vtkmodules_vtkCommonCore' not in sys.builtin_module_names:
import _vtkmodules_static
@_vtkmodules_static_import@
#------------------------------------------------------------------------------
# list the contents
__all__ = [
@_vtkmodules_all@
]
#------------------------------------------------------------------------------
# get the version
__version__ = "@VTK_MAJOR_VERSION@.@VTK_MINOR_VERSION@.@VTK_BUILD_VERSION@"
#------------------------------------------------------------------------------
# describe import dependencies to properly define Python @override
MODULE_MAPPER = {
"vtkCommonDataModel": [
"vtkmodules.util.data_model",
],
"vtkCommonExecutionModel": [
"vtkmodules.util.execution_model",
],
}
LOADED_MODULES = set()
PENDING_LOADED_MODULES = set()
def register_vtk_module_dependencies(vtk_module_name, *import_names):
"""Method to call for registering external override on vtkmodule load"""
MODULE_MAPPER.setdefault(vtk_module_name, []).extend(import_names)
# If already loaded let's make sure we import it now
if vtk_module_name in LOADED_MODULES:
for import_name in import_names:
importlib.import_module(import_name)
def on_vtk_module_init(module_name):
"""Automatically called by vtkmodule when they are loaded"""
if module_name in LOADED_MODULES:
return
PENDING_LOADED_MODULES.add(module_name)
def on_vtk_module_init_completed():
pending = list(PENDING_LOADED_MODULES)
PENDING_LOADED_MODULES.clear()
for module_name in pending:
LOADED_MODULES.add(module_name)
for import_name in MODULE_MAPPER.get(module_name, []):
importlib.import_module(import_name)
|