Python Module: Bundle VFX library python bindings

When calling bpy.utils.expose_bundled_modules(), these modules are
added to sys.path.

This provides a solution/workaround to two problems:
* Using bpy together with packages like usd-core is problematic. Besides
  crashing due to C++ symbol conflicts, it's just impossible to import
  different versions of the same module, or to have distinct environment
  variables for both. (#127132)
* Blender add-ons using these VFX modules do not currently work with
  the bpy module.

This adds about 15MB to the bpy package.

Pull Request: https://projects.blender.org/blender/blender/pulls/133082
This commit is contained in:
Brecht Van Lommel
2025-01-17 10:13:31 +01:00
committed by Brecht Van Lommel
parent ae6b47f346
commit a6b293daac
7 changed files with 201 additions and 116 deletions

View File

@@ -22,6 +22,9 @@ class CustomHydraRenderEngine(bpy.types.HydraRenderEngine):
# Register path to plugin.
@classmethod
def register(cls):
# Make pxr module available, for running as bpy pip package.
bpy.utils.expose_bundled_modules()
import pxr.Plug
pxr.Plug.Registry().RegisterPlugins(['/path/to/plugin'])

View File

@@ -185,11 +185,15 @@ bl_info = {
import bpy
import bpy.types
import textwrap
# Make pxr module available, for running as bpy pip package.
bpy.utils.expose_bundled_modules()
import pxr.Gf as Gf
import pxr.Sdf as Sdf
import pxr.Usd as Usd
import pxr.UsdShade as UsdShade
import textwrap
class USDHookExample(bpy.types.USDHook):

View File

@@ -46,6 +46,7 @@ __all__ = (
"unregister_tool",
"user_resource",
"execfile",
"expose_bundled_modules",
)
from _bpy import (
@@ -1355,3 +1356,27 @@ def make_rna_paths(struct_name, prop_name, enum_name):
else:
src = src_rna = struct_name
return src, src_rna, src_enum
def expose_bundled_modules():
"""
For Blender as a Python module, add bundled VFX library python bindings
to ``sys.path``. These may be used instead of dedicated packages, to ensure
the libraries are compatible with Blender.
"""
# For Blender executable there is nothing to do, already exposed.
if not _bpy.app.module:
return
# System installations do not bundle additional modules,
# these are expected to be installed on the system too.
if not _bpy.app.portable:
return
version_dir = _os.path.normpath(_os.path.join(_bpy.__file__, "..", "..", "..", ".."))
packages_dir = _os.path.join(version_dir, "python", "lib")
if _sys.platform != "win32":
packages_dir = _os.path.join(packages_dir, "python{:d}.{:d}".format(*_sys.version_info[:2]))
packages_dir = _os.path.join(packages_dir, "site-packages")
if packages_dir not in _sys.path:
_sys.path.insert(0, packages_dir)

View File

@@ -46,3 +46,8 @@ if materialx_libs_env is None:
os.environ["PXR_MTLX_STDLIB_SEARCH_PATHS"] = materialx_libs_dir
else:
os.environ["PXR_MTLX_STDLIB_SEARCH_PATHS"] = materialx_libs_env + os.pathsep + materialx_libs_dir
def register():
# To make this work as a startup script for Blender as a Python module.
pass

View File

@@ -88,6 +88,7 @@ static PyStructSequence_Field app_info_fields[] = {
{"version_cycle", "The release status of this build alpha/beta/rc/release"},
{"background",
"Boolean, True when blender is running without a user interface (started with -b)"},
{"module", "Boolean, True when running Blender as a python module"},
{"factory_startup", "Boolean, True when blender is running with --factory-startup)"},
{"portable", "Boolean, True unless blender was built to reference absolute paths (on UNIX)."},
@@ -159,6 +160,11 @@ static PyObject *make_app_info()
SetStrItem(STRINGIFY(BLENDER_VERSION_CYCLE));
SetObjItem(PyBool_FromLong(G.background));
#ifdef WITH_PYTHON_MODULE
SetObjItem(Py_NewRef(Py_True));
#else
SetObjItem(Py_NewRef(Py_False));
#endif
SetObjItem(PyBool_FromLong(G.factory_startup));
#ifdef WITH_INSTALL_PORTABLE

View File

@@ -402,7 +402,7 @@ if(UNIX AND NOT APPLE)
set(TARGETDIR_TEXT "share/doc/blender")
endif()
endif()
set(TARGETDIR_SITE_PACKAGES "${TARGETDIR_VER}/python/lib/python${PYTHON_VERSION}/site-packages")
elseif(WIN32)
if(WITH_PYTHON_MODULE)
set(TARGETDIR_BPY ${CMAKE_INSTALL_PREFIX_WITH_CONFIG}/bpy)
@@ -416,6 +416,7 @@ elseif(WIN32)
set(TARGETDIR_LIB "blender.shared")
set(TARGETDIR_EXE ".")
endif()
set(TARGETDIR_SITE_PACKAGES "${TARGETDIR_VER}/python/lib/site-packages")
elseif(APPLE)
if(WITH_PYTHON_MODULE)
if(WITH_INSTALL_PORTABLE)
@@ -434,6 +435,7 @@ elseif(APPLE)
set(TARGETDIR_LIB "Blender.app/Contents/Resources/lib")
set(TARGETDIR_TEXT "Blender.app/Contents/Resources/text")
endif()
set(TARGETDIR_SITE_PACKAGES "${TARGETDIR_VER}/python/lib/python${PYTHON_VERSION}/site-packages")
# Skip re-linking on CPACK / install.
set_target_properties(blender PROPERTIES BUILD_WITH_INSTALL_RPATH true)
endif()
@@ -465,18 +467,18 @@ if(WITH_PYTHON)
PATTERN "${FREESTYLE_EXCLUDE_CONDITIONAL}" EXCLUDE
)
if(WITH_PYTHON_INSTALL)
if(WIN32)
install(
FILES ${CMAKE_SOURCE_DIR}/scripts/site/sitecustomize.py
DESTINATION ${TARGETDIR_VER}/python/lib/site-packages
)
else()
install(
FILES ${CMAKE_SOURCE_DIR}/scripts/site/sitecustomize.py
DESTINATION ${TARGETDIR_VER}/python/lib/python${PYTHON_VERSION}/site-packages
)
endif()
if(WITH_PYTHON_MODULE)
install(
FILES ${CMAKE_SOURCE_DIR}/scripts/site/sitecustomize.py
DESTINATION ${TARGETDIR_VER}/scripts/startup
# Rename to avoid conflict with system sitecustomize.py
RENAME bpy_site_customize.py
)
elseif(WITH_PYTHON_INSTALL)
install(
FILES ${CMAKE_SOURCE_DIR}/scripts/site/sitecustomize.py
DESTINATION ${TARGETDIR_SITE_PACKAGES}
)
endif()
unset(FREESTYLE_EXCLUDE_CONDITIONAL)
@@ -574,12 +576,12 @@ if(WIN32)
)
install(
FILES ${LIBDIR}/opencolorio/lib/site-packages-debug/PyOpenColorIO_d.pyd
DESTINATION ${TARGETDIR_VER}/python/lib/site-packages
DESTINATION ${TARGETDIR_SITE_PACKAGES}
CONFIGURATIONS Debug
)
install(
FILES ${LIBDIR}/opencolorio/lib/site-packages/PyOpenColorIO.pyd
DESTINATION ${TARGETDIR_VER}/python/lib/site-packages
DESTINATION ${TARGETDIR_SITE_PACKAGES}
CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel
)
endif()
@@ -594,12 +596,12 @@ if(WIN32)
)
install(
DIRECTORY ${LIBDIR}/opencolorio/lib/site-packages-debug/PyOpenColorIO
DESTINATION ${TARGETDIR_VER}/python/lib/site-packages
DESTINATION ${TARGETDIR_SITE_PACKAGES}
CONFIGURATIONS Debug
)
install(
DIRECTORY ${LIBDIR}/opencolorio/lib/site-packages/PyOpenColorIO
DESTINATION ${TARGETDIR_VER}/python/lib/site-packages
DESTINATION ${TARGETDIR_SITE_PACKAGES}
CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel
)
endif()
@@ -1280,104 +1282,6 @@ elseif(WIN32)
CONFIGURATIONS Debug
)
# This will only exist for 3.5+.
if(EXISTS ${LIBDIR}/openimageio/lib/python${PYTHON_VERSION}/site-packages)
install(
DIRECTORY ${LIBDIR}/openimageio/lib/python${PYTHON_VERSION}/site-packages/
DESTINATION ${TARGETDIR_VER}/python/lib/site-packages/
CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel
PATTERN "__pycache__" EXCLUDE # * any cache *
PATTERN "*.pyc" EXCLUDE # * any cache *
PATTERN "*.pyo" EXCLUDE # * any cache *
)
endif()
if(EXISTS ${LIBDIR}/openimageio/lib/python${PYTHON_VERSION}_debug/site-packages)
install(
DIRECTORY ${LIBDIR}/openimageio/lib/python${PYTHON_VERSION}_debug/site-packages/
DESTINATION ${TARGETDIR_VER}/python/lib/site-packages/
CONFIGURATIONS Debug
PATTERN "__pycache__" EXCLUDE # * any cache *
PATTERN "*.pyc" EXCLUDE # * any cache *
PATTERN "*.pyo" EXCLUDE # * any cache *
)
endif()
# This will not exist for 3.4 and earlier `./lib` directory
# to ease the transition, support both 3.4 and 3.5 `./lib` directories.
if(EXISTS ${USD_LIBRARY_DIR}/python/)
install(
DIRECTORY ${USD_LIBRARY_DIR}/python/
DESTINATION ${TARGETDIR_VER}/python/lib/site-packages
CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel
PATTERN "__pycache__" EXCLUDE # * any cache *
PATTERN "*.pyc" EXCLUDE # * any cache *
PATTERN "*.pyo" EXCLUDE # * any cache *
)
endif()
if(EXISTS ${USD_LIBRARY_DIR}/debug/python/)
install(
DIRECTORY ${USD_LIBRARY_DIR}/debug/python/
DESTINATION ${TARGETDIR_VER}/python/lib/site-packages
CONFIGURATIONS Debug
PATTERN "__pycache__" EXCLUDE # * any cache *
PATTERN "*.pyc" EXCLUDE # * any cache *
PATTERN "*.pyo" EXCLUDE # * any cache *
)
endif()
# This will not exist for 3.4 and earlier `./lib` directories
# to ease the transition, support both 3.4 and 3.5 `./lib` directories.
if(EXISTS ${LIBDIR}/openvdb/python/pyopenvdb_d.pyd)
install(
FILES ${LIBDIR}/openvdb/python/pyopenvdb_d.pyd
DESTINATION ${TARGETDIR_VER}/python/lib/site-packages
CONFIGURATIONS Debug
)
install(
FILES ${LIBDIR}/openvdb/python/pyopenvdb.pyd
DESTINATION ${TARGETDIR_VER}/python/lib/site-packages
CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel
)
endif()
# This will exist for 4.1 `./lib` directories.
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "ARM64")
set(_openvdb_arch arm64)
else()
set(_openvdb_arch amd64)
endif()
if(EXISTS ${LIBDIR}/openvdb/python/pyopenvdb_d.cp${_PYTHON_VERSION_NO_DOTS}-win_${_openvdb_arch}.pyd)
install(
FILES ${LIBDIR}/openvdb/python/pyopenvdb_d.cp${_PYTHON_VERSION_NO_DOTS}-win_${_openvdb_arch}.pyd
DESTINATION ${TARGETDIR_VER}/python/lib/site-packages
CONFIGURATIONS Debug
)
install(
FILES ${LIBDIR}/openvdb/python/pyopenvdb.cp${_PYTHON_VERSION_NO_DOTS}-win_${_openvdb_arch}.pyd
DESTINATION ${TARGETDIR_VER}/python/lib/site-packages
CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel
)
endif()
# MaterialX python bindings
install(
DIRECTORY ${LIBDIR}/materialx/python/Release/MaterialX
DESTINATION ${TARGETDIR_VER}/python/lib/site-packages/
CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel
PATTERN "__pycache__" EXCLUDE # * any cache *
PATTERN "*.pyc" EXCLUDE # * any cache *
PATTERN "*.pyo" EXCLUDE # * any cache *
)
install(
DIRECTORY ${LIBDIR}/materialx/python/Debug/MaterialX
DESTINATION ${TARGETDIR_VER}/python/lib/site-packages/
CONFIGURATIONS Debug
PATTERN "__pycache__" EXCLUDE # * any cache *
PATTERN "*.pyc" EXCLUDE # * any cache *
PATTERN "*.pyo" EXCLUDE # * any cache *
)
if(WINDOWS_PYTHON_DEBUG)
install(
FILES
@@ -1395,6 +1299,107 @@ elseif(WIN32)
endif()
endif()
# VFX libs are bundled with both Blender executable and Python module.
if(WITH_PYTHON_INSTALL OR WITH_PYTHON_MODULE)
# This will only exist for 3.5+.
if(EXISTS ${LIBDIR}/openimageio/lib/python${PYTHON_VERSION}/site-packages)
install(
DIRECTORY ${LIBDIR}/openimageio/lib/python${PYTHON_VERSION}/site-packages/
DESTINATION ${TARGETDIR_SITE_PACKAGES}/
CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel
PATTERN "__pycache__" EXCLUDE # * any cache *
PATTERN "*.pyc" EXCLUDE # * any cache *
PATTERN "*.pyo" EXCLUDE # * any cache *
)
endif()
if(EXISTS ${LIBDIR}/openimageio/lib/python${PYTHON_VERSION}_debug/site-packages)
install(
DIRECTORY ${LIBDIR}/openimageio/lib/python${PYTHON_VERSION}_debug/site-packages/
DESTINATION ${TARGETDIR_SITE_PACKAGES}/
CONFIGURATIONS Debug
PATTERN "__pycache__" EXCLUDE # * any cache *
PATTERN "*.pyc" EXCLUDE # * any cache *
PATTERN "*.pyo" EXCLUDE # * any cache *
)
endif()
# This will not exist for 3.4 and earlier `./lib` directory
# to ease the transition, support both 3.4 and 3.5 `./lib` directories.
if(EXISTS ${USD_LIBRARY_DIR}/python/)
install(
DIRECTORY ${USD_LIBRARY_DIR}/python/
DESTINATION ${TARGETDIR_SITE_PACKAGES}
CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel
PATTERN "__pycache__" EXCLUDE # * any cache *
PATTERN "*.pyc" EXCLUDE # * any cache *
PATTERN "*.pyo" EXCLUDE # * any cache *
)
endif()
if(EXISTS ${USD_LIBRARY_DIR}/debug/python/)
install(
DIRECTORY ${USD_LIBRARY_DIR}/debug/python/
DESTINATION ${TARGETDIR_SITE_PACKAGES}
CONFIGURATIONS Debug
PATTERN "__pycache__" EXCLUDE # * any cache *
PATTERN "*.pyc" EXCLUDE # * any cache *
PATTERN "*.pyo" EXCLUDE # * any cache *
)
endif()
# This will not exist for 3.4 and earlier `./lib` directories
# to ease the transition, support both 3.4 and 3.5 `./lib` directories.
if(EXISTS ${LIBDIR}/openvdb/python/pyopenvdb_d.pyd)
install(
FILES ${LIBDIR}/openvdb/python/pyopenvdb_d.pyd
DESTINATION ${TARGETDIR_SITE_PACKAGES}
CONFIGURATIONS Debug
)
install(
FILES ${LIBDIR}/openvdb/python/pyopenvdb.pyd
DESTINATION ${TARGETDIR_SITE_PACKAGES}
CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel
)
endif()
# This will exist for 4.1 `./lib` directories.
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "ARM64")
set(_openvdb_arch arm64)
else()
set(_openvdb_arch amd64)
endif()
if(EXISTS ${LIBDIR}/openvdb/python/pyopenvdb_d.cp${_PYTHON_VERSION_NO_DOTS}-win_${_openvdb_arch}.pyd)
install(
FILES ${LIBDIR}/openvdb/python/pyopenvdb_d.cp${_PYTHON_VERSION_NO_DOTS}-win_${_openvdb_arch}.pyd
DESTINATION ${TARGETDIR_SITE_PACKAGES}
CONFIGURATIONS Debug
)
install(
FILES ${LIBDIR}/openvdb/python/pyopenvdb.cp${_PYTHON_VERSION_NO_DOTS}-win_${_openvdb_arch}.pyd
DESTINATION ${TARGETDIR_SITE_PACKAGES}
CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel
)
endif()
# MaterialX python bindings
install(
DIRECTORY ${LIBDIR}/materialx/python/Release/MaterialX
DESTINATION ${TARGETDIR_SITE_PACKAGES}/
CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel
PATTERN "__pycache__" EXCLUDE # * any cache *
PATTERN "*.pyc" EXCLUDE # * any cache *
PATTERN "*.pyo" EXCLUDE # * any cache *
)
install(
DIRECTORY ${LIBDIR}/materialx/python/Debug/MaterialX
DESTINATION ${TARGETDIR_SITE_PACKAGES}/
CONFIGURATIONS Debug
PATTERN "__pycache__" EXCLUDE # * any cache *
PATTERN "*.pyc" EXCLUDE # * any cache *
PATTERN "*.pyo" EXCLUDE # * any cache *
)
endif()
endif()
# Filenames change slightly between FFMPEG versions check both 6.0 and fallback to 5.0
@@ -1672,6 +1677,34 @@ elseif(APPLE)
endif()
# Windows already installs these, for Unix we need to pick just the VFX libs from
# the site-packages directory.
if(WITH_PYTHON_MODULE AND LIBDIR AND NOT WIN32)
install(
DIRECTORY ${LIBDIR}/python/lib/python${PYTHON_VERSION}/site-packages/MaterialX
DESTINATION ${TARGETDIR_SITE_PACKAGES})
install(
DIRECTORY ${LIBDIR}/python/lib/python${PYTHON_VERSION}/site-packages/OpenImageIO
DESTINATION ${TARGETDIR_SITE_PACKAGES})
install(
DIRECTORY ${LIBDIR}/python/lib/python${PYTHON_VERSION}/site-packages/PyOpenColorIO
DESTINATION ${TARGETDIR_SITE_PACKAGES})
install(
FILES ${LIBDIR}/python/lib/python${PYTHON_VERSION}/site-packages/oslquery.so
DESTINATION ${TARGETDIR_SITE_PACKAGES} OPTIONAL)
install(
DIRECTORY ${LIBDIR}/python/lib/python${PYTHON_VERSION}/site-packages/pxr
DESTINATION ${TARGETDIR_SITE_PACKAGES})
if(APPLE)
set(_pyopenvdb_filename pyopenvdb.cpython-${PYTHON_VERSION_NO_DOTS}-darwin.so)
else()
set(_pyopenvdb_filename pyopenvdb.cpython-${PYTHON_VERSION_NO_DOTS}-${CMAKE_SYSTEM_PROCESSOR}-linux-gnu.so)
endif()
install(
FILES ${LIBDIR}/python/lib/python${PYTHON_VERSION}/site-packages/${_pyopenvdb_filename}
DESTINATION ${TARGETDIR_SITE_PACKAGES})
endif()
# -----------------------------------------------------------------------------
# Generic Install, for all targets

View File

@@ -8,3 +8,12 @@ sys.path.append(sys.argv[1])
# Just import bpy and see if there are any dynamic loader errors.
import bpy
# Try bundled libraries
bpy.utils.expose_bundled_modules()
from pxr import Usd
import MaterialX
import OpenImageIO
import PyOpenColorIO
import pyopenvdb