From ecddf3d18f1a476e76ba6f67f7c31c3f4e7cb175 Mon Sep 17 00:00:00 2001 From: Ian Chua Date: Thu, 2 Jul 2026 17:49:36 +0800 Subject: [PATCH] feat: Python Plugins --- .github/workflows/build_orca.yml | 5 + CMakeLists.txt | 218 +- build_release_macos.sh | 43 + deps/CMakeLists.txt | 2 + deps/python3/python3.cmake | 238 ++ deps/python3/relocate_linux.cmake | 10 + deps/python3/stage_windows.cmake | 72 + deps_src/pybind11/LICENSE | 29 + deps_src/pybind11/README.rst | 216 + deps_src/pybind11/include/pybind11/attr.h | 722 ++++ .../pybind11/include/pybind11/buffer_info.h | 208 + deps_src/pybind11/include/pybind11/cast.h | 2361 +++++++++++ deps_src/pybind11/include/pybind11/chrono.h | 228 ++ deps_src/pybind11/include/pybind11/common.h | 2 + deps_src/pybind11/include/pybind11/complex.h | 74 + .../include/pybind11/conduit/README.txt | 15 + .../pybind11/conduit/pybind11_conduit_v1.h | 116 + .../conduit/pybind11_platform_abi_id.h | 87 + .../pybind11/conduit/wrap_include_python_h.h | 72 + .../include/pybind11/critical_section.h | 56 + .../pybind11/include/pybind11/detail/class.h | 823 ++++ .../pybind11/include/pybind11/detail/common.h | 1348 ++++++ .../include/pybind11/detail/cpp_conduit.h | 75 + .../pybind11/include/pybind11/detail/descr.h | 226 + .../detail/dynamic_raw_ptr_cast_if_possible.h | 39 + .../pybind11/detail/exception_translation.h | 71 + .../detail/function_record_pyobject.h | 191 + .../pybind11/include/pybind11/detail/init.h | 538 +++ .../include/pybind11/detail/internals.h | 799 ++++ .../pybind11/detail/native_enum_data.h | 209 + .../detail/pybind11_namespace_macros.h | 82 + .../pybind11/detail/struct_smart_holder.h | 378 ++ .../pybind11/detail/type_caster_base.h | 1591 +++++++ .../pybind11/include/pybind11/detail/typeid.h | 65 + .../pybind11/detail/using_smart_holder.h | 22 + .../pybind11/detail/value_and_holder.h | 90 + deps_src/pybind11/include/pybind11/eigen.h | 12 + .../pybind11/include/pybind11/eigen/common.h | 9 + .../pybind11/include/pybind11/eigen/matrix.h | 723 ++++ .../pybind11/include/pybind11/eigen/tensor.h | 521 +++ deps_src/pybind11/include/pybind11/embed.h | 320 ++ deps_src/pybind11/include/pybind11/eval.h | 161 + .../pybind11/include/pybind11/functional.h | 147 + deps_src/pybind11/include/pybind11/gil.h | 199 + .../include/pybind11/gil_safe_call_once.h | 102 + .../pybind11/include/pybind11/gil_simple.h | 37 + deps_src/pybind11/include/pybind11/iostream.h | 265 ++ .../pybind11/include/pybind11/native_enum.h | 67 + deps_src/pybind11/include/pybind11/numpy.h | 2312 +++++++++++ .../pybind11/include/pybind11/operators.h | 202 + deps_src/pybind11/include/pybind11/options.h | 92 + deps_src/pybind11/include/pybind11/pybind11.h | 3645 +++++++++++++++++ deps_src/pybind11/include/pybind11/pytypes.h | 2680 ++++++++++++ deps_src/pybind11/include/pybind11/stl.h | 666 +++ .../include/pybind11/stl/filesystem.h | 114 + deps_src/pybind11/include/pybind11/stl_bind.h | 858 ++++ .../include/pybind11/subinterpreter.h | 299 ++ .../pybind11/trampoline_self_life_support.h | 65 + .../pybind11/type_caster_pyobject_ptr.h | 61 + deps_src/pybind11/include/pybind11/typing.h | 298 ++ deps_src/pybind11/include/pybind11/warnings.h | 75 + docs/plugins/examples/host_ui_panel.py | 475 +++ .../examples/multi_capability_skeleton.py | 141 + docs/plugins/plugin_audit_hook.md | 385 ++ docs/plugins/plugin_development.md | 1015 +++++ docs/plugins/plugin_system.md | 344 ++ resources/images/param_add.svg | 8 + .../orca_plugins/BBLPrinterAgentPlugin.py | 1243 ++++++ .../web/dialog/PluginWebDialog/blank.html | 8 + resources/web/dialog/PluginsDialog/index.html | 141 + resources/web/dialog/PluginsDialog/index.js | 1030 +++++ resources/web/dialog/PluginsDialog/styles.css | 1089 +++++ .../web/dialog/TerminalDialog/index.html | 29 + resources/web/dialog/TerminalDialog/index.js | 125 + .../web/dialog/TerminalDialog/styles.css | 74 + resources/web/dialog/css/dark.css | 20 +- .../web/include/xterm/xterm-addon-fit.js | 8 + resources/web/include/xterm/xterm.css | 218 + resources/web/include/xterm/xterm.js | 8 + scripts/flatpak/com.orcaslicer.OrcaSlicer.yml | 6 + scripts/generate_orca_python_stubs.py | 212 + src/CMakeLists.txt | 63 + src/OrcaSlicer.cpp | 1 - src/OrcaSlicer_app_msvc.cpp | 8 + .../platform/unix/build_linux_image.sh.in | 27 + src/libslic3r/CMakeLists.txt | 2 - src/libslic3r/Config.cpp | 83 +- src/libslic3r/Config.hpp | 20 +- src/libslic3r/GCode/PostProcessor.cpp | 379 -- src/libslic3r/GCode/PostProcessor.hpp | 34 - src/libslic3r/Platform.cpp | 26 + src/libslic3r/Platform.hpp | 2 + src/libslic3r/Preset.cpp | 15 +- src/libslic3r/PresetBundle.cpp | 8 +- src/libslic3r/Print.cpp | 3 + src/libslic3r/PrintConfig.cpp | 21 +- src/libslic3r/PrintConfig.hpp | 1 + src/slic3r/CMakeLists.txt | 84 +- src/slic3r/GUI/BackgroundSlicingProcess.cpp | 1259 +++--- src/slic3r/GUI/DeviceCore/DevFilaSystem.cpp | 150 + src/slic3r/GUI/DeviceCore/DevFilaSystem.h | 2 + src/slic3r/GUI/Field.cpp | 497 ++- src/slic3r/GUI/Field.hpp | 51 + src/slic3r/GUI/GLCanvas3D.cpp | 5 + src/slic3r/GUI/GUI.cpp | 10 + src/slic3r/GUI/GUI_App.cpp | 313 +- src/slic3r/GUI/GUI_App.hpp | 7 + src/slic3r/GUI/MainFrame.cpp | 61 +- src/slic3r/GUI/NotificationManager.cpp | 87 + src/slic3r/GUI/NotificationManager.hpp | 66 + src/slic3r/GUI/OptionsGroup.cpp | 1097 +++-- src/slic3r/GUI/OptionsGroup.hpp | 6 +- src/slic3r/GUI/Plater.cpp | 252 +- src/slic3r/GUI/Plater.hpp | 19 + src/slic3r/GUI/PluginPickerDialog.cpp | 190 + src/slic3r/GUI/PluginPickerDialog.hpp | 60 + src/slic3r/GUI/PluginProgressDialog.cpp | 134 + src/slic3r/GUI/PluginProgressDialog.hpp | 66 + src/slic3r/GUI/PluginWebDialog.cpp | 318 ++ src/slic3r/GUI/PluginWebDialog.hpp | 92 + src/slic3r/GUI/PluginsDialog.cpp | 1226 ++++++ src/slic3r/GUI/PluginsDialog.hpp | 226 + src/slic3r/GUI/PostProcessor.cpp | 484 +++ src/slic3r/GUI/PostProcessor.hpp | 39 + src/slic3r/GUI/ProcessRunner.cpp | 254 ++ src/slic3r/GUI/ProcessRunner.hpp | 93 + src/slic3r/GUI/Tab.cpp | 55 +- src/slic3r/GUI/Tab.hpp | 1 + src/slic3r/GUI/TerminalDialog.cpp | 163 + src/slic3r/GUI/TerminalDialog.hpp | 36 + src/slic3r/GUI/Widgets/WebViewHostDialog.cpp | 133 + src/slic3r/GUI/Widgets/WebViewHostDialog.hpp | 56 + src/slic3r/GeneratedConfig.hpp.in | 3 +- src/slic3r/Utils/IPrinterAgent.hpp | 2 + src/slic3r/Utils/NetworkAgent.cpp | 9 +- src/slic3r/Utils/NetworkAgentFactory.cpp | 334 +- src/slic3r/Utils/NetworkAgentFactory.hpp | 32 +- src/slic3r/Utils/OrcaCloudServiceAgent.cpp | 1256 ++++-- src/slic3r/Utils/OrcaCloudServiceAgent.hpp | 37 +- src/slic3r/plugin/CloudPluginService.cpp | 347 ++ src/slic3r/plugin/CloudPluginService.hpp | 46 + src/slic3r/plugin/PluginAuditManager.cpp | 281 ++ src/slic3r/plugin/PluginAuditManager.hpp | 108 + src/slic3r/plugin/PluginCatalog.cpp | 714 ++++ src/slic3r/plugin/PluginCatalog.hpp | 76 + src/slic3r/plugin/PluginDescriptor.hpp | 222 + src/slic3r/plugin/PluginFsUtils.cpp | 110 + src/slic3r/plugin/PluginFsUtils.hpp | 35 + src/slic3r/plugin/PluginHostApi.cpp | 513 +++ src/slic3r/plugin/PluginHostApi.hpp | 13 + src/slic3r/plugin/PluginHostUi.cpp | 562 +++ src/slic3r/plugin/PluginHostUi.hpp | 24 + src/slic3r/plugin/PluginLoader.cpp | 1506 +++++++ src/slic3r/plugin/PluginLoader.hpp | 212 + src/slic3r/plugin/PluginManager.cpp | 732 ++++ src/slic3r/plugin/PluginManager.hpp | 163 + src/slic3r/plugin/PluginResolver.cpp | 401 ++ src/slic3r/plugin/PluginResolver.hpp | 88 + src/slic3r/plugin/PyPluginPackage.hpp | 27 + src/slic3r/plugin/PyPluginTrampoline.hpp | 110 + src/slic3r/plugin/PythonFileUtils.cpp | 955 +++++ src/slic3r/plugin/PythonFileUtils.hpp | 61 + src/slic3r/plugin/PythonInterpreter.cpp | 996 +++++ src/slic3r/plugin/PythonInterpreter.hpp | 148 + src/slic3r/plugin/PythonPluginBridge.cpp | 398 ++ src/slic3r/plugin/PythonPluginBridge.hpp | 50 + src/slic3r/plugin/PythonPluginInterface.hpp | 119 + .../gcode/GCodePluginCapability.cpp | 30 + .../gcode/GCodePluginCapability.hpp | 27 + .../gcode/GCodePluginCapabilityTrampoline.hpp | 35 + .../PrinterAgentPluginCapability.cpp | 121 + .../PrinterAgentPluginCapability.hpp | 83 + ...PrinterAgentPluginCapabilityTrampoline.hpp | 237 ++ .../script/ScriptPluginCapability.cpp | 24 + .../script/ScriptPluginCapability.hpp | 19 + .../ScriptPluginCapabilityTrampoline.hpp | 26 + tests/libslic3r/CMakeLists.txt | 1 + tests/libslic3r/test_config.cpp | 86 +- tests/libslic3r/test_preset_diff.cpp | 35 + tests/slic3rutils/CMakeLists.txt | 19 +- .../test_plugin_capability_identifier.cpp | 25 + tests/slic3rutils/test_plugin_host_api.cpp | 354 ++ tests/slic3rutils/test_plugin_install.cpp | 148 + 183 files changed, 49955 insertions(+), 2120 deletions(-) create mode 100644 deps/python3/python3.cmake create mode 100644 deps/python3/relocate_linux.cmake create mode 100644 deps/python3/stage_windows.cmake create mode 100644 deps_src/pybind11/LICENSE create mode 100644 deps_src/pybind11/README.rst create mode 100644 deps_src/pybind11/include/pybind11/attr.h create mode 100644 deps_src/pybind11/include/pybind11/buffer_info.h create mode 100644 deps_src/pybind11/include/pybind11/cast.h create mode 100644 deps_src/pybind11/include/pybind11/chrono.h create mode 100644 deps_src/pybind11/include/pybind11/common.h create mode 100644 deps_src/pybind11/include/pybind11/complex.h create mode 100644 deps_src/pybind11/include/pybind11/conduit/README.txt create mode 100644 deps_src/pybind11/include/pybind11/conduit/pybind11_conduit_v1.h create mode 100644 deps_src/pybind11/include/pybind11/conduit/pybind11_platform_abi_id.h create mode 100644 deps_src/pybind11/include/pybind11/conduit/wrap_include_python_h.h create mode 100644 deps_src/pybind11/include/pybind11/critical_section.h create mode 100644 deps_src/pybind11/include/pybind11/detail/class.h create mode 100644 deps_src/pybind11/include/pybind11/detail/common.h create mode 100644 deps_src/pybind11/include/pybind11/detail/cpp_conduit.h create mode 100644 deps_src/pybind11/include/pybind11/detail/descr.h create mode 100644 deps_src/pybind11/include/pybind11/detail/dynamic_raw_ptr_cast_if_possible.h create mode 100644 deps_src/pybind11/include/pybind11/detail/exception_translation.h create mode 100644 deps_src/pybind11/include/pybind11/detail/function_record_pyobject.h create mode 100644 deps_src/pybind11/include/pybind11/detail/init.h create mode 100644 deps_src/pybind11/include/pybind11/detail/internals.h create mode 100644 deps_src/pybind11/include/pybind11/detail/native_enum_data.h create mode 100644 deps_src/pybind11/include/pybind11/detail/pybind11_namespace_macros.h create mode 100644 deps_src/pybind11/include/pybind11/detail/struct_smart_holder.h create mode 100644 deps_src/pybind11/include/pybind11/detail/type_caster_base.h create mode 100644 deps_src/pybind11/include/pybind11/detail/typeid.h create mode 100644 deps_src/pybind11/include/pybind11/detail/using_smart_holder.h create mode 100644 deps_src/pybind11/include/pybind11/detail/value_and_holder.h create mode 100644 deps_src/pybind11/include/pybind11/eigen.h create mode 100644 deps_src/pybind11/include/pybind11/eigen/common.h create mode 100644 deps_src/pybind11/include/pybind11/eigen/matrix.h create mode 100644 deps_src/pybind11/include/pybind11/eigen/tensor.h create mode 100644 deps_src/pybind11/include/pybind11/embed.h create mode 100644 deps_src/pybind11/include/pybind11/eval.h create mode 100644 deps_src/pybind11/include/pybind11/functional.h create mode 100644 deps_src/pybind11/include/pybind11/gil.h create mode 100644 deps_src/pybind11/include/pybind11/gil_safe_call_once.h create mode 100644 deps_src/pybind11/include/pybind11/gil_simple.h create mode 100644 deps_src/pybind11/include/pybind11/iostream.h create mode 100644 deps_src/pybind11/include/pybind11/native_enum.h create mode 100644 deps_src/pybind11/include/pybind11/numpy.h create mode 100644 deps_src/pybind11/include/pybind11/operators.h create mode 100644 deps_src/pybind11/include/pybind11/options.h create mode 100644 deps_src/pybind11/include/pybind11/pybind11.h create mode 100644 deps_src/pybind11/include/pybind11/pytypes.h create mode 100644 deps_src/pybind11/include/pybind11/stl.h create mode 100644 deps_src/pybind11/include/pybind11/stl/filesystem.h create mode 100644 deps_src/pybind11/include/pybind11/stl_bind.h create mode 100644 deps_src/pybind11/include/pybind11/subinterpreter.h create mode 100644 deps_src/pybind11/include/pybind11/trampoline_self_life_support.h create mode 100644 deps_src/pybind11/include/pybind11/type_caster_pyobject_ptr.h create mode 100644 deps_src/pybind11/include/pybind11/typing.h create mode 100644 deps_src/pybind11/include/pybind11/warnings.h create mode 100644 docs/plugins/examples/host_ui_panel.py create mode 100644 docs/plugins/examples/multi_capability_skeleton.py create mode 100644 docs/plugins/plugin_audit_hook.md create mode 100644 docs/plugins/plugin_development.md create mode 100644 docs/plugins/plugin_system.md create mode 100644 resources/images/param_add.svg create mode 100644 resources/orca_plugins/BBLPrinterAgentPlugin.py create mode 100644 resources/web/dialog/PluginWebDialog/blank.html create mode 100644 resources/web/dialog/PluginsDialog/index.html create mode 100644 resources/web/dialog/PluginsDialog/index.js create mode 100644 resources/web/dialog/PluginsDialog/styles.css create mode 100644 resources/web/dialog/TerminalDialog/index.html create mode 100644 resources/web/dialog/TerminalDialog/index.js create mode 100644 resources/web/dialog/TerminalDialog/styles.css create mode 100644 resources/web/include/xterm/xterm-addon-fit.js create mode 100644 resources/web/include/xterm/xterm.css create mode 100644 resources/web/include/xterm/xterm.js create mode 100644 scripts/generate_orca_python_stubs.py delete mode 100644 src/libslic3r/GCode/PostProcessor.cpp delete mode 100644 src/libslic3r/GCode/PostProcessor.hpp create mode 100644 src/slic3r/GUI/PluginPickerDialog.cpp create mode 100644 src/slic3r/GUI/PluginPickerDialog.hpp create mode 100644 src/slic3r/GUI/PluginProgressDialog.cpp create mode 100644 src/slic3r/GUI/PluginProgressDialog.hpp create mode 100644 src/slic3r/GUI/PluginWebDialog.cpp create mode 100644 src/slic3r/GUI/PluginWebDialog.hpp create mode 100644 src/slic3r/GUI/PluginsDialog.cpp create mode 100644 src/slic3r/GUI/PluginsDialog.hpp create mode 100644 src/slic3r/GUI/PostProcessor.cpp create mode 100644 src/slic3r/GUI/PostProcessor.hpp create mode 100644 src/slic3r/GUI/ProcessRunner.cpp create mode 100644 src/slic3r/GUI/ProcessRunner.hpp create mode 100644 src/slic3r/GUI/TerminalDialog.cpp create mode 100644 src/slic3r/GUI/TerminalDialog.hpp create mode 100644 src/slic3r/GUI/Widgets/WebViewHostDialog.cpp create mode 100644 src/slic3r/GUI/Widgets/WebViewHostDialog.hpp create mode 100644 src/slic3r/plugin/CloudPluginService.cpp create mode 100644 src/slic3r/plugin/CloudPluginService.hpp create mode 100644 src/slic3r/plugin/PluginAuditManager.cpp create mode 100644 src/slic3r/plugin/PluginAuditManager.hpp create mode 100644 src/slic3r/plugin/PluginCatalog.cpp create mode 100644 src/slic3r/plugin/PluginCatalog.hpp create mode 100644 src/slic3r/plugin/PluginDescriptor.hpp create mode 100644 src/slic3r/plugin/PluginFsUtils.cpp create mode 100644 src/slic3r/plugin/PluginFsUtils.hpp create mode 100644 src/slic3r/plugin/PluginHostApi.cpp create mode 100644 src/slic3r/plugin/PluginHostApi.hpp create mode 100644 src/slic3r/plugin/PluginHostUi.cpp create mode 100644 src/slic3r/plugin/PluginHostUi.hpp create mode 100644 src/slic3r/plugin/PluginLoader.cpp create mode 100644 src/slic3r/plugin/PluginLoader.hpp create mode 100644 src/slic3r/plugin/PluginManager.cpp create mode 100644 src/slic3r/plugin/PluginManager.hpp create mode 100644 src/slic3r/plugin/PluginResolver.cpp create mode 100644 src/slic3r/plugin/PluginResolver.hpp create mode 100644 src/slic3r/plugin/PyPluginPackage.hpp create mode 100644 src/slic3r/plugin/PyPluginTrampoline.hpp create mode 100644 src/slic3r/plugin/PythonFileUtils.cpp create mode 100644 src/slic3r/plugin/PythonFileUtils.hpp create mode 100644 src/slic3r/plugin/PythonInterpreter.cpp create mode 100644 src/slic3r/plugin/PythonInterpreter.hpp create mode 100644 src/slic3r/plugin/PythonPluginBridge.cpp create mode 100644 src/slic3r/plugin/PythonPluginBridge.hpp create mode 100644 src/slic3r/plugin/PythonPluginInterface.hpp create mode 100644 src/slic3r/plugin/pluginTypes/gcode/GCodePluginCapability.cpp create mode 100644 src/slic3r/plugin/pluginTypes/gcode/GCodePluginCapability.hpp create mode 100644 src/slic3r/plugin/pluginTypes/gcode/GCodePluginCapabilityTrampoline.hpp create mode 100644 src/slic3r/plugin/pluginTypes/printerAgent/PrinterAgentPluginCapability.cpp create mode 100644 src/slic3r/plugin/pluginTypes/printerAgent/PrinterAgentPluginCapability.hpp create mode 100644 src/slic3r/plugin/pluginTypes/printerAgent/PrinterAgentPluginCapabilityTrampoline.hpp create mode 100644 src/slic3r/plugin/pluginTypes/script/ScriptPluginCapability.cpp create mode 100644 src/slic3r/plugin/pluginTypes/script/ScriptPluginCapability.hpp create mode 100644 src/slic3r/plugin/pluginTypes/script/ScriptPluginCapabilityTrampoline.hpp create mode 100644 tests/libslic3r/test_preset_diff.cpp create mode 100644 tests/slic3rutils/test_plugin_capability_identifier.cpp create mode 100644 tests/slic3rutils/test_plugin_host_api.cpp create mode 100644 tests/slic3rutils/test_plugin_install.cpp diff --git a/.github/workflows/build_orca.yml b/.github/workflows/build_orca.yml index 0bc2c37a20..0da4e6dfec 100644 --- a/.github/workflows/build_orca.yml +++ b/.github/workflows/build_orca.yml @@ -204,9 +204,14 @@ jobs: security list-keychain -d user -s $KEYCHAIN_PATH security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k $P12_PASSWORD $KEYCHAIN_PATH codesign --deep --force --verbose --options runtime --timestamp --entitlements ${{ github.workspace }}/scripts/disable_validation.entitlements --sign "$CERTIFICATE_ID" ${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer.app + # --deep is deprecated and signing/sealing can fail or skip files in + # non-standard layouts (python/bin, python/lib/**, tools/uv). Verify + # here so coverage gaps fail at CI time rather than at notarization. + codesign --verify --deep --strict --verbose=2 ${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer.app # Sign OrcaSlicer_profile_validator.app if it exists if [ -f "${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer_profile_validator.app/Contents/MacOS/OrcaSlicer_profile_validator" ]; then codesign --deep --force --verbose --options runtime --timestamp --entitlements ${{ github.workspace }}/scripts/disable_validation.entitlements --sign "$CERTIFICATE_ID" ${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer_profile_validator.app + codesign --verify --deep --strict --verbose=2 ${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer_profile_validator.app fi # Create main OrcaSlicer DMG without the profile validator helper diff --git a/CMakeLists.txt b/CMakeLists.txt index 8dcd4cf004..63d48c4f5e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -125,6 +125,131 @@ option(SLIC3R_PCH "Use precompiled headers" 1) option(SLIC3R_MSVC_COMPILE_PARALLEL "Compile on Visual Studio in parallel" 1) option(SLIC3R_MSVC_PDB "Generate PDB files on MSVC in Release mode" 1) option(SLIC3R_ASAN "Enable ASan on Clang and GCC" 0) + +# Python stubgen module +option(ORCA_BUILD_PYTHON_STUBGEN_MODULE "Build importable Python module for pybind11-stubgen" ON) +set(ORCA_BUNDLED_UV_EXECUTABLE "" CACHE FILEPATH "Path to a uv executable to bundle for Python package installation. Leave empty to auto-download.") + +# Auto-download uv if not provided by the user. +# uv is pinned: the executable ships inside the signed app, so the build must +# be reproducible and the download verified. To upgrade, bump ORCA_UV_VERSION +# and refresh every hash below from the release's .sha256 assets, e.g.: +# curl -sL https://github.com/astral-sh/uv/releases/download//uv-.tar.gz.sha256 +set(ORCA_UV_VERSION "0.11.21") +set(ORCA_UV_SHA256_aarch64-apple-darwin "1f921d491ba5ffeea774eb04d6681ecee379101341cbb1500394993b541bf3f4") +set(ORCA_UV_SHA256_x86_64-apple-darwin "f3c8e5708a84b920c18b691214d54d2b0da6b984789caae95d47c95120cb7765") +set(ORCA_UV_SHA256_aarch64-unknown-linux-gnu "88e800834007cc5efd4675f166eb2a51e7e3ad19876d85fa8805a6fb5c922397") +set(ORCA_UV_SHA256_x86_64-unknown-linux-gnu "8c88519b0ef0af9801fcdee419bbb12116bd9e6b18e162ae093c932d8b264050") +set(ORCA_UV_SHA256_x86_64-pc-windows-msvc "ace861f360c6de2babedc1607d0f454b6b09a820dbc8182dc15af927e4df9589") + +# Version-scoped cache dir so a version bump invalidates the cached binary. +set(ORCA_UV_DOWNLOAD_DIR "${CMAKE_BINARY_DIR}/.uv/${ORCA_UV_VERSION}") +set(ORCA_UV_BINARY "${ORCA_UV_DOWNLOAD_DIR}/uv") +if(WIN32) + set(ORCA_UV_BINARY "${ORCA_UV_DOWNLOAD_DIR}/uv.exe") +endif() + +# Older configures FORCE-cached the auto-downloaded path; any cache entry +# under .uv/ is that legacy value -- clear it so auto-download re-resolves +# (user-provided paths never live under .uv/). +if(ORCA_BUNDLED_UV_EXECUTABLE MATCHES "/\\.uv/") + set(ORCA_BUNDLED_UV_EXECUTABLE "" CACHE FILEPATH "Path to the auto-downloaded uv executable" FORCE) +endif() + +if(NOT ORCA_BUNDLED_UV_EXECUTABLE) + if(NOT EXISTS "${ORCA_UV_BINARY}") + # Select uv by TARGET arch. On macOS universal builds each leg sets a single + # CMAKE_OSX_ARCHITECTURES (and the x86_64 leg cross-builds on an arm64 runner), + # so prefer it over the host CMAKE_SYSTEM_PROCESSOR -- otherwise both legs fetch + # the same arch and the later universal lipo merge of tools/uv/uv fails. + set(_orca_uv_proc "${CMAKE_SYSTEM_PROCESSOR}") + if(APPLE AND CMAKE_OSX_ARCHITECTURES) + set(_orca_uv_proc "${CMAKE_OSX_ARCHITECTURES}") + endif() + # All release archives are tar.gz except the Windows zip. + set(ORCA_UV_EXT "tar.gz") + if(_orca_uv_proc MATCHES "x86_64|AMD64|amd64") + if(WIN32) + set(ORCA_UV_ARCH "x86_64-pc-windows-msvc") + set(ORCA_UV_EXT "zip") + elseif(APPLE) + set(ORCA_UV_ARCH "x86_64-apple-darwin") + else() + set(ORCA_UV_ARCH "x86_64-unknown-linux-gnu") + endif() + elseif(_orca_uv_proc MATCHES "aarch64|arm64|ARM64") + if(APPLE) + set(ORCA_UV_ARCH "aarch64-apple-darwin") + else() + set(ORCA_UV_ARCH "aarch64-unknown-linux-gnu") + endif() + else() + message(WARNING "Unsupported architecture for auto-downloading uv: ${_orca_uv_proc}") + endif() + + if(ORCA_UV_ARCH) + set(ORCA_UV_URL "https://github.com/astral-sh/uv/releases/download/${ORCA_UV_VERSION}/uv-${ORCA_UV_ARCH}.${ORCA_UV_EXT}") + set(ORCA_UV_ARCHIVE "${ORCA_UV_DOWNLOAD_DIR}/uv-archive.${ORCA_UV_EXT}") + file(MAKE_DIRECTORY "${ORCA_UV_DOWNLOAD_DIR}") + message(STATUS "Downloading uv ${ORCA_UV_VERSION} from ${ORCA_UV_URL} ...") + file(DOWNLOAD "${ORCA_UV_URL}" "${ORCA_UV_ARCHIVE}" STATUS ORCA_UV_DL_STATUS TLS_VERIFY ON) + list(GET ORCA_UV_DL_STATUS 0 ORCA_UV_DL_CODE) + list(GET ORCA_UV_DL_STATUS 1 ORCA_UV_DL_MSG) + if(NOT ORCA_UV_DL_CODE EQUAL 0) + # Network failure keeps the historical graceful degradation (uv is + # optional at build time) -- but a hash mismatch below is fatal. + message(WARNING "Failed to download uv: ${ORCA_UV_DL_MSG}") + file(REMOVE "${ORCA_UV_ARCHIVE}") + else() + file(SHA256 "${ORCA_UV_ARCHIVE}" _orca_uv_actual_sha256) + if(NOT _orca_uv_actual_sha256 STREQUAL "${ORCA_UV_SHA256_${ORCA_UV_ARCH}}") + file(REMOVE "${ORCA_UV_ARCHIVE}") + message(FATAL_ERROR + "uv archive checksum mismatch for ${ORCA_UV_ARCH} ${ORCA_UV_VERSION}:\n" + " expected ${ORCA_UV_SHA256_${ORCA_UV_ARCH}}\n" + " actual ${_orca_uv_actual_sha256}\n" + "Possible tampering or a stale pin; refusing to bundle.") + endif() + message(STATUS "Extracting uv ...") + file(ARCHIVE_EXTRACT INPUT "${ORCA_UV_ARCHIVE}" DESTINATION "${ORCA_UV_DOWNLOAD_DIR}") + file(REMOVE "${ORCA_UV_ARCHIVE}") + # Pinned archives have a fixed layout: the tarballs hold + # uv-/uv, the Windows zip holds uv.exe at the root + # (already at ORCA_UV_BINARY). A layout change implies a new + # archive, hence a hash bump -- and RENAME fails loudly. + if(NOT WIN32) + file(RENAME "${ORCA_UV_DOWNLOAD_DIR}/uv-${ORCA_UV_ARCH}/uv" "${ORCA_UV_BINARY}") + file(REMOVE_RECURSE "${ORCA_UV_DOWNLOAD_DIR}/uv-${ORCA_UV_ARCH}") + endif() + endif() + endif() + endif() + + if(EXISTS "${ORCA_UV_BINARY}") + # Plain set (shadows the empty cache entry for this configure run): + # the version-scoped path re-derives every configure, so a version + # bump takes effect in warm build dirs without cache surgery. + set(ORCA_BUNDLED_UV_EXECUTABLE "${ORCA_UV_BINARY}") + endif() +endif() + +# Resolve ORCA_BUNDLED_UV_EXECUTABLE -> ORCA_BUNDLED_UV_EXECUTABLE_CONFIG / ORCA_BUNDLED_UV_FILENAME +# Must run before add_subdirectory(src) so target_compile_definitions sees it. +if(ORCA_BUNDLED_UV_EXECUTABLE) + if(EXISTS "${ORCA_BUNDLED_UV_EXECUTABLE}") + file(TO_CMAKE_PATH "${ORCA_BUNDLED_UV_EXECUTABLE}" ORCA_BUNDLED_UV_EXECUTABLE_CONFIG) + get_filename_component(ORCA_BUNDLED_UV_FILENAME "${ORCA_BUNDLED_UV_EXECUTABLE}" NAME) + else() + message(WARNING "ORCA_BUNDLED_UV_EXECUTABLE does not exist: ${ORCA_BUNDLED_UV_EXECUTABLE}") + set(ORCA_BUNDLED_UV_EXECUTABLE "") + set(ORCA_BUNDLED_UV_EXECUTABLE_CONFIG "") + set(ORCA_BUNDLED_UV_FILENAME "") + endif() +else() + set(ORCA_BUNDLED_UV_EXECUTABLE_CONFIG "") + set(ORCA_BUNDLED_UV_FILENAME "") +endif() + # If SLIC3R_FHS is 1 -> SLIC3R_DESKTOP_INTEGRATION is always 0, othrewise variable. CMAKE_DEPENDENT_OPTION(SLIC3R_DESKTOP_INTEGRATION "Allow performing desktop integration during runtime" 1 "NOT SLIC3R_FHS" 0) @@ -750,6 +875,86 @@ endforeach() find_package(NLopt 1.4 REQUIRED) +# Use bundled Python from deps instead of system Python +set(_bundled_python_version "3.12.3") +set(_bundled_python_abi "312") +string(REGEX REPLACE "^([0-9]+\\.[0-9]+)\\..*$" "\\1" _bundled_python_version_short "${_bundled_python_version}") +set(_bundled_python_root "${CMAKE_PREFIX_PATH}/libpython") +set(Python3_ROOT_DIR "${_bundled_python_root}" CACHE PATH "Root directory for bundled Python" FORCE) +set(Python3_USE_STATIC_LIBS OFF) +set(Python3_FIND_STRATEGY LOCATION) +set(Python3_FIND_IMPLEMENTATIONS CPython) + +if(WIN32) + set(_bundled_python_executable "${_bundled_python_root}/python.exe") + set(_bundled_python_library "${_bundled_python_root}/libs/python${_bundled_python_abi}.lib") + if(EXISTS "${_bundled_python_root}/python_d.exe") + set(_bundled_python_executable "${_bundled_python_root}/python_d.exe") + endif() + if(EXISTS "${_bundled_python_root}/libs/python${_bundled_python_abi}_d.lib") + set(_bundled_python_library "${_bundled_python_root}/libs/python${_bundled_python_abi}_d.lib") + endif() + + set(Python3_FIND_REGISTRY NEVER) + set(Python3_EXECUTABLE "${_bundled_python_executable}" CACHE FILEPATH "Bundled Python executable" FORCE) + set(Python3_INCLUDE_DIR "${_bundled_python_root}/include" CACHE PATH "Bundled Python include directory" FORCE) + set(Python3_LIBRARY "${_bundled_python_library}" CACHE FILEPATH "Bundled Python embed import library" FORCE) +elseif(APPLE) + set(Python3_FIND_FRAMEWORK NEVER) + + find_program(_bundled_python_executable + NAMES python${_bundled_python_version_short} python3 + PATHS "${_bundled_python_root}/bin" + NO_DEFAULT_PATH + ) + find_path(_bundled_python_include_dir + NAMES Python.h + PATHS + "${_bundled_python_root}/include/python${_bundled_python_version_short}" + "${_bundled_python_root}/include" + NO_DEFAULT_PATH + ) + find_library(_bundled_python_library + NAMES python${_bundled_python_version_short} libpython${_bundled_python_version_short} + PATHS "${_bundled_python_root}/lib" + NO_DEFAULT_PATH + ) + + if(NOT _bundled_python_executable) + message(FATAL_ERROR "Bundled Python executable not found under ${_bundled_python_root}/bin") + endif() + if(NOT _bundled_python_include_dir) + message(FATAL_ERROR "Bundled Python headers not found under ${_bundled_python_root}/include") + endif() + if(NOT _bundled_python_library) + message(FATAL_ERROR "Bundled Python library not found under ${_bundled_python_root}/lib") + endif() + + set(Python3_EXECUTABLE "${_bundled_python_executable}" CACHE FILEPATH "Bundled Python executable" FORCE) + set(Python3_INCLUDE_DIR "${_bundled_python_include_dir}" CACHE PATH "Bundled Python include directory" FORCE) + set(Python3_LIBRARY "${_bundled_python_library}" CACHE FILEPATH "Bundled Python embed library" FORCE) +endif() + +find_package(Python3 ${_bundled_python_version} EXACT REQUIRED + COMPONENTS Interpreter Development.Embed +) + +# Provide a minimal pybind11::embed target sourced from deps_src/pybind11 headers. +set(PYBIND11_SOURCE_DIR "${CMAKE_SOURCE_DIR}/deps_src/pybind11") +if(NOT EXISTS "${PYBIND11_SOURCE_DIR}/include/pybind11/pybind11.h") + message(FATAL_ERROR "pybind11 headers not found in ${PYBIND11_SOURCE_DIR}. Did you initialize submodules?") +endif() + +add_library(pybind11_headers INTERFACE) +target_include_directories(pybind11_headers INTERFACE "${PYBIND11_SOURCE_DIR}/include") +add_library(pybind11::headers ALIAS pybind11_headers) +add_library(pybind11::pybind11 ALIAS pybind11_headers) + +add_library(pybind11_embed INTERFACE) +target_link_libraries(pybind11_embed INTERFACE pybind11_headers Python3::Python) +target_compile_definitions(pybind11_embed INTERFACE PYBIND11_SIMPLE_GIL_MANAGEMENT) +add_library(pybind11::embed ALIAS pybind11_embed) + if(SLIC3R_STATIC) set(OPENVDB_USE_STATIC_LIBS ON) @@ -899,10 +1104,12 @@ if (NOT WIN32 AND NOT APPLE) configure_file(${LIBDIR}/dev-utils/platform/unix/build_appimage.sh.in ${CMAKE_CURRENT_BINARY_DIR}/build_appimage.sh USE_SOURCE_PERMISSIONS @ONLY) endif() - # Resources install target, configure fhs.hpp on UNIX if (WIN32) install(DIRECTORY "${SLIC3R_RESOURCES_DIR}/" DESTINATION "./resources") + if(ORCA_BUNDLED_UV_EXECUTABLE) + install(PROGRAMS "${ORCA_BUNDLED_UV_EXECUTABLE}" DESTINATION "./resources/tools/uv" RENAME "${ORCA_BUNDLED_UV_FILENAME}") + endif() set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE) include(InstallRequiredSystemLibraries) install (PROGRAMS ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS} DESTINATION ".") @@ -912,6 +1119,9 @@ elseif (SLIC3R_FHS) install(DIRECTORY ${SLIC3R_RESOURCES_DIR}/ DESTINATION ${SLIC3R_FHS_RESOURCES} PATTERN "*/udev" EXCLUDE ) + if(ORCA_BUNDLED_UV_EXECUTABLE) + install(PROGRAMS "${ORCA_BUNDLED_UV_EXECUTABLE}" DESTINATION "${SLIC3R_FHS_RESOURCES}/tools/uv" RENAME "${ORCA_BUNDLED_UV_FILENAME}") + endif() install(FILES src/dev-utils/platform/unix/com.orcaslicer.OrcaSlicer.desktop DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications) foreach(SIZE 32 128 192) install(FILES ${SLIC3R_RESOURCES_DIR}/images/OrcaSlicer_${SIZE}px.png @@ -920,9 +1130,15 @@ elseif (SLIC3R_FHS) endforeach() elseif (CMAKE_MACOSX_BUNDLE) # install(DIRECTORY "${SLIC3R_RESOURCES_DIR}/" DESTINATION "${CMAKE_INSTALL_PREFIX}/OrcaSlicer.app/Contents/resources") + if(ORCA_BUNDLED_UV_EXECUTABLE) + install(PROGRAMS "${ORCA_BUNDLED_UV_EXECUTABLE}" DESTINATION "${CMAKE_INSTALL_PREFIX}/OrcaSlicer.app/Contents/Resources/tools/uv" RENAME "${ORCA_BUNDLED_UV_FILENAME}") + endif() else () install(FILES src/dev-utils/platform/unix/com.orcaslicer.OrcaSlicer.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/resources/applications) install(DIRECTORY "${SLIC3R_RESOURCES_DIR}/" DESTINATION "${CMAKE_INSTALL_PREFIX}/resources") + if(ORCA_BUNDLED_UV_EXECUTABLE) + install(PROGRAMS "${ORCA_BUNDLED_UV_EXECUTABLE}" DESTINATION "${CMAKE_INSTALL_PREFIX}/resources/tools/uv" RENAME "${ORCA_BUNDLED_UV_FILENAME}") + endif() endif () install(FILES ${CMAKE_SOURCE_DIR}/LICENSE.txt DESTINATION ".") diff --git a/build_release_macos.sh b/build_release_macos.sh index 8d417695ad..1cded914de 100755 --- a/build_release_macos.sh +++ b/build_release_macos.sh @@ -179,6 +179,45 @@ function pack_deps() { ) } +# --- Bundled Python runtime verification -------------------------------------- +# Relocation is handled at the source: deps/python3/python3.cmake stamps +# libpython with an @rpath id and src/CMakeLists.txt gives the app a matching +# rpath. This gate catches regressions that would otherwise only surface as +# launch failures on end users' machines (the absolute deps path still exists +# on the build host, so a plain run can pass while relocation is broken -- +# hence the otool checks). The x86_64 leg runs under Rosetta on arm64 hosts. +function verify_python_runtime() { + local app="$1" + local pydir="$app/Contents/MacOS/python" + [ -d "$pydir" ] || return 0 # app doesn't bundle Python (e.g. profile validator) + # Version-agnostic interpreter name so a CPython version bump cannot + # silently skip the gate; if the dir exists the interpreter must too. + local pybin="$pydir/bin/python3" + if [ ! -x "$pybin" ]; then + echo "ERROR: bundled python/ present but no interpreter at $pybin" >&2 + exit 1 + fi + echo " Verifying bundled Python runtime in $(basename "$app")..." + local bad + bad=$(otool -arch all -L "$pybin" "$app/Contents/MacOS/OrcaSlicer" | grep "libpython" | grep -v "@rpath/" || true) + if [ -n "$bad" ]; then + echo "ERROR: a bundled binary references libpython by absolute path (relocation regression):" >&2 + echo "$bad" >&2 + exit 1 + fi + # otool -L shows load commands only; assert the consumer rpath separately. + # Its loss is masked on the build host by CMake's absolute build-tree rpath. + if ! otool -arch all -l "$app/Contents/MacOS/OrcaSlicer" | grep -q "path @executable_path/python/lib "; then + echo "ERROR: OrcaSlicer lacks the @executable_path/python/lib rpath (relocation regression)" >&2 + exit 1 + fi + if ! "$pybin" -c "import ssl"; then + echo "ERROR: bundled Python failed to start (libpython relocation broken," >&2 + echo " or missing Rosetta 2 for the x86_64 leg?)" >&2 + exit 1 + fi +} + function build_slicer() { # iterate over two architectures: x86_64 and arm64 for _ARCH in x86_64 arm64; do @@ -239,6 +278,8 @@ function build_slicer() { cp -R "$resources_path" ./OrcaSlicer.app/Contents/Resources # delete .DS_Store file find ./OrcaSlicer.app/ -name '.DS_Store' -delete + + verify_python_runtime ./OrcaSlicer.app # Copy OrcaSlicer_profile_validator.app if it exists if [ -f "../src$BUILD_DIR_CONFIG_SUBDIR/OrcaSlicer_profile_validator.app/Contents/MacOS/OrcaSlicer_profile_validator" ]; then @@ -247,6 +288,7 @@ function build_slicer() { cp -pR "../src$BUILD_DIR_CONFIG_SUBDIR/OrcaSlicer_profile_validator.app" ./OrcaSlicer_profile_validator.app # delete .DS_Store file find ./OrcaSlicer_profile_validator.app/ -name '.DS_Store' -delete + verify_python_runtime ./OrcaSlicer_profile_validator.app fi ) @@ -302,6 +344,7 @@ function build_universal() { echo "Creating universal binaries for OrcaSlicer.app..." lipo_dir "$UNIVERSAL_APP" "$X86_64_APP" echo "Universal OrcaSlicer.app created at $UNIVERSAL_APP" + verify_python_runtime "$UNIVERSAL_APP" # Create universal binary for profile validator if it exists ARM64_VALIDATOR="$PROJECT_DIR/build/arm64/OrcaSlicer/OrcaSlicer_profile_validator.app" diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index 26a1860e33..9720cebcc9 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -422,6 +422,7 @@ endif () include(OCCT/OCCT.cmake) include(OpenCV/OpenCV.cmake) +include(python3/python3.cmake) set(_dep_list dep_Boost @@ -444,6 +445,7 @@ set(_dep_list ${ZLIB_PKG} ${EXPAT_PKG} dep_libnoise + dep_python3 ) if (MSVC) diff --git a/deps/python3/python3.cmake b/deps/python3/python3.cmake new file mode 100644 index 0000000000..0b8e26d9cc --- /dev/null +++ b/deps/python3/python3.cmake @@ -0,0 +1,238 @@ + +include(ProcessorCount) +ProcessorCount(NPROC) + +set(_python_version "3.12.3") +string(REGEX REPLACE "^([0-9]+\\.[0-9]+)\\..*" "\\1" _python_version_short "${_python_version}") +set(_python_url "https://www.python.org/ftp/python/${_python_version}/Python-${_python_version}.tar.xz") +set(_python_sha256 "56bfef1fdfc1221ce6720e43a661e3eb41785dd914ce99698d8c7896af4bdaa1") + +if(WIN32) + if(MSVC_VERSION EQUAL 1800) + set(_python_platform_toolset v120) + elseif(MSVC_VERSION EQUAL 1900) + set(_python_platform_toolset v140) + elseif(MSVC_VERSION LESS 1920) + set(_python_platform_toolset v141) + elseif(MSVC_VERSION LESS 1930) + set(_python_platform_toolset v142) + elseif(MSVC_VERSION LESS 1950) + set(_python_platform_toolset v143) + elseif(MSVC_VERSION LESS 1960) + set(_python_platform_toolset v145) + else() + message(FATAL_ERROR "Unsupported MSVC version for CPython build: ${MSVC_VERSION}") + endif() + + if(CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64|aarch64") + set(_python_pcbuild_platform ARM64) + set(_python_layout_arch arm64) + set(_python_pcbuild_output_dir arm64) + elseif(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(_python_pcbuild_platform x64) + set(_python_layout_arch amd64) + set(_python_pcbuild_output_dir amd64) + else() + set(_python_pcbuild_platform Win32) + set(_python_layout_arch win32) + set(_python_pcbuild_output_dir win32) + endif() + + set(_python_pcbuild_config Release) + set(_python_layout_debug OFF) + if(DEFINED DEP_DEBUG AND DEP_DEBUG) + set(_python_pcbuild_config Debug) + set(_python_layout_debug ON) + endif() + + set(_conf_cmd + cmd /c "echo /p:PlatformToolset=${_python_platform_toolset}>PCbuild\\msbuild.rsp" + ) + set(_build_cmd + ${CMAKE_COMMAND} -E env "GIT_CEILING_DIRECTORIES=/.." + cmd /c PCbuild\\build.bat + -p ${_python_pcbuild_platform} + -c ${_python_pcbuild_config} + --no-tkinter + ) + set(_install_cmd + ${CMAKE_COMMAND} + -DPYTHON_SOURCE_DIR= + -DPYTHON_BUILD_DIR=/PCbuild/${_python_pcbuild_output_dir} + -DPYTHON_DEST_DIR=${DESTDIR}/libpython + -DPYTHON_LAYOUT_ARCH=${_python_layout_arch} + -DPYTHON_DEBUG=${_python_layout_debug} + -P ${CMAKE_CURRENT_LIST_DIR}/stage_windows.cmake + ) +elseif(APPLE) + # macOS configuration + if(CMAKE_OSX_ARCHITECTURES) + set(_python_target_arch "${CMAKE_OSX_ARCHITECTURES}") + else() + set(_python_target_arch "${CMAKE_SYSTEM_PROCESSOR}") + endif() + + if(CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64|arm64|aarch64") + set(_python_build_arch aarch64) + set(_python_build_arch_flag "arm64") + elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64|amd64") + set(_python_build_arch x86_64) + set(_python_build_arch_flag "x86_64") + else() + set(_python_build_arch "${CMAKE_SYSTEM_PROCESSOR}") + set(_python_build_arch_flag "${CMAKE_SYSTEM_PROCESSOR}") + endif() + + if(_python_target_arch MATCHES "ARM64|arm64|aarch64") + set(_python_host_arch aarch64) + set(_python_arch_flag "arm64") + elseif(_python_target_arch MATCHES "x86_64|AMD64|amd64") + set(_python_host_arch x86_64) + set(_python_arch_flag "x86_64") + else() + message(FATAL_ERROR "Unsupported macOS Python target architecture: ${_python_target_arch}") + endif() + + set(_python_arch_flags "-arch ${_python_arch_flag} -mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}") + # No -rpath: all other deps are static, so libpython has no shared + # dependencies to find there. headerpad reserves load-command space for + # the post-install -add_rpath below. + set(_python_ldflags "${_python_arch_flags} -Wl,-headerpad_max_install_names") + + if(IS_CROSS_COMPILE) + set(_python_build_tgt --build=${_python_build_arch}-apple-darwin --host=${_python_host_arch}-apple-darwin) + set(_python_build_arch_flags "-arch ${_python_build_arch_flag} -mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}") + set(_python_build_ldflags "${_python_build_arch_flags} -Wl,-rpath,${DESTDIR}/lib") + set(_python_build_python_dir "/build-python-host") + set(_python_build_python "${_python_build_python_dir}/python") + set(_conf_cmd + /bin/sh -c + "rm -rf '${_python_build_python_dir}' && \ + mkdir -p '${_python_build_python_dir}' && \ + cd '${_python_build_python_dir}' && \ + env \ + CC='${CMAKE_C_COMPILER}' \ + CXX='${CMAKE_CXX_COMPILER}' \ + CFLAGS='${_python_build_arch_flags}' \ + CXXFLAGS='${_python_build_arch_flags}' \ + LDFLAGS='${_python_build_ldflags}' \ + MACOSX_DEPLOYMENT_TARGET='${CMAKE_OSX_DEPLOYMENT_TARGET}' \ + ../configure \ + --prefix='${_python_build_python_dir}/install' \ + --enable-shared \ + --without-static-libpython \ + --disable-test-modules \ + --build=${_python_build_arch}-apple-darwin && \ + make -j${NPROC} python && \ + cd '' && \ + env \ + CC='${CMAKE_C_COMPILER}' \ + CXX='${CMAKE_CXX_COMPILER}' \ + CFLAGS='${_python_arch_flags}' \ + CXXFLAGS='${_python_arch_flags}' \ + LDFLAGS='${_python_ldflags}' \ + MACOSX_DEPLOYMENT_TARGET='${CMAKE_OSX_DEPLOYMENT_TARGET}' \ + ./configure \ + --prefix='${DESTDIR}/libpython' \ + --enable-shared \ + --enable-optimizations \ + --without-static-libpython \ + --with-openssl='${DESTDIR}' \ + --disable-test-modules \ + ${_python_build_tgt} \ + --with-build-python='${_python_build_python}' \ + py_cv_module__tkinter=n/a" + ) + else() + set(_python_build_tgt --build=${_python_host_arch}-apple-darwin) + set(_conf_cmd + env + "CC=${CMAKE_C_COMPILER}" + "CXX=${CMAKE_CXX_COMPILER}" + "CFLAGS=${_python_arch_flags}" + "CXXFLAGS=${_python_arch_flags}" + "LDFLAGS=${_python_ldflags}" + "MACOSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}" + ./configure + --prefix=${DESTDIR}/libpython + --enable-shared + --enable-optimizations + --without-static-libpython + --with-openssl=${DESTDIR} + --disable-test-modules + ${_python_build_tgt} + # Tcl/Tk 9.0 (e.g. from Homebrew) is incompatible with CPython 3.12's + # _tkinter; OrcaSlicer's embedded Python does not need tkinter anyway. + py_cv_module__tkinter=n/a + ) + endif() + set(_build_cmd make -j${NPROC}) + + # CPython stamps libpython with an absolute install name ($prefix/lib/...), + # which every consumer inherits at link time and which only exists on the + # build host. Normalize once here, before anything links against the dep: + # give the dylib an @rpath id and teach the interpreter to find it relative + # to itself. Consumers then just need an rpath entry (src/CMakeLists.txt). + # install_name_tool invalidates code signatures, so re-sign ad-hoc; CI + # re-signs the whole bundle with the real identity later. + # ld collapses '//' in -install_name (but not in -rpath) strings, while + # ${DESTDIR} ends with a slash -- collapse slashes so -change matches the + # recorded install name. + string(REGEX REPLACE "/+" "/" _python_prefix "${DESTDIR}/libpython") + set(_python_dylib "${_python_prefix}/lib/libpython${_python_version_short}.dylib") + set(_python_bin "${_python_prefix}/bin/python${_python_version_short}") + set(_install_cmd make install + COMMAND install_name_tool -id "@rpath/libpython${_python_version_short}.dylib" "${_python_dylib}" + COMMAND install_name_tool -change "${_python_dylib}" "@rpath/libpython${_python_version_short}.dylib" "${_python_bin}" + COMMAND install_name_tool -add_rpath "@loader_path/../lib" "${_python_bin}" + COMMAND codesign --force --sign - "${_python_dylib}" + COMMAND codesign --force --sign - "${_python_bin}" + ) +else() + # Linux/Unix + # Kept verbatim, no slash normalization (unlike the macOS branch's + # collapsed copy): the LDFLAGS rpath below is recorded byte-for-byte in + # the ELF, and the OLD_RPATH handed to relocate_linux.cmake must match it + # exactly -- both derive from this one variable to make that structural. + set(_python_prefix "${DESTDIR}/libpython") + # The rpath points at libpython's real install dir, so the interpreter runs + # in-tree pre-relocation -- and, critically, it reserves enough RUNPATH + # bytes for the in-place $ORIGIN rewrite at install time (Flatpak's + # DESTDIR is the short /app) -- see relocate_linux.cmake. + set(_conf_cmd ./configure + --prefix=${_python_prefix} + --enable-shared + --enable-optimizations + --with-openssl=${DESTDIR} + --without-static-libpython + --disable-test-modules + # Tcl/Tk 9.0 is incompatible with CPython 3.12's _tkinter; not needed here. + py_cv_module__tkinter=n/a + LDFLAGS=-Wl,-rpath,${_python_prefix}/lib + ) + set(_build_cmd make -j${NPROC}) + set(_install_cmd make install + COMMAND ${CMAKE_COMMAND} + "-DPYTHON_BIN=${_python_prefix}/bin/python${_python_version_short}" + "-DOLD_RPATH=${_python_prefix}/lib" + -P "${CMAKE_CURRENT_LIST_DIR}/relocate_linux.cmake" + ) +endif() + +ExternalProject_Add(dep_python3 + URL "${_python_url}" + URL_HASH SHA256=${_python_sha256} + DOWNLOAD_DIR ${DEP_DOWNLOAD_DIR}/python3 + BUILD_IN_SOURCE ON + CONFIGURE_COMMAND ${_conf_cmd} + BUILD_COMMAND ${_build_cmd} + INSTALL_COMMAND ${_install_cmd} +) + +# Python depends on OpenSSL and ZLIB +if(TARGET dep_OpenSSL) + add_dependencies(dep_python3 dep_OpenSSL) +endif() +if(TARGET dep_ZLIB) + add_dependencies(dep_python3 dep_ZLIB) +endif() diff --git a/deps/python3/relocate_linux.cmake b/deps/python3/relocate_linux.cmake new file mode 100644 index 0000000000..7c6acdceed --- /dev/null +++ b/deps/python3/relocate_linux.cmake @@ -0,0 +1,10 @@ +# Repoint the installed interpreter's RUNPATH from the absolute deps dir to a +# self-relative entry so the bundled runtime is relocatable (the deps tree, +# Flatpak /app/libpython, and AppImage $APPDIR/lib/python all keep bin/ and +# lib/ as siblings). $ORIGIN is expanded by the dynamic loader; CMake leaves +# it alone (only ${...} is expanded here). RPATH_CHANGE edits the ELF in +# place, so the new entry must not be longer than the old one: the reserved +# ${DESTDIR}/libpython/lib is at least 18 bytes even for the shortest +# supported DESTDIR (Flatpak's /app), longer than the 14-byte $ORIGIN/../lib. +# Invoked from python3.cmake with -DPYTHON_BIN=... -DOLD_RPATH=... +file(RPATH_CHANGE FILE "${PYTHON_BIN}" OLD_RPATH "${OLD_RPATH}" NEW_RPATH "$ORIGIN/../lib") diff --git a/deps/python3/stage_windows.cmake b/deps/python3/stage_windows.cmake new file mode 100644 index 0000000000..b127f06b68 --- /dev/null +++ b/deps/python3/stage_windows.cmake @@ -0,0 +1,72 @@ +cmake_minimum_required(VERSION 3.13) + +set(_python_abi "312") + +foreach(_var PYTHON_SOURCE_DIR PYTHON_BUILD_DIR PYTHON_DEST_DIR PYTHON_LAYOUT_ARCH) + if(NOT DEFINED ${_var} OR "${${_var}}" STREQUAL "") + message(FATAL_ERROR "${_var} is required") + endif() +endforeach() + +set(_python_exe "${PYTHON_BUILD_DIR}/python.exe") +if(PYTHON_DEBUG) + set(_python_exe "${PYTHON_BUILD_DIR}/python_d.exe") +endif() + +if(NOT EXISTS "${_python_exe}") + message(FATAL_ERROR "Built Python executable not found: ${_python_exe}") +endif() + +file(REMOVE_RECURSE "${PYTHON_DEST_DIR}") +file(MAKE_DIRECTORY "${PYTHON_DEST_DIR}") + +# CPython's Windows layout helper reads LICENSE.txt from the build output. +# Source archives ship this file as LICENSE, so provide the expected name. +if(EXISTS "${PYTHON_SOURCE_DIR}/LICENSE" AND NOT EXISTS "${PYTHON_BUILD_DIR}/LICENSE.txt") + configure_file("${PYTHON_SOURCE_DIR}/LICENSE" "${PYTHON_BUILD_DIR}/LICENSE.txt" COPYONLY) +endif() + +execute_process( + COMMAND + "${CMAKE_COMMAND}" -E env + "PYTHONHOME=" + "PYTHONPATH=${PYTHON_SOURCE_DIR}/Lib" + "${_python_exe}" + "${PYTHON_SOURCE_DIR}/PC/layout" + --source "${PYTHON_SOURCE_DIR}" + --build "${PYTHON_BUILD_DIR}" + --arch "${PYTHON_LAYOUT_ARCH}" + --copy "${PYTHON_DEST_DIR}" + --include-dev + WORKING_DIRECTORY "${PYTHON_SOURCE_DIR}" + RESULT_VARIABLE _layout_result +) + +if(NOT _layout_result EQUAL 0) + message(FATAL_ERROR "CPython Windows layout staging failed with exit code ${_layout_result}") +endif() + +set(_required_files + "${PYTHON_DEST_DIR}/Lib/encodings/__init__.py" + "${PYTHON_DEST_DIR}/include/Python.h" +) + +if(PYTHON_DEBUG) + list(APPEND _required_files + "${PYTHON_DEST_DIR}/python_d.exe" + "${PYTHON_DEST_DIR}/python${_python_abi}_d.dll" + "${PYTHON_DEST_DIR}/libs/python${_python_abi}_d.lib" + ) +else() + list(APPEND _required_files + "${PYTHON_DEST_DIR}/python.exe" + "${PYTHON_DEST_DIR}/python${_python_abi}.dll" + "${PYTHON_DEST_DIR}/libs/python${_python_abi}.lib" + ) +endif() + +foreach(_required_file IN LISTS _required_files) + if(NOT EXISTS "${_required_file}") + message(FATAL_ERROR "Staged Python file missing: ${_required_file}") + endif() +endforeach() diff --git a/deps_src/pybind11/LICENSE b/deps_src/pybind11/LICENSE new file mode 100644 index 0000000000..e466b0dfda --- /dev/null +++ b/deps_src/pybind11/LICENSE @@ -0,0 +1,29 @@ +Copyright (c) 2016 Wenzel Jakob , All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Please also refer to the file .github/CONTRIBUTING.md, which clarifies licensing of +external contributions to this project including patches, pull requests, etc. diff --git a/deps_src/pybind11/README.rst b/deps_src/pybind11/README.rst new file mode 100644 index 0000000000..f30fd61838 --- /dev/null +++ b/deps_src/pybind11/README.rst @@ -0,0 +1,216 @@ +.. figure:: https://github.com/pybind/pybind11/raw/master/docs/pybind11-logo.png + :alt: pybind11 logo + +**pybind11 (v3) — Seamless interoperability between C++ and Python** + +|Latest Documentation Status| |Stable Documentation Status| |Gitter chat| |GitHub Discussions| + +|CI| |Build status| |SPEC 4 — Using and Creating Nightly Wheels| + +|Repology| |PyPI package| |Conda-forge| |Python Versions| + +`Setuptools example `_ +• `Scikit-build example `_ +• `CMake example `_ + +.. start + + +**pybind11** is a lightweight header-only library that exposes C++ types +in Python and vice versa, mainly to create Python bindings of existing +C++ code. Its goals and syntax are similar to the excellent +`Boost.Python `_ +library by David Abrahams: to minimize boilerplate code in traditional +extension modules by inferring type information using compile-time +introspection. + +The main issue with Boost.Python—and the reason for creating such a +similar project—is Boost. Boost is an enormously large and complex suite +of utility libraries that works with almost every C++ compiler in +existence. This compatibility has its cost: arcane template tricks and +workarounds are necessary to support the oldest and buggiest of compiler +specimens. Now that C++11-compatible compilers are widely available, +this heavy machinery has become an excessively large and unnecessary +dependency. + +Think of this library as a tiny self-contained version of Boost.Python +with everything stripped away that isn't relevant for binding +generation. Without comments, the core header files only require ~4K +lines of code and depend on Python (CPython 3.8+, PyPy, or GraalPy) and the C++ +standard library. This compact implementation was possible thanks to some C++11 +language features (specifically: tuples, lambda functions and variadic +templates). Since its creation, this library has grown beyond Boost.Python in +many ways, leading to dramatically simpler binding code in many common +situations. + +Tutorial and reference documentation is provided at +`pybind11.readthedocs.io `_. +A PDF version of the manual is available +`here `_. +And the source code is always available at +`github.com/pybind/pybind11 `_. + + +Core features +------------- + + +pybind11 can map the following core C++ features to Python: + +- Functions accepting and returning custom data structures per value, + reference, or pointer +- Instance methods and static methods +- Overloaded functions +- Instance attributes and static attributes +- Arbitrary exception types +- Enumerations +- Callbacks +- Iterators and ranges +- Custom operators +- Single and multiple inheritance +- STL data structures +- Smart pointers with reference counting like ``std::shared_ptr`` +- Internal references with correct reference counting +- C++ classes with virtual (and pure virtual) methods can be extended + in Python +- Integrated NumPy support (NumPy 2 requires pybind11 2.12+) + +Goodies +------- + +In addition to the core functionality, pybind11 provides some extra +goodies: + +- CPython 3.8+, PyPy3 7.3.17+, and GraalPy 24.1+ are supported with an + implementation-agnostic interface (see older versions for older CPython + and PyPy versions). + +- It is possible to bind C++11 lambda functions with captured + variables. The lambda capture data is stored inside the resulting + Python function object. + +- pybind11 uses C++11 move constructors and move assignment operators + whenever possible to efficiently transfer custom data types. + +- It's easy to expose the internal storage of custom data types through + Pythons' buffer protocols. This is handy e.g. for fast conversion + between C++ matrix classes like Eigen and NumPy without expensive + copy operations. + +- pybind11 can automatically vectorize functions so that they are + transparently applied to all entries of one or more NumPy array + arguments. + +- Python's slice-based access and assignment operations can be + supported with just a few lines of code. + +- Everything is contained in just a few header files; there is no need + to link against any additional libraries. + +- Binaries are generally smaller by a factor of at least 2 compared to + equivalent bindings generated by Boost.Python. A recent pybind11 + conversion of PyRosetta, an enormous Boost.Python binding project, + `reported `_ + a binary size reduction of **5.4x** and compile time reduction by + **5.8x**. + +- Function signatures are precomputed at compile time (using + ``constexpr``), leading to smaller binaries. + +- With little extra effort, C++ types can be pickled and unpickled + similar to regular Python objects. + +Supported compilers +------------------- + +1. Clang/LLVM 3.3 or newer (for Apple Xcode's clang, this is 5.0.0 or + newer) +2. GCC 4.8 or newer +3. Microsoft Visual Studio 2022 or newer (2019 probably works, but was dropped in CI) +4. Intel classic C++ compiler 18 or newer (ICC 20.2 tested in CI) +5. Cygwin/GCC (previously tested on 2.5.1) +6. NVCC (CUDA 11.0 tested in CI) +7. NVIDIA PGI (20.9 tested in CI) + +Supported Platforms +------------------- + +* Windows, Linux, macOS, and iOS +* CPython 3.8+, Pyodide, PyPy, and GraalPy +* C++11, C++14, C++17, C++20, and C++23 + +About +----- + +This project was created by `Wenzel +Jakob `_. Significant features and/or +improvements to the code were contributed by +Jonas Adler, +Lori A. Burns, +Sylvain Corlay, +Eric Cousineau, +Aaron Gokaslan, +Ralf Grosse-Kunstleve, +Trent Houliston, +Axel Huebl, +@hulucc, +Yannick Jadoul, +Sergey Lyskov, +Johan Mabille, +Tomasz Miąsko, +Dean Moldovan, +Ben Pritchard, +Jason Rhinelander, +Boris Schäling, +Pim Schellart, +Henry Schreiner, +Ivan Smirnov, +Dustin Spicuzza, +Boris Staletic, +Ethan Steinberg, +Patrick Stewart, +Ivor Wanders, +and +Xiaofei Wang. + +We thank Google for a generous financial contribution to the continuous +integration infrastructure used by this project. + + +Contributing +~~~~~~~~~~~~ + +See the `contributing +guide `_ +for information on building and contributing to pybind11. + +License +~~~~~~~ + +pybind11 is provided under a BSD-style license that can be found in the +`LICENSE `_ +file. By using, distributing, or contributing to this project, you agree +to the terms and conditions of this license. + +.. |Latest Documentation Status| image:: https://readthedocs.org/projects/pybind11/badge?version=latest + :target: http://pybind11.readthedocs.org/en/latest +.. |Stable Documentation Status| image:: https://img.shields.io/badge/docs-stable-blue.svg + :target: http://pybind11.readthedocs.org/en/stable +.. |Gitter chat| image:: https://img.shields.io/gitter/room/gitterHQ/gitter.svg + :target: https://gitter.im/pybind/Lobby +.. |CI| image:: https://github.com/pybind/pybind11/workflows/CI/badge.svg + :target: https://github.com/pybind/pybind11/actions +.. |Build status| image:: https://ci.appveyor.com/api/projects/status/riaj54pn4h08xy40?svg=true + :target: https://ci.appveyor.com/project/wjakob/pybind11 +.. |PyPI package| image:: https://img.shields.io/pypi/v/pybind11.svg + :target: https://pypi.org/project/pybind11/ +.. |Conda-forge| image:: https://img.shields.io/conda/vn/conda-forge/pybind11.svg + :target: https://github.com/conda-forge/pybind11-feedstock +.. |Repology| image:: https://repology.org/badge/latest-versions/python:pybind11.svg + :target: https://repology.org/project/python:pybind11/versions +.. |Python Versions| image:: https://img.shields.io/pypi/pyversions/pybind11.svg + :target: https://pypi.org/project/pybind11/ +.. |GitHub Discussions| image:: https://img.shields.io/static/v1?label=Discussions&message=Ask&color=blue&logo=github + :target: https://github.com/pybind/pybind11/discussions +.. |SPEC 4 — Using and Creating Nightly Wheels| image:: https://img.shields.io/badge/SPEC-4-green?labelColor=%23004811&color=%235CA038 + :target: https://scientific-python.org/specs/spec-0004/ diff --git a/deps_src/pybind11/include/pybind11/attr.h b/deps_src/pybind11/include/pybind11/attr.h new file mode 100644 index 0000000000..9b631fa48d --- /dev/null +++ b/deps_src/pybind11/include/pybind11/attr.h @@ -0,0 +1,722 @@ +/* + pybind11/attr.h: Infrastructure for processing custom + type and function attributes + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "detail/common.h" +#include "cast.h" +#include "trampoline_self_life_support.h" + +#include + +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +/// \addtogroup annotations +/// @{ + +/// Annotation for methods +struct is_method { + handle class_; + explicit is_method(const handle &c) : class_(c) {} +}; + +/// Annotation for setters +struct is_setter {}; + +/// Annotation for operators +struct is_operator {}; + +/// Annotation for classes that cannot be subclassed +struct is_final {}; + +/// Annotation for parent scope +struct scope { + handle value; + explicit scope(const handle &s) : value(s) {} +}; + +/// Annotation for documentation +struct doc { + const char *value; + explicit doc(const char *value) : value(value) {} +}; + +/// Annotation for function names +struct name { + const char *value; + explicit name(const char *value) : value(value) {} +}; + +/// Annotation indicating that a function is an overload associated with a given "sibling" +struct sibling { + handle value; + explicit sibling(const handle &value) : value(value.ptr()) {} +}; + +/// Annotation indicating that a class derives from another given type +template +struct base { + + PYBIND11_DEPRECATED( + "base() was deprecated in favor of specifying 'T' as a template argument to class_") + base() = default; +}; + +/// Keep patient alive while nurse lives +template +struct keep_alive {}; + +/// Annotation indicating that a class is involved in a multiple inheritance relationship +struct multiple_inheritance {}; + +/// Annotation which enables dynamic attributes, i.e. adds `__dict__` to a class +struct dynamic_attr {}; + +/// Annotation which enables the buffer protocol for a type +struct buffer_protocol {}; + +/// Annotation which enables releasing the GIL before calling the C++ destructor of wrapped +/// instances (pybind/pybind11#1446). +struct release_gil_before_calling_cpp_dtor {}; + +/// Annotation which requests that a special metaclass is created for a type +struct metaclass { + handle value; + + PYBIND11_DEPRECATED("py::metaclass() is no longer required. It's turned on by default now.") + metaclass() = default; + + /// Override pybind11's default metaclass + explicit metaclass(handle value) : value(value) {} +}; + +/// Specifies a custom callback with signature `void (PyHeapTypeObject*)` that +/// may be used to customize the Python type. +/// +/// The callback is invoked immediately before `PyType_Ready`. +/// +/// Note: This is an advanced interface, and uses of it may require changes to +/// work with later versions of pybind11. You may wish to consult the +/// implementation of `make_new_python_type` in `detail/classes.h` to understand +/// the context in which the callback will be run. +struct custom_type_setup { + using callback = std::function; + + explicit custom_type_setup(callback value) : value(std::move(value)) {} + + callback value; +}; + +/// Annotation that marks a class as local to the module: +struct module_local { + const bool value; + constexpr explicit module_local(bool v = true) : value(v) {} +}; + +/// Annotation to mark enums as an arithmetic type +struct arithmetic {}; + +/// Mark a function for addition at the beginning of the existing overload chain instead of the end +struct prepend {}; + +/** \rst + A call policy which places one or more guard variables (``Ts...``) around the function call. + + For example, this definition: + + .. code-block:: cpp + + m.def("foo", foo, py::call_guard()); + + is equivalent to the following pseudocode: + + .. code-block:: cpp + + m.def("foo", [](args...) { + T scope_guard; + return foo(args...); // forwarded arguments + }); + \endrst */ +template +struct call_guard; + +template <> +struct call_guard<> { + using type = detail::void_type; +}; + +template +struct call_guard { + static_assert(std::is_default_constructible::value, + "The guard type must be default constructible"); + + using type = T; +}; + +template +struct call_guard { + struct type { + T guard{}; // Compose multiple guard types with left-to-right default-constructor order + typename call_guard::type next{}; + }; +}; + +/// @} annotations + +PYBIND11_NAMESPACE_BEGIN(detail) +/* Forward declarations */ +enum op_id : int; +enum op_type : int; +struct undefined_t; +template +struct op_; +void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret); + +/// Internal data structure which holds metadata about a keyword argument +struct argument_record { + const char *name; ///< Argument name + const char *descr; ///< Human-readable version of the argument value + handle value; ///< Associated Python object + bool convert : 1; ///< True if the argument is allowed to convert when loading + bool none : 1; ///< True if None is allowed when loading + + argument_record(const char *name, const char *descr, handle value, bool convert, bool none) + : name(name), descr(descr), value(value), convert(convert), none(none) {} +}; + +/// Internal data structure which holds metadata about a bound function (signature, overloads, +/// etc.) +#define PYBIND11_DETAIL_FUNCTION_RECORD_ABI_ID "v1" // PLEASE UPDATE if the struct is changed. +struct function_record { + function_record() + : is_constructor(false), is_new_style_constructor(false), is_stateless(false), + is_operator(false), is_method(false), is_setter(false), has_args(false), + has_kwargs(false), prepend(false) {} + + /// Function name + char *name = nullptr; /* why no C++ strings? They generate heavier code.. */ + + // User-specified documentation string + char *doc = nullptr; + + /// Human-readable version of the function signature + char *signature = nullptr; + + /// List of registered keyword arguments + std::vector args; + + /// Pointer to lambda function which converts arguments and performs the actual call + handle (*impl)(function_call &) = nullptr; + + /// Storage for the wrapped function pointer and captured data, if any + void *data[3] = {}; + + /// Pointer to custom destructor for 'data' (if needed) + void (*free_data)(function_record *ptr) = nullptr; + + /// Return value policy associated with this function + return_value_policy policy = return_value_policy::automatic; + + /// True if name == '__init__' + bool is_constructor : 1; + + /// True if this is a new-style `__init__` defined in `detail/init.h` + bool is_new_style_constructor : 1; + + /// True if this is a stateless function pointer + bool is_stateless : 1; + + /// True if this is an operator (__add__), etc. + bool is_operator : 1; + + /// True if this is a method + bool is_method : 1; + + /// True if this is a setter + bool is_setter : 1; + + /// True if the function has a '*args' argument + bool has_args : 1; + + /// True if the function has a '**kwargs' argument + bool has_kwargs : 1; + + /// True if this function is to be inserted at the beginning of the overload resolution chain + bool prepend : 1; + + /// Number of arguments (including py::args and/or py::kwargs, if present) + std::uint16_t nargs; + + /// Number of leading positional arguments, which are terminated by a py::args or py::kwargs + /// argument or by a py::kw_only annotation. + std::uint16_t nargs_pos = 0; + + /// Number of leading arguments (counted in `nargs`) that are positional-only + std::uint16_t nargs_pos_only = 0; + + /// Python method object + PyMethodDef *def = nullptr; + + /// Python handle to the parent scope (a class or a module) + handle scope; + + /// Python handle to the sibling function representing an overload chain + handle sibling; + + /// Pointer to next overload + function_record *next = nullptr; +}; +// The main purpose of this macro is to make it easy to pin-point the critically related code +// sections. +#define PYBIND11_ENSURE_PRECONDITION_FOR_FUNCTIONAL_H_PERFORMANCE_OPTIMIZATIONS(...) \ + static_assert( \ + __VA_ARGS__, \ + "Violation of precondition for pybind11/functional.h performance optimizations!") + +/// Special data structure which (temporarily) holds metadata about a bound class +struct type_record { + PYBIND11_NOINLINE type_record() + : multiple_inheritance(false), dynamic_attr(false), buffer_protocol(false), + module_local(false), is_final(false), release_gil_before_calling_cpp_dtor(false) {} + + /// Handle to the parent scope + handle scope; + + /// Name of the class + const char *name = nullptr; + + // Pointer to RTTI type_info data structure + const std::type_info *type = nullptr; + + /// How large is the underlying C++ type? + size_t type_size = 0; + + /// What is the alignment of the underlying C++ type? + size_t type_align = 0; + + /// How large is the type's holder? + size_t holder_size = 0; + + /// The global operator new can be overridden with a class-specific variant + void *(*operator_new)(size_t) = nullptr; + + /// Function pointer to class_<..>::init_instance + void (*init_instance)(instance *, const void *) = nullptr; + + /// Function pointer to class_<..>::dealloc + void (*dealloc)(detail::value_and_holder &) = nullptr; + + /// Function pointer for casting alias class (aka trampoline) pointer to + /// trampoline_self_life_support pointer. Sidesteps cross-DSO RTTI issues + /// on platforms like macOS (see PR #5728 for details). + get_trampoline_self_life_support_fn get_trampoline_self_life_support + = [](void *) -> trampoline_self_life_support * { return nullptr; }; + + /// List of base classes of the newly created type + list bases; + + /// Optional docstring + const char *doc = nullptr; + + /// Custom metaclass (optional) + handle metaclass; + + /// Custom type setup. + custom_type_setup::callback custom_type_setup_callback; + + /// Multiple inheritance marker + bool multiple_inheritance : 1; + + /// Does the class manage a __dict__? + bool dynamic_attr : 1; + + /// Does the class implement the buffer protocol? + bool buffer_protocol : 1; + + /// Is the class definition local to the module shared object? + bool module_local : 1; + + /// Is the class inheritable from python classes? + bool is_final : 1; + + /// Solves pybind/pybind11#1446 + bool release_gil_before_calling_cpp_dtor : 1; + + holder_enum_t holder_enum_v = holder_enum_t::undefined; + + PYBIND11_NOINLINE void add_base(const std::type_info &base, void *(*caster)(void *) ) { + auto *base_info = detail::get_type_info(base, false); + if (!base_info) { + std::string tname(base.name()); + detail::clean_type_id(tname); + pybind11_fail("generic_type: type \"" + std::string(name) + + "\" referenced unknown base type \"" + tname + "\""); + } + + // SMART_HOLDER_BAKEIN_FOLLOW_ON: Refine holder compatibility checks. + bool this_has_unique_ptr_holder = (holder_enum_v == holder_enum_t::std_unique_ptr); + bool base_has_unique_ptr_holder + = (base_info->holder_enum_v == holder_enum_t::std_unique_ptr); + if (this_has_unique_ptr_holder != base_has_unique_ptr_holder) { + std::string tname(base.name()); + detail::clean_type_id(tname); + pybind11_fail("generic_type: type \"" + std::string(name) + "\" " + + (this_has_unique_ptr_holder ? "does not have" : "has") + + " a non-default holder type while its base \"" + tname + "\" " + + (base_has_unique_ptr_holder ? "does not" : "does")); + } + + bases.append((PyObject *) base_info->type); + +#ifdef PYBIND11_BACKWARD_COMPATIBILITY_TP_DICTOFFSET + dynamic_attr |= base_info->type->tp_dictoffset != 0; +#else + dynamic_attr |= (base_info->type->tp_flags & Py_TPFLAGS_MANAGED_DICT) != 0; +#endif + + if (caster) { + base_info->implicit_casts.emplace_back(type, caster); + } + } +}; + +inline function_call::function_call(const function_record &f, handle p) : func(f), parent(p) { + args.reserve(f.nargs); + args_convert.reserve(f.nargs); +} + +/// Tag for a new-style `__init__` defined in `detail/init.h` +struct is_new_style_constructor {}; + +/** + * Partial template specializations to process custom attributes provided to + * cpp_function_ and class_. These are either used to initialize the respective + * fields in the type_record and function_record data structures or executed at + * runtime to deal with custom call policies (e.g. keep_alive). + */ +template +struct process_attribute; + +template +struct process_attribute_default { + /// Default implementation: do nothing + static void init(const T &, function_record *) {} + static void init(const T &, type_record *) {} + static void precall(function_call &) {} + static void postcall(function_call &, handle) {} +}; + +/// Process an attribute specifying the function's name +template <> +struct process_attribute : process_attribute_default { + static void init(const name &n, function_record *r) { r->name = const_cast(n.value); } +}; + +/// Process an attribute specifying the function's docstring +template <> +struct process_attribute : process_attribute_default { + static void init(const doc &n, function_record *r) { r->doc = const_cast(n.value); } +}; + +/// Process an attribute specifying the function's docstring (provided as a C-style string) +template <> +struct process_attribute : process_attribute_default { + static void init(const char *d, function_record *r) { r->doc = const_cast(d); } + static void init(const char *d, type_record *r) { r->doc = d; } +}; +template <> +struct process_attribute : process_attribute {}; + +/// Process an attribute indicating the function's return value policy +template <> +struct process_attribute : process_attribute_default { + static void init(const return_value_policy &p, function_record *r) { r->policy = p; } +}; + +/// Process an attribute which indicates that this is an overloaded function associated with a +/// given sibling +template <> +struct process_attribute : process_attribute_default { + static void init(const sibling &s, function_record *r) { r->sibling = s.value; } +}; + +/// Process an attribute which indicates that this function is a method +template <> +struct process_attribute : process_attribute_default { + static void init(const is_method &s, function_record *r) { + r->is_method = true; + r->scope = s.class_; + } +}; + +/// Process an attribute which indicates that this function is a setter +template <> +struct process_attribute : process_attribute_default { + static void init(const is_setter &, function_record *r) { r->is_setter = true; } +}; + +/// Process an attribute which indicates the parent scope of a method +template <> +struct process_attribute : process_attribute_default { + static void init(const scope &s, function_record *r) { r->scope = s.value; } +}; + +/// Process an attribute which indicates that this function is an operator +template <> +struct process_attribute : process_attribute_default { + static void init(const is_operator &, function_record *r) { r->is_operator = true; } +}; + +template <> +struct process_attribute + : process_attribute_default { + static void init(const is_new_style_constructor &, function_record *r) { + r->is_new_style_constructor = true; + } +}; + +inline void check_kw_only_arg(const arg &a, function_record *r) { + if (r->args.size() > r->nargs_pos && (!a.name || a.name[0] == '\0')) { + pybind11_fail("arg(): cannot specify an unnamed argument after a kw_only() annotation or " + "args() argument"); + } +} + +inline void append_self_arg_if_needed(function_record *r) { + if (r->is_method && r->args.empty()) { + r->args.emplace_back("self", nullptr, handle(), /*convert=*/true, /*none=*/false); + } +} + +/// Process a keyword argument attribute (*without* a default value) +template <> +struct process_attribute : process_attribute_default { + static void init(const arg &a, function_record *r) { + append_self_arg_if_needed(r); + r->args.emplace_back(a.name, nullptr, handle(), !a.flag_noconvert, a.flag_none); + + check_kw_only_arg(a, r); + } +}; + +/// Process a keyword argument attribute (*with* a default value) +template <> +struct process_attribute : process_attribute_default { + static void init(const arg_v &a, function_record *r) { + if (r->is_method && r->args.empty()) { + r->args.emplace_back( + "self", /*descr=*/nullptr, /*parent=*/handle(), /*convert=*/true, /*none=*/false); + } + + if (!a.value) { +#if defined(PYBIND11_DETAILED_ERROR_MESSAGES) + std::string descr("'"); + if (a.name) { + descr += std::string(a.name) + ": "; + } + descr += a.type + "'"; + if (r->is_method) { + if (r->name) { + descr += " in method '" + (std::string) str(r->scope) + "." + + (std::string) r->name + "'"; + } else { + descr += " in method of '" + (std::string) str(r->scope) + "'"; + } + } else if (r->name) { + descr += " in function '" + (std::string) r->name + "'"; + } + pybind11_fail("arg(): could not convert default argument " + descr + + " into a Python object (type not registered yet?)"); +#else + pybind11_fail("arg(): could not convert default argument " + "into a Python object (type not registered yet?). " + "#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for " + "more information."); +#endif + } + r->args.emplace_back(a.name, a.descr, a.value.inc_ref(), !a.flag_noconvert, a.flag_none); + + check_kw_only_arg(a, r); + } +}; + +/// Process a keyword-only-arguments-follow pseudo argument +template <> +struct process_attribute : process_attribute_default { + static void init(const kw_only &, function_record *r) { + append_self_arg_if_needed(r); + if (r->has_args && r->nargs_pos != static_cast(r->args.size())) { + pybind11_fail("Mismatched args() and kw_only(): they must occur at the same relative " + "argument location (or omit kw_only() entirely)"); + } + r->nargs_pos = static_cast(r->args.size()); + } +}; + +/// Process a positional-only-argument maker +template <> +struct process_attribute : process_attribute_default { + static void init(const pos_only &, function_record *r) { + append_self_arg_if_needed(r); + r->nargs_pos_only = static_cast(r->args.size()); + if (r->nargs_pos_only > r->nargs_pos) { + pybind11_fail("pos_only(): cannot follow a py::args() argument"); + } + // It also can't follow a kw_only, but a static_assert in pybind11.h checks that + } +}; + +/// Process a parent class attribute. Single inheritance only (class_ itself already guarantees +/// that) +template +struct process_attribute::value>> + : process_attribute_default { + static void init(const handle &h, type_record *r) { r->bases.append(h); } +}; + +/// Process a parent class attribute (deprecated, does not support multiple inheritance) +template +struct process_attribute> : process_attribute_default> { + static void init(const base &, type_record *r) { r->add_base(typeid(T), nullptr); } +}; + +/// Process a multiple inheritance attribute +template <> +struct process_attribute : process_attribute_default { + static void init(const multiple_inheritance &, type_record *r) { + r->multiple_inheritance = true; + } +}; + +template <> +struct process_attribute : process_attribute_default { + static void init(const dynamic_attr &, type_record *r) { r->dynamic_attr = true; } +}; + +template <> +struct process_attribute { + static void init(const custom_type_setup &value, type_record *r) { + r->custom_type_setup_callback = value.value; + } +}; + +template <> +struct process_attribute : process_attribute_default { + static void init(const is_final &, type_record *r) { r->is_final = true; } +}; + +template <> +struct process_attribute : process_attribute_default { + static void init(const buffer_protocol &, type_record *r) { r->buffer_protocol = true; } +}; + +template <> +struct process_attribute : process_attribute_default { + static void init(const metaclass &m, type_record *r) { r->metaclass = m.value; } +}; + +template <> +struct process_attribute : process_attribute_default { + static void init(const module_local &l, type_record *r) { r->module_local = l.value; } +}; + +template <> +struct process_attribute + : process_attribute_default { + static void init(const release_gil_before_calling_cpp_dtor &, type_record *r) { + r->release_gil_before_calling_cpp_dtor = true; + } +}; + +/// Process a 'prepend' attribute, putting this at the beginning of the overload chain +template <> +struct process_attribute : process_attribute_default { + static void init(const prepend &, function_record *r) { r->prepend = true; } +}; + +/// Process an 'arithmetic' attribute for enums (does nothing here) +template <> +struct process_attribute : process_attribute_default {}; + +template +struct process_attribute> : process_attribute_default> {}; + +/** + * Process a keep_alive call policy -- invokes keep_alive_impl during the + * pre-call handler if both Nurse, Patient != 0 and use the post-call handler + * otherwise + */ +template +struct process_attribute> + : public process_attribute_default> { + template = 0> + static void precall(function_call &call) { + keep_alive_impl(Nurse, Patient, call, handle()); + } + template = 0> + static void postcall(function_call &, handle) {} + template = 0> + static void precall(function_call &) {} + template = 0> + static void postcall(function_call &call, handle ret) { + keep_alive_impl(Nurse, Patient, call, ret); + } +}; + +/// Recursively iterate over variadic template arguments +template +struct process_attributes { + static void init(const Args &...args, function_record *r) { + PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(r); + PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(r); + using expander = int[]; + (void) expander{ + 0, ((void) process_attribute::type>::init(args, r), 0)...}; + } + static void init(const Args &...args, type_record *r) { + PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(r); + PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(r); + using expander = int[]; + (void) expander{0, + (process_attribute::type>::init(args, r), 0)...}; + } + static void precall(function_call &call) { + PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(call); + using expander = int[]; + (void) expander{0, + (process_attribute::type>::precall(call), 0)...}; + } + static void postcall(function_call &call, handle fn_ret) { + PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(call, fn_ret); + PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(fn_ret); + using expander = int[]; + (void) expander{ + 0, (process_attribute::type>::postcall(call, fn_ret), 0)...}; + } +}; + +template +using is_call_guard = is_instantiation; + +/// Extract the ``type`` from the first `call_guard` in `Extras...` (or `void_type` if none found) +template +using extract_guard_t = typename exactly_one_t, Extra...>::type; + +/// Check the number of named arguments at compile time +template ::value...), + size_t self = constexpr_sum(std::is_same::value...)> +constexpr bool expected_num_args(size_t nargs, bool has_args, bool has_kwargs) { + PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(nargs, has_args, has_kwargs); + return named == 0 || (self + named + size_t(has_args) + size_t(has_kwargs)) == nargs; +} + +PYBIND11_NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/deps_src/pybind11/include/pybind11/buffer_info.h b/deps_src/pybind11/include/pybind11/buffer_info.h new file mode 100644 index 0000000000..75aec0ba30 --- /dev/null +++ b/deps_src/pybind11/include/pybind11/buffer_info.h @@ -0,0 +1,208 @@ +/* + pybind11/buffer_info.h: Python buffer object interface + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "detail/common.h" + +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +PYBIND11_NAMESPACE_BEGIN(detail) + +// Default, C-style strides +inline std::vector c_strides(const std::vector &shape, ssize_t itemsize) { + auto ndim = shape.size(); + std::vector strides(ndim, itemsize); + if (ndim > 0) { + for (size_t i = ndim - 1; i > 0; --i) { + strides[i - 1] = strides[i] * shape[i]; + } + } + return strides; +} + +// F-style strides; default when constructing an array_t with `ExtraFlags & f_style` +inline std::vector f_strides(const std::vector &shape, ssize_t itemsize) { + auto ndim = shape.size(); + std::vector strides(ndim, itemsize); + for (size_t i = 1; i < ndim; ++i) { + strides[i] = strides[i - 1] * shape[i - 1]; + } + return strides; +} + +template +struct compare_buffer_info; + +PYBIND11_NAMESPACE_END(detail) + +/// Information record describing a Python buffer object +struct buffer_info { + void *ptr = nullptr; // Pointer to the underlying storage + ssize_t itemsize = 0; // Size of individual items in bytes + ssize_t size = 0; // Total number of entries + std::string format; // For homogeneous buffers, this should be set to + // format_descriptor::format() + ssize_t ndim = 0; // Number of dimensions + std::vector shape; // Shape of the tensor (1 entry per dimension) + std::vector strides; // Number of bytes between adjacent entries + // (for each per dimension) + bool readonly = false; // flag to indicate if the underlying storage may be written to + + buffer_info() = default; + + buffer_info(void *ptr, + ssize_t itemsize, + const std::string &format, + ssize_t ndim, + detail::any_container shape_in, + detail::any_container strides_in, + bool readonly = false) + : ptr(ptr), itemsize(itemsize), size(1), format(format), ndim(ndim), + shape(std::move(shape_in)), strides(std::move(strides_in)), readonly(readonly) { + if (ndim != (ssize_t) shape.size() || ndim != (ssize_t) strides.size()) { + pybind11_fail("buffer_info: ndim doesn't match shape and/or strides length"); + } + for (size_t i = 0; i < (size_t) ndim; ++i) { + size *= shape[i]; + } + } + + template + buffer_info(T *ptr, + detail::any_container shape_in, + detail::any_container strides_in, + bool readonly = false) + : buffer_info(private_ctr_tag(), + ptr, + sizeof(T), + format_descriptor::format(), + static_cast(shape_in->size()), + std::move(shape_in), + std::move(strides_in), + readonly) {} + + buffer_info(void *ptr, + ssize_t itemsize, + const std::string &format, + ssize_t size, + bool readonly = false) + : buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}, readonly) {} + + template + buffer_info(T *ptr, ssize_t size, bool readonly = false) + : buffer_info(ptr, sizeof(T), format_descriptor::format(), size, readonly) {} + + template + buffer_info(const T *ptr, ssize_t size, bool readonly = true) + : buffer_info( + const_cast(ptr), sizeof(T), format_descriptor::format(), size, readonly) {} + + explicit buffer_info(Py_buffer *view, bool ownview = true) + : buffer_info( + view->buf, + view->itemsize, + view->format, + view->ndim, + {view->shape, view->shape + view->ndim}, + /* Though buffer::request() requests PyBUF_STRIDES, ctypes objects + * ignore this flag and return a view with NULL strides. + * When strides are NULL, build them manually. */ + view->strides + ? std::vector(view->strides, view->strides + view->ndim) + : detail::c_strides({view->shape, view->shape + view->ndim}, view->itemsize), + (view->readonly != 0)) { + // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) + this->m_view = view; + // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) + this->ownview = ownview; + } + + buffer_info(const buffer_info &) = delete; + buffer_info &operator=(const buffer_info &) = delete; + + buffer_info(buffer_info &&other) noexcept { (*this) = std::move(other); } + + buffer_info &operator=(buffer_info &&rhs) noexcept { + ptr = rhs.ptr; + itemsize = rhs.itemsize; + size = rhs.size; + format = std::move(rhs.format); + ndim = rhs.ndim; + shape = std::move(rhs.shape); + strides = std::move(rhs.strides); + std::swap(m_view, rhs.m_view); + std::swap(ownview, rhs.ownview); + readonly = rhs.readonly; + return *this; + } + + ~buffer_info() { + if (m_view && ownview) { + PyBuffer_Release(m_view); + delete m_view; + } + } + + Py_buffer *view() const { return m_view; } + Py_buffer *&view() { return m_view; } + + /* True if the buffer item type is equivalent to `T`. */ + // To define "equivalent" by example: + // `buffer_info::item_type_is_equivalent_to(b)` and + // `buffer_info::item_type_is_equivalent_to(b)` may both be true + // on some platforms, but `int` and `unsigned` will never be equivalent. + // For the ground truth, please inspect `detail::compare_buffer_info<>`. + template + bool item_type_is_equivalent_to() const { + return detail::compare_buffer_info::compare(*this); + } + +private: + struct private_ctr_tag {}; + + buffer_info(private_ctr_tag, + void *ptr, + ssize_t itemsize, + const std::string &format, + ssize_t ndim, + detail::any_container &&shape_in, + detail::any_container &&strides_in, + bool readonly) + : buffer_info( + ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in), readonly) {} + + Py_buffer *m_view = nullptr; + bool ownview = false; +}; + +PYBIND11_NAMESPACE_BEGIN(detail) + +template +struct compare_buffer_info { + static bool compare(const buffer_info &b) { + // NOLINTNEXTLINE(bugprone-sizeof-expression) Needed for `PyObject *` + return b.format == format_descriptor::format() && b.itemsize == (ssize_t) sizeof(T); + } +}; + +template +struct compare_buffer_info::value>> { + static bool compare(const buffer_info &b) { + return (size_t) b.itemsize == sizeof(T) + && (b.format == format_descriptor::value + || ((sizeof(T) == sizeof(long)) + && b.format == (std::is_unsigned::value ? "L" : "l")) + || ((sizeof(T) == sizeof(size_t)) + && b.format == (std::is_unsigned::value ? "N" : "n"))); + } +}; + +PYBIND11_NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/deps_src/pybind11/include/pybind11/cast.h b/deps_src/pybind11/include/pybind11/cast.h new file mode 100644 index 0000000000..c635791fee --- /dev/null +++ b/deps_src/pybind11/include/pybind11/cast.h @@ -0,0 +1,2361 @@ +/* + pybind11/cast.h: Partial template specializations to cast between + C++ and Python types + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "detail/common.h" +#include "detail/descr.h" +#include "detail/native_enum_data.h" +#include "detail/type_caster_base.h" +#include "detail/typeid.h" +#include "pytypes.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +PYBIND11_WARNING_DISABLE_MSVC(4127) + +PYBIND11_NAMESPACE_BEGIN(detail) + +template +class type_caster : public type_caster_base {}; +template +using make_caster = type_caster>; + +// Shortcut for calling a caster's `cast_op_type` cast operator for casting a type_caster to a T +template +typename make_caster::template cast_op_type cast_op(make_caster &caster) { + using result_t = typename make_caster::template cast_op_type; // See PR #4893 + return caster.operator result_t(); +} +template +typename make_caster::template cast_op_type::type> +cast_op(make_caster &&caster) { + using result_t = typename make_caster::template cast_op_type< + typename std::add_rvalue_reference::type>; // See PR #4893 + return std::move(caster).operator result_t(); +} + +template +class type_caster_enum_type { +private: + using Underlying = typename std::underlying_type::type; + +public: + static constexpr auto name = const_name(); + + template + static handle cast(SrcType &&src, return_value_policy, handle parent) { + handle native_enum + = global_internals_native_enum_type_map_get_item(std::type_index(typeid(EnumType))); + if (native_enum) { + return native_enum(static_cast(src)).release(); + } + return type_caster_base::cast( + std::forward(src), + // Fixes https://github.com/pybind/pybind11/pull/3643#issuecomment-1022987818: + return_value_policy::copy, + parent); + } + + template + static handle cast(SrcType *src, return_value_policy policy, handle parent) { + return cast(*src, policy, parent); + } + + bool load(handle src, bool convert) { + handle native_enum + = global_internals_native_enum_type_map_get_item(std::type_index(typeid(EnumType))); + if (native_enum) { + if (!isinstance(src, native_enum)) { + return false; + } + type_caster underlying_caster; + if (!underlying_caster.load(src.attr("value"), convert)) { + pybind11_fail("native_enum internal consistency failure."); + } + value = static_cast(static_cast(underlying_caster)); + return true; + } + if (!pybind11_enum_) { + pybind11_enum_.reset(new type_caster_base()); + } + return pybind11_enum_->load(src, convert); + } + + template + using cast_op_type = detail::cast_op_type; + + // NOLINTNEXTLINE(google-explicit-constructor) + operator EnumType *() { + if (!pybind11_enum_) { + return &value; + } + return pybind11_enum_->operator EnumType *(); + } + + // NOLINTNEXTLINE(google-explicit-constructor) + operator EnumType &() { + if (!pybind11_enum_) { + return value; + } + return pybind11_enum_->operator EnumType &(); + } + +private: + std::unique_ptr> pybind11_enum_; + EnumType value; +}; + +template +struct type_caster_enum_type_enabled : std::true_type {}; + +template +struct type_uses_type_caster_enum_type { + static constexpr bool value + = std::is_enum::value && type_caster_enum_type_enabled::value; +}; + +template +class type_caster::value>> + : public type_caster_enum_type {}; + +template ::value, int> = 0> +bool isinstance_native_enum_impl(handle obj, const std::type_info &tp) { + handle native_enum = global_internals_native_enum_type_map_get_item(tp); + if (!native_enum) { + return false; + } + return isinstance(obj, native_enum); +} + +template ::value, int> = 0> +bool isinstance_native_enum_impl(handle, const std::type_info &) { + return false; +} + +template +bool isinstance_native_enum(handle obj, const std::type_info &tp) { + return isinstance_native_enum_impl>(obj, tp); +} + +template +class type_caster> { +private: + using caster_t = make_caster; + caster_t subcaster; + using reference_t = type &; + using subcaster_cast_op_type = typename caster_t::template cast_op_type; + + static_assert( + std::is_same::type &, subcaster_cast_op_type>::value + || std::is_same::value, + "std::reference_wrapper caster requires T to have a caster with an " + "`operator T &()` or `operator const T &()`"); + +public: + bool load(handle src, bool convert) { return subcaster.load(src, convert); } + static constexpr auto name = caster_t::name; + static handle + cast(const std::reference_wrapper &src, return_value_policy policy, handle parent) { + // It is definitely wrong to take ownership of this pointer, so mask that rvp + if (policy == return_value_policy::take_ownership + || policy == return_value_policy::automatic) { + policy = return_value_policy::automatic_reference; + } + return caster_t::cast(&src.get(), policy, parent); + } + template + using cast_op_type = std::reference_wrapper; + explicit operator std::reference_wrapper() { return cast_op(subcaster); } +}; + +#define PYBIND11_TYPE_CASTER(type, py_name) \ +protected: \ + type value; \ + \ +public: \ + static constexpr auto name = py_name; \ + template >::value, \ + int> \ + = 0> \ + static ::pybind11::handle cast( \ + T_ *src, ::pybind11::return_value_policy policy, ::pybind11::handle parent) { \ + if (!src) \ + return ::pybind11::none().release(); \ + if (policy == ::pybind11::return_value_policy::take_ownership) { \ + auto h = cast(std::move(*src), policy, parent); \ + delete src; \ + return h; \ + } \ + return cast(*src, policy, parent); \ + } \ + operator type *() { return &value; } /* NOLINT(bugprone-macro-parentheses) */ \ + operator type &() { return value; } /* NOLINT(bugprone-macro-parentheses) */ \ + operator type &&() && { return std::move(value); } /* NOLINT(bugprone-macro-parentheses) */ \ + template \ + using cast_op_type = ::pybind11::detail::movable_cast_op_type + +template +using is_std_char_type = any_of, /* std::string */ +#if defined(PYBIND11_HAS_U8STRING) + std::is_same, /* std::u8string */ +#endif + std::is_same, /* std::u16string */ + std::is_same, /* std::u32string */ + std::is_same /* std::wstring */ + >; + +template +struct type_caster::value && !is_std_char_type::value>> { + using _py_type_0 = conditional_t; + using _py_type_1 = conditional_t::value, + _py_type_0, + typename std::make_unsigned<_py_type_0>::type>; + using py_type = conditional_t::value, double, _py_type_1>; + +public: + bool load(handle src, bool convert) { + py_type py_value; + + if (!src) { + return false; + } + +#if !defined(PYPY_VERSION) + auto index_check = [](PyObject *o) { return PyIndex_Check(o); }; +#else + // In PyPy 7.3.3, `PyIndex_Check` is implemented by calling `__index__`, + // while CPython only considers the existence of `nb_index`/`__index__`. + auto index_check = [](PyObject *o) { return hasattr(o, "__index__"); }; +#endif + + if (std::is_floating_point::value) { + if (convert || PyFloat_Check(src.ptr())) { + py_value = (py_type) PyFloat_AsDouble(src.ptr()); + } else { + return false; + } + } else if (PyFloat_Check(src.ptr()) + || (!convert && !PYBIND11_LONG_CHECK(src.ptr()) && !index_check(src.ptr()))) { + return false; + } else { + handle src_or_index = src; + // PyPy: 7.3.7's 3.8 does not implement PyLong_*'s __index__ calls. +#if defined(PYPY_VERSION) + object index; + if (!PYBIND11_LONG_CHECK(src.ptr())) { // So: index_check(src.ptr()) + index = reinterpret_steal(PyNumber_Index(src.ptr())); + if (!index) { + PyErr_Clear(); + if (!convert) + return false; + } else { + src_or_index = index; + } + } +#endif + if (std::is_unsigned::value) { + py_value = as_unsigned(src_or_index.ptr()); + } else { // signed integer: + py_value = sizeof(T) <= sizeof(long) + ? (py_type) PyLong_AsLong(src_or_index.ptr()) + : (py_type) PYBIND11_LONG_AS_LONGLONG(src_or_index.ptr()); + } + } + + // Python API reported an error + bool py_err = py_value == (py_type) -1 && PyErr_Occurred(); + + // Check to see if the conversion is valid (integers should match exactly) + // Signed/unsigned checks happen elsewhere + if (py_err + || (std::is_integral::value && sizeof(py_type) != sizeof(T) + && py_value != (py_type) (T) py_value)) { + PyErr_Clear(); + if (py_err && convert && (PyNumber_Check(src.ptr()) != 0)) { + auto tmp = reinterpret_steal(std::is_floating_point::value + ? PyNumber_Float(src.ptr()) + : PyNumber_Long(src.ptr())); + PyErr_Clear(); + return load(tmp, false); + } + return false; + } + + value = (T) py_value; + return true; + } + + template + static typename std::enable_if::value, handle>::type + cast(U src, return_value_policy /* policy */, handle /* parent */) { + return PyFloat_FromDouble((double) src); + } + + template + static typename std::enable_if::value && std::is_signed::value + && (sizeof(U) <= sizeof(long)), + handle>::type + cast(U src, return_value_policy /* policy */, handle /* parent */) { + return PYBIND11_LONG_FROM_SIGNED((long) src); + } + + template + static typename std::enable_if::value && std::is_unsigned::value + && (sizeof(U) <= sizeof(unsigned long)), + handle>::type + cast(U src, return_value_policy /* policy */, handle /* parent */) { + return PYBIND11_LONG_FROM_UNSIGNED((unsigned long) src); + } + + template + static typename std::enable_if::value && std::is_signed::value + && (sizeof(U) > sizeof(long)), + handle>::type + cast(U src, return_value_policy /* policy */, handle /* parent */) { + return PyLong_FromLongLong((long long) src); + } + + template + static typename std::enable_if::value && std::is_unsigned::value + && (sizeof(U) > sizeof(unsigned long)), + handle>::type + cast(U src, return_value_policy /* policy */, handle /* parent */) { + return PyLong_FromUnsignedLongLong((unsigned long long) src); + } + + PYBIND11_TYPE_CASTER(T, + io_name::value>( + "typing.SupportsInt", "int", "typing.SupportsFloat", "float")); +}; + +template +struct void_caster { +public: + bool load(handle src, bool) { + if (src && src.is_none()) { + return true; + } + return false; + } + static handle cast(T, return_value_policy /* policy */, handle /* parent */) { + return none().release(); + } + PYBIND11_TYPE_CASTER(T, const_name("None")); +}; + +template <> +class type_caster : public void_caster {}; + +template <> +class type_caster : public type_caster { +public: + using type_caster::cast; + + bool load(handle h, bool) { + if (!h) { + return false; + } + if (h.is_none()) { + value = nullptr; + return true; + } + + /* Check if this is a capsule */ + if (isinstance(h)) { + value = reinterpret_borrow(h); + return true; + } + + /* Check if this is a C++ type */ + const auto &bases = all_type_info((PyTypeObject *) type::handle_of(h).ptr()); + if (bases.size() == 1) { // Only allowing loading from a single-value type + value = values_and_holders(reinterpret_cast(h.ptr())).begin()->value_ptr(); + return true; + } + + /* Fail */ + return false; + } + + static handle cast(const void *ptr, return_value_policy /* policy */, handle /* parent */) { + if (ptr) { + return capsule(ptr).release(); + } + return none().release(); + } + + template + using cast_op_type = void *&; + explicit operator void *&() { return value; } + static constexpr auto name = const_name(PYBIND11_CAPSULE_TYPE_TYPE_HINT); + +private: + void *value = nullptr; +}; + +template <> +class type_caster : public void_caster {}; + +template <> +class type_caster { +public: + bool load(handle src, bool convert) { + if (!src) { + return false; + } + if (src.ptr() == Py_True) { + value = true; + return true; + } + if (src.ptr() == Py_False) { + value = false; + return true; + } + if (convert || is_numpy_bool(src)) { + // (allow non-implicit conversion for numpy booleans), use strncmp + // since NumPy 1.x had an additional trailing underscore. + + Py_ssize_t res = -1; + if (src.is_none()) { + res = 0; // None is implicitly converted to False + } +#if defined(PYPY_VERSION) + // On PyPy, check that "__bool__" attr exists + else if (hasattr(src, PYBIND11_BOOL_ATTR)) { + res = PyObject_IsTrue(src.ptr()); + } +#else + // Alternate approach for CPython: this does the same as the above, but optimized + // using the CPython API so as to avoid an unneeded attribute lookup. + else if (auto *tp_as_number = Py_TYPE(src.ptr())->tp_as_number) { + if (PYBIND11_NB_BOOL(tp_as_number)) { + res = (*PYBIND11_NB_BOOL(tp_as_number))(src.ptr()); + } + } +#endif + if (res == 0 || res == 1) { + value = (res != 0); + return true; + } + PyErr_Clear(); + } + return false; + } + static handle cast(bool src, return_value_policy /* policy */, handle /* parent */) { + return handle(src ? Py_True : Py_False).inc_ref(); + } + PYBIND11_TYPE_CASTER(bool, const_name("bool")); + +private: + // Test if an object is a NumPy boolean (without fetching the type). + static inline bool is_numpy_bool(handle object) { + const char *type_name = Py_TYPE(object.ptr())->tp_name; + // Name changed to `numpy.bool` in NumPy 2, `numpy.bool_` is needed for 1.x support + return std::strcmp("numpy.bool", type_name) == 0 + || std::strcmp("numpy.bool_", type_name) == 0; + } +}; + +// Helper class for UTF-{8,16,32} C++ stl strings: +template +struct string_caster { + using CharT = typename StringType::value_type; + + // Simplify life by being able to assume standard char sizes (the standard only guarantees + // minimums, but Python requires exact sizes) + static_assert(!std::is_same::value || sizeof(CharT) == 1, + "Unsupported char size != 1"); +#if defined(PYBIND11_HAS_U8STRING) + static_assert(!std::is_same::value || sizeof(CharT) == 1, + "Unsupported char8_t size != 1"); +#endif + static_assert(!std::is_same::value || sizeof(CharT) == 2, + "Unsupported char16_t size != 2"); + static_assert(!std::is_same::value || sizeof(CharT) == 4, + "Unsupported char32_t size != 4"); + // wchar_t can be either 16 bits (Windows) or 32 (everywhere else) + static_assert(!std::is_same::value || sizeof(CharT) == 2 || sizeof(CharT) == 4, + "Unsupported wchar_t size != 2/4"); + static constexpr size_t UTF_N = 8 * sizeof(CharT); + + bool load(handle src, bool) { + handle load_src = src; + if (!src) { + return false; + } + if (!PyUnicode_Check(load_src.ptr())) { + return load_raw(load_src); + } + + // For UTF-8 we avoid the need for a temporary `bytes` object by using + // `PyUnicode_AsUTF8AndSize`. + if (UTF_N == 8) { + Py_ssize_t size = -1; + const auto *buffer + = reinterpret_cast(PyUnicode_AsUTF8AndSize(load_src.ptr(), &size)); + if (!buffer) { + PyErr_Clear(); + return false; + } + value = StringType(buffer, static_cast(size)); + return true; + } + + auto utfNbytes + = reinterpret_steal(PyUnicode_AsEncodedString(load_src.ptr(), + UTF_N == 8 ? "utf-8" + : UTF_N == 16 ? "utf-16" + : "utf-32", + nullptr)); + if (!utfNbytes) { + PyErr_Clear(); + return false; + } + + const auto *buffer + = reinterpret_cast(PYBIND11_BYTES_AS_STRING(utfNbytes.ptr())); + size_t length = (size_t) PYBIND11_BYTES_SIZE(utfNbytes.ptr()) / sizeof(CharT); + // Skip BOM for UTF-16/32 + if (UTF_N > 8) { + buffer++; + length--; + } + value = StringType(buffer, length); + + // If we're loading a string_view we need to keep the encoded Python object alive: + if (IsView) { + loader_life_support::add_patient(utfNbytes); + } + + return true; + } + + static handle + cast(const StringType &src, return_value_policy /* policy */, handle /* parent */) { + const char *buffer = reinterpret_cast(src.data()); + auto nbytes = ssize_t(src.size() * sizeof(CharT)); + handle s = decode_utfN(buffer, nbytes); + if (!s) { + throw error_already_set(); + } + return s; + } + + PYBIND11_TYPE_CASTER(StringType, const_name(PYBIND11_STRING_NAME)); + +private: + static handle decode_utfN(const char *buffer, ssize_t nbytes) { +#if !defined(PYPY_VERSION) + return UTF_N == 8 ? PyUnicode_DecodeUTF8(buffer, nbytes, nullptr) + : UTF_N == 16 ? PyUnicode_DecodeUTF16(buffer, nbytes, nullptr, nullptr) + : PyUnicode_DecodeUTF32(buffer, nbytes, nullptr, nullptr); +#else + // PyPy segfaults when on PyUnicode_DecodeUTF16 (and possibly on PyUnicode_DecodeUTF32 as + // well), so bypass the whole thing by just passing the encoding as a string value, which + // works properly: + return PyUnicode_Decode(buffer, + nbytes, + UTF_N == 8 ? "utf-8" + : UTF_N == 16 ? "utf-16" + : "utf-32", + nullptr); +#endif + } + + // When loading into a std::string or char*, accept a bytes/bytearray object as-is (i.e. + // without any encoding/decoding attempt). For other C++ char sizes this is a no-op. + // which supports loading a unicode from a str, doesn't take this path. + template + bool load_raw(enable_if_t::value, handle> src) { + if (PYBIND11_BYTES_CHECK(src.ptr())) { + // We were passed raw bytes; accept it into a std::string or char* + // without any encoding attempt. + const char *bytes = PYBIND11_BYTES_AS_STRING(src.ptr()); + if (!bytes) { + pybind11_fail("Unexpected PYBIND11_BYTES_AS_STRING() failure."); + } + value = StringType(bytes, (size_t) PYBIND11_BYTES_SIZE(src.ptr())); + return true; + } + if (PyByteArray_Check(src.ptr())) { + // We were passed a bytearray; accept it into a std::string or char* + // without any encoding attempt. + const char *bytearray = PyByteArray_AsString(src.ptr()); + if (!bytearray) { + pybind11_fail("Unexpected PyByteArray_AsString() failure."); + } + value = StringType(bytearray, (size_t) PyByteArray_Size(src.ptr())); + return true; + } + + return false; + } + + template + bool load_raw(enable_if_t::value, handle>) { + return false; + } +}; + +template +struct type_caster, + enable_if_t::value>> + : string_caster> {}; + +#ifdef PYBIND11_HAS_STRING_VIEW +template +struct type_caster, + enable_if_t::value>> + : string_caster, true> {}; +#endif + +// Type caster for C-style strings. We basically use a std::string type caster, but also add the +// ability to use None as a nullptr char* (which the string caster doesn't allow). +template +struct type_caster::value>> { + using StringType = std::basic_string; + using StringCaster = make_caster; + StringCaster str_caster; + bool none = false; + CharT one_char = 0; + +public: + bool load(handle src, bool convert) { + if (!src) { + return false; + } + if (src.is_none()) { + // Defer accepting None to other overloads (if we aren't in convert mode): + if (!convert) { + return false; + } + none = true; + return true; + } + return str_caster.load(src, convert); + } + + static handle cast(const CharT *src, return_value_policy policy, handle parent) { + if (src == nullptr) { + return pybind11::none().release(); + } + return StringCaster::cast(StringType(src), policy, parent); + } + + static handle cast(CharT src, return_value_policy policy, handle parent) { + if (std::is_same::value) { + handle s = PyUnicode_DecodeLatin1((const char *) &src, 1, nullptr); + if (!s) { + throw error_already_set(); + } + return s; + } + return StringCaster::cast(StringType(1, src), policy, parent); + } + + explicit operator CharT *() { + return none ? nullptr : const_cast(static_cast(str_caster).c_str()); + } + explicit operator CharT &() { + if (none) { + throw value_error("Cannot convert None to a character"); + } + + auto &value = static_cast(str_caster); + size_t str_len = value.size(); + if (str_len == 0) { + throw value_error("Cannot convert empty string to a character"); + } + + // If we're in UTF-8 mode, we have two possible failures: one for a unicode character that + // is too high, and one for multiple unicode characters (caught later), so we need to + // figure out how long the first encoded character is in bytes to distinguish between these + // two errors. We also allow want to allow unicode characters U+0080 through U+00FF, as + // those can fit into a single char value. + if (StringCaster::UTF_N == 8 && str_len > 1 && str_len <= 4) { + auto v0 = static_cast(value[0]); + // low bits only: 0-127 + // 0b110xxxxx - start of 2-byte sequence + // 0b1110xxxx - start of 3-byte sequence + // 0b11110xxx - start of 4-byte sequence + size_t char0_bytes = (v0 & 0x80) == 0 ? 1 + : (v0 & 0xE0) == 0xC0 ? 2 + : (v0 & 0xF0) == 0xE0 ? 3 + : 4; + + if (char0_bytes == str_len) { + // If we have a 128-255 value, we can decode it into a single char: + if (char0_bytes == 2 && (v0 & 0xFC) == 0xC0) { // 0x110000xx 0x10xxxxxx + one_char = static_cast(((v0 & 3) << 6) + + (static_cast(value[1]) & 0x3F)); + return one_char; + } + // Otherwise we have a single character, but it's > U+00FF + throw value_error("Character code point not in range(0x100)"); + } + } + + // UTF-16 is much easier: we can only have a surrogate pair for values above U+FFFF, thus a + // surrogate pair with total length 2 instantly indicates a range error (but not a "your + // string was too long" error). + else if (StringCaster::UTF_N == 16 && str_len == 2) { + one_char = static_cast(value[0]); + if (one_char >= 0xD800 && one_char < 0xE000) { + throw value_error("Character code point not in range(0x10000)"); + } + } + + if (str_len != 1) { + throw value_error("Expected a character, but multi-character string found"); + } + + one_char = value[0]; + return one_char; + } + + static constexpr auto name = const_name(PYBIND11_STRING_NAME); + template + using cast_op_type = pybind11::detail::cast_op_type<_T>; +}; + +// Base implementation for std::tuple and std::pair +template