diff --git a/VERSION.txt b/VERSION.txt
index 524ef5293..3e63e3598 100644
--- a/VERSION.txt
+++ b/VERSION.txt
@@ -1 +1 @@
-0.20251204.0
+0.20260210.0
diff --git a/extern/README.md b/extern/README.md
index 06df16d38..4fe5a6b81 100644
--- a/extern/README.md
+++ b/extern/README.md
@@ -2,4 +2,4 @@
- [GoogleTest](https://github.com/google/googletest) [1.17.0](https://github.com/google/googletest/releases/tag/v1.17.0)
- [modp_b64](https://chromium.googlesource.com/chromium/src/third_party/modp_b64/) at commit [5068510](https://chromium.googlesource.com/chromium/src/third_party/modp_b64/+/50685101d51ef9aabbd60c94f52d9e026d39c509)
-- [nanobind](https://github.com/wjakob/nanobind) [2.9.2](https://github.com/wjakob/nanobind/releases/tag/v2.9.2)
+- [nanobind](https://github.com/wjakob/nanobind) [2.11.0](https://github.com/wjakob/nanobind/releases/tag/v2.11.0)
diff --git a/extern/nanobind/.github/workflows/ci.yml b/extern/nanobind/.github/workflows/ci.yml
index ee4589d95..9568eca5d 100644
--- a/extern/nanobind/.github/workflows/ci.yml
+++ b/extern/nanobind/.github/workflows/ci.yml
@@ -20,8 +20,8 @@ jobs:
strategy:
fail-fast: false
matrix:
- os: ['ubuntu-latest', 'windows-2022', 'macos-13']
- python: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13', '3.14.0-rc.2', 'pypy3.9-v7.3.16', 'pypy3.10-v7.3.17']
+ os: ['ubuntu-latest', 'windows-2022', 'macos-15']
+ python: ['3.9', '3.10', '3.11', '3.12', '3.13', '3.14', 'pypy3.10-v7.3.19', 'pypy3.11-v7.3.20']
name: "Python ${{ matrix.python }} / ${{ matrix.os }}"
runs-on: ${{ matrix.os }}
@@ -107,98 +107,180 @@ jobs:
cd build;
python3 -m pytest
- old-compilers:
- if: false # Disable for now, the CI is glitchy
- strategy:
- fail-fast: false
- matrix:
- include:
- - cc: gcc-8
- cxx: g++-8
- apt: gcc-8 g++-8
- - cc: gcc-9
- cxx: g++-9
- apt: gcc-9
- - cc: clang-8
- cxx: clang++-8
- apt: clang-8
- - cc: clang-9
- cxx: clang++-9
- apt: clang-9
- - cc: clang-10
- cxx: clang++-10
- apt: clang-10
-
+ free-threaded:
+ name: "Python 3.14-dev / ubuntu.latest [free-threaded]"
runs-on: ubuntu-latest
- container: ubuntu:20.04
- name: "${{matrix.cc}} on Ubuntu 20.04"
- env:
- CC: ${{matrix.cc}}
- CXX: ${{matrix.cxx}}
- DEBIAN_FRONTEND: noninteractive
steps:
- - name: Install dependencies
- run: |
- apt-get update
- apt-get install -y python3-numpy python3-pip python3-pytest libeigen3-dev cmake git ${{matrix.apt}}
- python3 -m pip install typing_extensions
-
- uses: actions/checkout@v4
with:
submodules: true
+ - uses: deadsnakes/action@v3.1.0
+ with:
+ python-version: 3.14-dev
+ nogil: true
+
+ - name: Install the latest CMake
+ uses: lukka/get-cmake@latest
+
+ - name: Install PyTest
+ run: |
+ python -m pip install pytest pytest-github-actions-annotate-failures
+
- name: Configure
- run: cmake -S . -B build
+ run: >
+ cmake -S . -B build -DNB_TEST_FREE_THREADED=ON
- name: Build C++
- run: cmake --build build -j 2
+ run: >
+ cmake --build build -j 2
- name: Check ABI tag
run: >
cd build/tests;
- python3 -c 'import test_functions_ext as t; print(f"ABI tag is \"{ t.abi_tag() }\"")'
+ python -c 'import test_functions_ext as t; print(f"ABI tag is \"{ t.abi_tag() }\"")'
- name: Run tests
run: >
cd build;
- python3 -m pytest
+ python -m pytest
- free-threaded:
- name: "Python 3.14-dev / ubuntu.latest [free-threaded]"
- runs-on: ubuntu-latest
+ mingw:
+ runs-on: windows-2022
+ name: "Python ${{ matrix.python }} / MinGW-w64"
+ strategy:
+ fail-fast: false
+ matrix:
+ python: ['3.12']
steps:
- uses: actions/checkout@v4
with:
submodules: true
- - uses: deadsnakes/action@v3.1.0
+ - name: Setup Python ${{ matrix.python }}
+ uses: actions/setup-python@v5
with:
- python-version: 3.14-dev
- nogil: true
+ python-version: ${{ matrix.python }}
+ cache: 'pip'
+
+ - name: Setup MSYS2 (MINGW64)
+ uses: msys2/setup-msys2@v2
+ with:
+ msystem: MINGW64
+ install: >-
+ mingw-w64-x86_64-gcc
+ mingw-w64-x86_64-cmake
+ mingw-w64-x86_64-ninja
+ mingw-w64-x86_64-python
+ mingw-w64-x86_64-python-pip
+ mingw-w64-x86_64-python-pytest
+
+ - name: Install Python packages
+ shell: msys2 {0}
+ run: |
+ python -m pip install pytest-github-actions-annotate-failures typing_extensions
+
+ - name: Configure
+ shell: msys2 {0}
+ run: |
+ export PATH=/mingw64/bin:$PATH
+ export CC=gcc
+ export CXX=g++
+ PYEXE=/mingw64/bin/python3.exe
+ cmake -S . -B build -G Ninja \
+ -DPython_EXECUTABLE="$(cygpath -w "$PYEXE")" \
+ -DNB_TEST_FREE_THREADED=OFF
+
+ - name: Build C++
+ shell: msys2 {0}
+ run: cmake --build build -j 2
+
+ - name: Check ABI tag
+ shell: msys2 {0}
+ run: |
+ cd build/tests
+ python -c 'import test_functions_ext as t; print(f"ABI tag is \"{t.abi_tag()}\"")'
+
+ - name: Run tests
+ shell: msys2 {0}
+ run: |
+ cd build
+ python -m pytest
+
+ intel:
+ runs-on: ubuntu-22.04
+ name: "Python ${{ matrix.python }} / Intel ICX"
+ strategy:
+ fail-fast: false
+ matrix:
+ python: ['3.12']
+
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: true
+
+ - name: Setup Python ${{ matrix.python }}
+ uses: actions/setup-python@v5
+ with:
+ python-version: ${{ matrix.python }}
+ cache: 'pip'
+
+ - name: Cache Intel oneAPI
+ id: cache-oneapi
+ uses: actions/cache@v4
+ with:
+ path: /opt/intel/oneapi
+ key: install-${{ runner.os }}-intel-oneapi-compiler-2025.2
+
+ - name: Add Intel repository
+ if: steps.cache-oneapi.outputs.cache-hit != 'true'
+ run: |
+ wget -O- https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB | gpg --dearmor | sudo tee /usr/share/keyrings/oneapi-archive-keyring.gpg > /dev/null
+ echo "deb [signed-by=/usr/share/keyrings/oneapi-archive-keyring.gpg] https://apt.repos.intel.com/oneapi all main" | sudo tee /etc/apt/sources.list.d/oneAPI.list
+ sudo apt-get update
+
+ - name: Install Intel oneAPI compilers
+ if: steps.cache-oneapi.outputs.cache-hit != 'true'
+ run: |
+ sudo apt-get install -y intel-oneapi-compiler-dpcpp-cpp
+
+ - name: Cleanup Intel oneAPI cache
+ if: steps.cache-oneapi.outputs.cache-hit != 'true'
+ run: |
+ sudo rm -rf /opt/intel/oneapi/compiler/*/linux/lib/ia32
+ sudo rm -rf /opt/intel/oneapi/compiler/*/linux/lib/emu
+ sudo rm -rf /opt/intel/oneapi/compiler/*/linux/lib/oclfpga
- name: Install the latest CMake
uses: lukka/get-cmake@latest
- name: Install PyTest
run: |
- python -m pip install pytest pytest-github-actions-annotate-failures
+ python -m pip install pytest pytest-github-actions-annotate-failures typing_extensions
- name: Configure
- run: >
- cmake -S . -B build -DNB_TEST_FREE_THREADED=ON
+ run: |
+ source /opt/intel/oneapi/setvars.sh
+ export CC=icx
+ export CXX=icpx
+ cmake -S . -B build
- name: Build C++
- run: >
+ run: |
+ source /opt/intel/oneapi/setvars.sh
cmake --build build -j 2
- name: Check ABI tag
- run: >
- cd build/tests;
+ run: |
+ source /opt/intel/oneapi/setvars.sh
+ cd build/tests
python -c 'import test_functions_ext as t; print(f"ABI tag is \"{ t.abi_tag() }\"")'
- name: Run tests
- run: >
- cd build;
+ run: |
+ source /opt/intel/oneapi/setvars.sh
+ cd build
python -m pytest
diff --git a/extern/nanobind/CMakeLists.txt b/extern/nanobind/CMakeLists.txt
index 1c1ebdf23..3d59ca634 100644
--- a/extern/nanobind/CMakeLists.txt
+++ b/extern/nanobind/CMakeLists.txt
@@ -145,7 +145,7 @@ if (NOT TARGET Python::Module OR NOT TARGET Python::Interpreter)
set(NB_PYTHON_DEV_MODULE Development.Module)
endif()
- find_package(Python 3.8
+ find_package(Python 3.9
REQUIRED COMPONENTS Interpreter ${NB_PYTHON_DEV_MODULE}
OPTIONAL_COMPONENTS Development.SABIModule)
endif()
diff --git a/extern/nanobind/README.md b/extern/nanobind/README.md
index 4d5b12ade..efff040f5 100644
--- a/extern/nanobind/README.md
+++ b/extern/nanobind/README.md
@@ -30,6 +30,64 @@ runtime overheads compared to pybind11. nanobind also outperforms Cython in
important metrics (**3-12×** binary size reduction, **1.6-4×** compilation time
reduction, similar runtime performance).
+## Testimonials
+
+A selection of testimonials from projects that migrated from pybind11 to nanobind.
+
+
+|
+
+**IREE** · [LLVM Discourse](https://discourse.llvm.org/t/nanobind-for-mlir-python-bindings/83511/5)
+
+> *"IREE and its derivatives switched 1.5 years ago. It has been one of the single best dep decisions I've made. Not only is it much-much faster to compile, it produces smaller binaries and has a much more lean interface to the underlying Python machinery that all adds up to significant performance improvements. Worked exactly like it said on the tin."*
+
+— **Stella Laurenzo**, Google
+
+ |
+|
+
+**XLA/MLIR** · [GitHub PR](https://github.com/llvm/llvm-project/pull/118583)
+
+> *"For a complicated Google-internal LLM model in JAX, this change improves the MLIR lowering time by around 5s (out of around 30s), which is a significant speedup for simply switching binding frameworks."*
+
+— **Peter Hawkins**, Google
+
+ |
+|
+
+**Apple MLX** · [X](https://x.com/awnihannun/status/1890495434021326974)
+
+> *"MLX uses nanobind to bind C++ to Python. It's a critical piece of MLX infra and is why running Python code is nearly the same speed as running C++ directly. Also makes it super easy to move arrays between frameworks."*
+
+— **Awni Hannun**, Apple
+
+ |
+|
+
+**JAX** · [GitHub](https://github.com/jax-ml/jax/commit/70b7d501816c6e9f131a0a8b3e4a527e53eeebd7)
+
+> *"nanobind has a number of [advantages](https://nanobind.readthedocs.io/en/latest/why.html), notably speed of compilation and dispatch, but the main reason to do this for these bindings is because nanobind can target the Python Stable ABI starting with Python 3.12. This means that we will not need to ship per-Python version CUDA plugins starting with Python 3.12."*
+
+— **Peter Hawkins**, Google
+
+ |
+|
+
+**FEniCS / DOLFINx** · [GitHub](https://github.com/FEniCS/dolfinx/pull/2820)
+
+> *"nanobind is smaller than pybind11, the wrappers build faster and it has significantly improved support for wrapping multi-dimensional arrays, which we use heavily. The nanobind docs are easier to follow on the low-level details, which makes understanding the memory management in the wrapper layer easier."*
+
+— **Garth N. Wells**
+ |
+|
+
+**PennyLane** · [Release notes](https://docs.pennylane.ai/projects/catalyst/en/stable/dev/release_notes.html)
+
+> *"Nanobind has been developed as a natural successor to the pybind11 library and offers a number of advantages like its ability to target Python's Stable ABI."*
+
+ |
+
+
## Documentation
Please see the following links for tutorial and reference documentation in
diff --git a/extern/nanobind/cmake/collect-symbols-pypy.py b/extern/nanobind/cmake/collect-symbols-pypy.py
index 805490d0c..b76886d01 100644
--- a/extern/nanobind/cmake/collect-symbols-pypy.py
+++ b/extern/nanobind/cmake/collect-symbols-pypy.py
@@ -5,7 +5,8 @@
funcs: "set[str]" = set()
files = [
- ('https://downloads.python.org/pypy/pypy3.9-v7.3.11-macos_arm64.tar.bz2', 'pypy3.9-v7.3.11-macos_arm64/bin/libpypy3.9-c.dylib')
+ ('https://downloads.python.org/pypy/pypy3.9-v7.3.11-macos_arm64.tar.bz2', 'pypy3.9-v7.3.11-macos_arm64/bin/libpypy3.9-c.dylib'),
+ ('https://downloads.python.org/pypy/pypy3.11-v7.3.20-macos_arm64.tar.bz2', 'pypy3.11-v7.3.20-macos_arm64/bin/libpypy3.11-c.dylib'),
]
for f in files:
@@ -25,4 +26,4 @@
with open("darwin-ld-pypy.sym", "w") as f:
for func in sorted(list(funcs)):
- f.write(f'-U _{func}\n')
+ f.write(f'-U {func}\n')
diff --git a/extern/nanobind/cmake/collect-symbols.py b/extern/nanobind/cmake/collect-symbols.py
index ad92da4c1..246397686 100644
--- a/extern/nanobind/cmake/collect-symbols.py
+++ b/extern/nanobind/cmake/collect-symbols.py
@@ -10,13 +10,13 @@
funcs: "set[str]" = set()
-for ver in ['3.7', '3.8', '3.9']:
+for ver in ['3.9']:
url = f'https://raw.githubusercontent.com/python/cpython/{ver}/PC/python3.def'
output = urlopen(url).read().decode('utf-8')
for match in re.findall(r" (.*)=.*", output):
funcs.add(match)
-for ver in ['3.10', '3.11', 'main']:
+for ver in ['3.10', '3.11', '3.12', '3.13', '3.14', 'main']:
url = f'https://raw.githubusercontent.com/python/cpython/{ver}/PC/python3dll.c'
output = urlopen(url).read().decode('utf-8')
for match in re.findall(r"EXPORT_FUNC\((.*)\)", output):
diff --git a/extern/nanobind/cmake/darwin-ld-cpython.sym b/extern/nanobind/cmake/darwin-ld-cpython.sym
index 031e74e1f..819f2eca0 100644
--- a/extern/nanobind/cmake/darwin-ld-cpython.sym
+++ b/extern/nanobind/cmake/darwin-ld-cpython.sym
@@ -1,3 +1,4 @@
+-U _PyABIInfo_Check
-U _PyAIter_Check
-U _PyArg_Parse
-U _PyArg_ParseTuple
@@ -128,6 +129,7 @@
-U _PyErr_CheckSignals
-U _PyErr_Clear
-U _PyErr_Display
+-U _PyErr_DisplayException
-U _PyErr_ExceptionMatches
-U _PyErr_Fetch
-U _PyErr_Format
@@ -184,6 +186,9 @@
-U _PyEval_GetBuiltins
-U _PyEval_GetCallStats
-U _PyEval_GetFrame
+-U _PyEval_GetFrameBuiltins
+-U _PyEval_GetFrameGlobals
+-U _PyEval_GetFrameLocals
-U _PyEval_GetFuncDesc
-U _PyEval_GetFuncName
-U _PyEval_GetGlobals
@@ -263,9 +268,11 @@
-U _PyExc_WindowsError
-U _PyExc_ZeroDivisionError
-U _PyExceptionClass_Name
+-U _PyException_GetArgs
-U _PyException_GetCause
-U _PyException_GetContext
-U _PyException_GetTraceback
+-U _PyException_SetArgs
-U _PyException_SetCause
-U _PyException_SetContext
-U _PyException_SetTraceback
@@ -326,6 +333,7 @@
-U _PyInterpreterState_New
-U _PyIter_Check
-U _PyIter_Next
+-U _PyIter_NextItem
-U _PyIter_Send
-U _PyListIter_Type
-U _PyListRevIter_Type
@@ -343,33 +351,49 @@
-U _PyList_Type
-U _PyLongRangeIter_Type
-U _PyLong_AsDouble
+-U _PyLong_AsInt
+-U _PyLong_AsInt32
+-U _PyLong_AsInt64
-U _PyLong_AsLong
-U _PyLong_AsLongAndOverflow
-U _PyLong_AsLongLong
-U _PyLong_AsLongLongAndOverflow
+-U _PyLong_AsNativeBytes
-U _PyLong_AsSize_t
-U _PyLong_AsSsize_t
+-U _PyLong_AsUInt32
+-U _PyLong_AsUInt64
-U _PyLong_AsUnsignedLong
-U _PyLong_AsUnsignedLongLong
-U _PyLong_AsUnsignedLongLongMask
-U _PyLong_AsUnsignedLongMask
-U _PyLong_AsVoidPtr
-U _PyLong_FromDouble
+-U _PyLong_FromInt32
+-U _PyLong_FromInt64
-U _PyLong_FromLong
-U _PyLong_FromLongLong
+-U _PyLong_FromNativeBytes
-U _PyLong_FromSize_t
-U _PyLong_FromSsize_t
+-U _PyLong_FromUInt32
+-U _PyLong_FromUInt64
-U _PyLong_FromString
-U _PyLong_FromUnsignedLong
-U _PyLong_FromUnsignedLongLong
+-U _PyLong_FromUnsignedNativeBytes
-U _PyLong_FromVoidPtr
-U _PyLong_GetInfo
-U _PyLong_Type
-U _PyMap_Type
-U _PyMapping_Check
-U _PyMapping_GetItemString
+-U _PyMapping_GetOptionalItem
+-U _PyMapping_GetOptionalItemString
-U _PyMapping_HasKey
-U _PyMapping_HasKeyString
+-U _PyMapping_HasKeyStringWithError
+-U _PyMapping_HasKeyWithError
-U _PyMapping_Items
-U _PyMapping_Keys
-U _PyMapping_Length
@@ -381,6 +405,10 @@
-U _PyMem_Calloc
-U _PyMem_Free
-U _PyMem_Malloc
+-U _PyMem_RawCalloc
+-U _PyMem_RawFree
+-U _PyMem_RawMalloc
+-U _PyMem_RawRealloc
-U _PyMem_Realloc
-U _PyMemberDescr_Type
-U _PyMember_GetOne
@@ -393,6 +421,7 @@
-U _PyMethodDescr_Type
-U _PyModuleDef_Init
-U _PyModuleDef_Type
+-U _PyModule_Add
-U _PyModule_AddFunctions
-U _PyModule_AddIntConstant
-U _PyModule_AddObject
@@ -401,7 +430,9 @@
-U _PyModule_AddType
-U _PyModule_Create2
-U _PyModule_ExecDef
+-U _PyModule_Exec
-U _PyModule_FromDefAndSpec2
+-U _PyModule_FromSlotsAndSpec
-U _PyModule_GetDef
-U _PyModule_GetDict
-U _PyModule_GetFilename
@@ -409,6 +440,8 @@
-U _PyModule_GetName
-U _PyModule_GetNameObject
-U _PyModule_GetState
+-U _PyModule_GetStateSize
+-U _PyModule_GetToken
-U _PyModule_New
-U _PyModule_NewObject
-U _PyModule_SetDocString
@@ -521,6 +554,8 @@
-U _PyObject_GetIter
-U _PyObject_HasAttr
-U _PyObject_HasAttrString
+-U _PyObject_HasAttrStringWithError
+-U _PyObject_HasAttrWithError
-U _PyObject_Hash
-U _PyObject_HashNotImplemented
-U _PyObject_Init
@@ -601,9 +636,15 @@
-U _PySys_AddWarnOption
-U _PySys_AddWarnOptionUnicode
-U _PySys_AddXOption
+-U _PySys_Audit
+-U _PySys_AuditTuple
-U _PySys_FormatStderr
-U _PySys_FormatStdout
+-U _PySys_GetAttr
+-U _PySys_GetAttrString
-U _PySys_GetObject
+-U _PySys_GetOptionalAttr
+-U _PySys_GetOptionalAttrString
-U _PySys_GetXOptions
-U _PySys_HasWarnOptions
-U _PySys_ResetWarnOptions
@@ -663,15 +704,21 @@
-U _PyTuple_Size
-U _PyTuple_Type
-U _PyType_ClearCache
+-U _PyType_Freeze
-U _PyType_FromMetaclass
-U _PyType_FromModuleAndSpec
-U _PyType_FromSpec
-U _PyType_FromSpecWithBases
-U _PyType_GenericAlloc
-U _PyType_GenericNew
+-U _PyType_GetBaseByToken
-U _PyType_GetFlags
+-U _PyType_GetFullyQualifiedName
-U _PyType_GetModule
+-U _PyType_GetModuleByDef
+-U _PyType_GetModuleByToken
-U _PyType_GetModuleState
+-U _PyType_GetModuleName
-U _PyType_GetName
-U _PyType_GetQualName
-U _PyType_GetSlot
@@ -757,6 +804,9 @@
-U _PyUnicode_EncodeCodePage
-U _PyUnicode_EncodeFSDefault
-U _PyUnicode_EncodeLocale
+-U _PyUnicode_Equal
+-U _PyUnicode_EqualToUTF8
+-U _PyUnicode_EqualToUTF8AndSize
-U _PyUnicode_FSConverter
-U _PyUnicode_FSDecoder
-U _PyUnicode_Find
@@ -822,6 +872,8 @@
-U _Py_GetArgcArgv
-U _Py_GetBuildInfo
-U _Py_GetCompiler
+-U _Py_GetConstant
+-U _Py_GetConstantBorrowed
-U _Py_GetCopyright
-U _Py_GetExecPrefix
-U _Py_GetPath
@@ -833,6 +885,7 @@
-U _Py_GetRecursionLimit
-U _Py_GetVersion
-U _Py_HasFileSystemDefaultEncoding
+-U _Py_IS_TYPE
-U _Py_IncRef
-U _Py_Initialize
-U _Py_InitializeEx
@@ -847,13 +900,18 @@
-U _Py_MakePendingCalls
-U _Py_NewInterpreter
-U _Py_NewRef
+-U _Py_PACK_FULL_VERSION
+-U _Py_PACK_VERSION
-U _Py_ReprEnter
-U _Py_ReprLeave
+-U _Py_SET_SIZE
+-U _Py_SIZE
-U _Py_SetPath
-U _Py_SetProgramName
-U _Py_SetPythonHome
-U _Py_SetRecursionLimit
-U _Py_SymtableString
+-U _Py_TYPE
-U _Py_UTF8Mode
-U _Py_VaBuildValue
-U _Py_XNewRef
@@ -901,6 +959,7 @@
-U __Py_NoneStruct
-U __Py_NotImplementedStruct
-U __Py_RefTotal
+-U __Py_SetRefcnt
-U __Py_SwappedOp
-U __Py_TrueStruct
-U __Py_VaBuildValue_SizeT
diff --git a/extern/nanobind/cmake/darwin-ld-pypy.sym b/extern/nanobind/cmake/darwin-ld-pypy.sym
index 73ecb7750..4e3e1c709 100644
--- a/extern/nanobind/cmake/darwin-ld-pypy.sym
+++ b/extern/nanobind/cmake/darwin-ld-pypy.sym
@@ -1,4 +1,73 @@
-U _PyArg_ValidateKeywordArguments
+-U _PyExpat_XML_DefaultCurrent
+-U _PyExpat_XML_ErrorString
+-U _PyExpat_XML_ExpatVersion
+-U _PyExpat_XML_ExpatVersionInfo
+-U _PyExpat_XML_ExternalEntityParserCreate
+-U _PyExpat_XML_FreeContentModel
+-U _PyExpat_XML_GetBase
+-U _PyExpat_XML_GetBuffer
+-U _PyExpat_XML_GetCurrentByteCount
+-U _PyExpat_XML_GetCurrentByteIndex
+-U _PyExpat_XML_GetCurrentColumnNumber
+-U _PyExpat_XML_GetCurrentLineNumber
+-U _PyExpat_XML_GetErrorCode
+-U _PyExpat_XML_GetFeatureList
+-U _PyExpat_XML_GetIdAttributeIndex
+-U _PyExpat_XML_GetInputContext
+-U _PyExpat_XML_GetParsingStatus
+-U _PyExpat_XML_GetSpecifiedAttributeCount
+-U _PyExpat_XML_MemFree
+-U _PyExpat_XML_MemMalloc
+-U _PyExpat_XML_MemRealloc
+-U _PyExpat_XML_Parse
+-U _PyExpat_XML_ParseBuffer
+-U _PyExpat_XML_ParserCreate
+-U _PyExpat_XML_ParserCreateNS
+-U _PyExpat_XML_ParserCreate_MM
+-U _PyExpat_XML_ParserFree
+-U _PyExpat_XML_ParserReset
+-U _PyExpat_XML_ResumeParser
+-U _PyExpat_XML_SetAttlistDeclHandler
+-U _PyExpat_XML_SetBase
+-U _PyExpat_XML_SetBillionLaughsAttackProtectionActivationThreshold
+-U _PyExpat_XML_SetBillionLaughsAttackProtectionMaximumAmplification
+-U _PyExpat_XML_SetCdataSectionHandler
+-U _PyExpat_XML_SetCharacterDataHandler
+-U _PyExpat_XML_SetCommentHandler
+-U _PyExpat_XML_SetDefaultHandler
+-U _PyExpat_XML_SetDefaultHandlerExpand
+-U _PyExpat_XML_SetDoctypeDeclHandler
+-U _PyExpat_XML_SetElementDeclHandler
+-U _PyExpat_XML_SetElementHandler
+-U _PyExpat_XML_SetEncoding
+-U _PyExpat_XML_SetEndCdataSectionHandler
+-U _PyExpat_XML_SetEndDoctypeDeclHandler
+-U _PyExpat_XML_SetEndElementHandler
+-U _PyExpat_XML_SetEndNamespaceDeclHandler
+-U _PyExpat_XML_SetEntityDeclHandler
+-U _PyExpat_XML_SetExternalEntityRefHandler
+-U _PyExpat_XML_SetExternalEntityRefHandlerArg
+-U _PyExpat_XML_SetHashSalt
+-U _PyExpat_XML_SetNamespaceDeclHandler
+-U _PyExpat_XML_SetNotStandaloneHandler
+-U _PyExpat_XML_SetNotationDeclHandler
+-U _PyExpat_XML_SetParamEntityParsing
+-U _PyExpat_XML_SetProcessingInstructionHandler
+-U _PyExpat_XML_SetReparseDeferralEnabled
+-U _PyExpat_XML_SetReturnNSTriplet
+-U _PyExpat_XML_SetSkippedEntityHandler
+-U _PyExpat_XML_SetStartCdataSectionHandler
+-U _PyExpat_XML_SetStartDoctypeDeclHandler
+-U _PyExpat_XML_SetStartElementHandler
+-U _PyExpat_XML_SetStartNamespaceDeclHandler
+-U _PyExpat_XML_SetUnknownEncodingHandler
+-U _PyExpat_XML_SetUnparsedEntityDeclHandler
+-U _PyExpat_XML_SetUserData
+-U _PyExpat_XML_SetXmlDeclHandler
+-U _PyExpat_XML_StopParser
+-U _PyExpat_XML_UseForeignDTD
+-U _PyExpat_XML_UseParserAsHandlerArg
-U _PyModule_AddType
-U _PyPyAnySet_Check
-U _PyPyAnySet_CheckExact
@@ -42,8 +111,8 @@
-U _PyPyCFunction_Call
-U _PyPyCFunction_Check
-U _PyPyCFunction_GetFunction
--U _PyPyCFunction_Type
-U _PyPyCFunction_NewEx
+-U _PyPyCFunction_Type
-U _PyPyCMethod_New
-U _PyPyCallIter_New
-U _PyPyCallable_Check
@@ -66,9 +135,14 @@
-U _PyPyCode_Addr2Line
-U _PyPyCode_Check
-U _PyPyCode_CheckExact
+-U _PyPyCode_GetCellvars
+-U _PyPyCode_GetCode
+-U _PyPyCode_GetFreevars
-U _PyPyCode_GetNumFree
+-U _PyPyCode_GetVarnames
-U _PyPyCode_New
-U _PyPyCode_NewEmpty
+-U _PyPyCode_NewWithPosOnlyArgs
-U _PyPyCodec_Decode
-U _PyPyCodec_Decoder
-U _PyPyCodec_Encode
@@ -85,6 +159,7 @@
-U _PyPyComplex_Type
-U _PyPyContextVar_Get
-U _PyPyContextVar_New
+-U _PyPyContextVar_Reset
-U _PyPyContextVar_Set
-U _PyPyCoro_Check
-U _PyPyCoro_CheckExact
@@ -95,6 +170,7 @@
-U _PyPyDateTime_DATE_GET_MICROSECOND
-U _PyPyDateTime_DATE_GET_MINUTE
-U _PyPyDateTime_DATE_GET_SECOND
+-U _PyPyDateTime_DATE_GET_TZINFO
-U _PyPyDateTime_DELTA_GET_DAYS
-U _PyPyDateTime_DELTA_GET_MICROSECONDS
-U _PyPyDateTime_DELTA_GET_SECONDS
@@ -108,6 +184,7 @@
-U _PyPyDateTime_TIME_GET_MICROSECOND
-U _PyPyDateTime_TIME_GET_MINUTE
-U _PyPyDateTime_TIME_GET_SECOND
+-U _PyPyDateTime_TIME_GET_TZINFO
-U _PyPyDate_Check
-U _PyPyDate_CheckExact
-U _PyPyDate_FromTimestamp
@@ -150,7 +227,9 @@
-U _PyPyErr_ExceptionMatches
-U _PyPyErr_Fetch
-U _PyPyErr_Format
+-U _PyPyErr_FormatV
-U _PyPyErr_GetExcInfo
+-U _PyPyErr_GetHandledException
-U _PyPyErr_GivenExceptionMatches
-U _PyPyErr_NewException
-U _PyPyErr_NewExceptionWithDoc
@@ -165,6 +244,7 @@
-U _PyPyErr_SetFromErrnoWithFilename
-U _PyPyErr_SetFromErrnoWithFilenameObject
-U _PyPyErr_SetFromErrnoWithFilenameObjects
+-U _PyPyErr_SetHandledException
-U _PyPyErr_SetInterrupt
-U _PyPyErr_SetNone
-U _PyPyErr_SetObject
@@ -181,6 +261,8 @@
-U _PyPyEval_EvalCode
-U _PyPyEval_GetBuiltins
-U _PyPyEval_GetFrame
+-U _PyPyEval_GetFuncDesc
+-U _PyPyEval_GetFuncName
-U _PyPyEval_GetGlobals
-U _PyPyEval_GetLocals
-U _PyPyEval_InitThreads
@@ -193,6 +275,7 @@
-U _PyPyExc_AssertionError
-U _PyPyExc_AttributeError
-U _PyPyExc_BaseException
+-U _PyPyExc_BaseExceptionGroup
-U _PyPyExc_BlockingIOError
-U _PyPyExc_BrokenPipeError
-U _PyPyExc_BufferError
@@ -204,7 +287,9 @@
-U _PyPyExc_ConnectionResetError
-U _PyPyExc_DeprecationWarning
-U _PyPyExc_EOFError
+-U _PyPyExc_EncodingWarning
-U _PyPyExc_Exception
+-U _PyPyExc_ExceptionGroup
-U _PyPyExc_FileExistsError
-U _PyPyExc_FileNotFoundError
-U _PyPyExc_FloatingPointError
@@ -271,8 +356,21 @@
-U _PyPyFloat_CheckExact
-U _PyPyFloat_FromDouble
-U _PyPyFloat_FromString
+-U _PyPyFloat_Pack2
+-U _PyPyFloat_Pack4
+-U _PyPyFloat_Pack8
-U _PyPyFloat_Type
+-U _PyPyFloat_Unpack2
+-U _PyPyFloat_Unpack4
+-U _PyPyFloat_Unpack8
+-U _PyPyFrame_GetBuiltins
+-U _PyPyFrame_GetGenerator
+-U _PyPyFrame_GetGlobals
+-U _PyPyFrame_GetLasti
+-U _PyPyFrame_GetLineNumber
+-U _PyPyFrame_GetLocals
-U _PyPyFrame_New
+-U _PyPyFrame_Type
-U _PyPyFrozenSet_Check
-U _PyPyFrozenSet_CheckExact
-U _PyPyFrozenSet_New
@@ -280,9 +378,16 @@
-U _PyPyFunction_Check
-U _PyPyFunction_CheckExact
-U _PyPyFunction_GetCode
+-U _PyPyFunction_GetGlobals
+-U _PyPyFunction_GetModule
-U _PyPyFunction_Type
+-U _PyPyGC_Collect
+-U _PyPyGC_Disable
+-U _PyPyGC_Enable
+-U _PyPyGC_IsEnabled
-U _PyPyGILState_Check
-U _PyPyGILState_Ensure
+-U _PyPyGILState_GetThisThreadState
-U _PyPyGILState_Release
-U _PyPyGen_Check
-U _PyPyGen_CheckExact
@@ -294,6 +399,7 @@
-U _PyPyImport_GetModuleDict
-U _PyPyImport_Import
-U _PyPyImport_ImportModule
+-U _PyPyImport_ImportModuleLevel
-U _PyPyImport_ImportModuleLevelObject
-U _PyPyImport_ImportModuleNoBlock
-U _PyPyImport_ReloadModule
@@ -308,6 +414,7 @@
-U _PyPyInterpreterState_Next
-U _PyPyIter_Check
-U _PyPyIter_Next
+-U _PyPyIter_Send
-U _PyPyList_Append
-U _PyPyList_AsTuple
-U _PyPyList_GET_ITEM
@@ -390,14 +497,19 @@
-U _PyPyModule_AddFunctions
-U _PyPyModule_AddIntConstant
-U _PyPyModule_AddObject
+-U _PyPyModule_AddObjectRef
-U _PyPyModule_AddStringConstant
-U _PyPyModule_Check
-U _PyPyModule_CheckExact
-U _PyPyModule_Create2
-U _PyPyModule_ExecDef
+-U _PyPyModule_FromDefAndSpec
+-U _PyPyModule_FromDefAndSpec2
-U _PyPyModule_GetDef
-U _PyPyModule_GetDict
+-U _PyPyModule_GetFilenameObject
-U _PyPyModule_GetName
+-U _PyPyModule_GetNameObject
-U _PyPyModule_GetState
-U _PyPyModule_New
-U _PyPyModule_NewObject
@@ -480,6 +592,8 @@
-U _PyPyObject_Format
-U _PyPyObject_Free
-U _PyPyObject_GC_Del
+-U _PyPyObject_GC_IsFinalized
+-U _PyPyObject_GC_IsTracked
-U _PyPyObject_GenericGetAttr
-U _PyPyObject_GenericGetDict
-U _PyPyObject_GenericSetAttr
@@ -565,6 +679,7 @@
-U _PyPySlice_Type
-U _PyPySlice_Unpack
-U _PyPyState_AddModule
+-U _PyPyState_FindModule
-U _PyPyState_RemoveModule
-U _PyPyStaticMethod_New
-U _PyPyStaticMethod_Type
@@ -584,13 +699,18 @@
-U _PyPyThreadState_Clear
-U _PyPyThreadState_Delete
-U _PyPyThreadState_DeleteCurrent
+-U _PyPyThreadState_EnterTracing
-U _PyPyThreadState_Get
-U _PyPyThreadState_GetDict
+-U _PyPyThreadState_GetFrame
+-U _PyPyThreadState_GetID
+-U _PyPyThreadState_LeaveTracing
-U _PyPyThreadState_New
-U _PyPyThreadState_SetAsyncExc
-U _PyPyThreadState_Swap
-U _PyPyThread_ReInitTLS
-U _PyPyThread_acquire_lock
+-U _PyPyThread_acquire_lock_timed
-U _PyPyThread_allocate_lock
-U _PyPyThread_create_key
-U _PyPyThread_delete_key
@@ -624,8 +744,10 @@
-U _PyPyType_GenericAlloc
-U _PyPyType_GenericNew
-U _PyPyType_GetModule
+-U _PyPyType_GetModuleByDef
-U _PyPyType_GetModuleState
-U _PyPyType_GetName
+-U _PyPyType_GetQualName
-U _PyPyType_GetSlot
-U _PyPyType_IsSubtype
-U _PyPyType_Modified
@@ -637,6 +759,7 @@
-U _PyPyUnicode_AsEncodedObject
-U _PyPyUnicode_AsEncodedString
-U _PyPyUnicode_AsLatin1String
+-U _PyPyUnicode_AsRawUnicodeEscapeString
-U _PyPyUnicode_AsUCS4
-U _PyPyUnicode_AsUCS4Copy
-U _PyPyUnicode_AsUTF16String
@@ -663,6 +786,7 @@
-U _PyPyUnicode_DecodeLatin1
-U _PyPyUnicode_DecodeLocale
-U _PyPyUnicode_DecodeLocaleAndSize
+-U _PyPyUnicode_DecodeRawUnicodeEscape
-U _PyPyUnicode_DecodeUTF16
-U _PyPyUnicode_DecodeUTF32
-U _PyPyUnicode_DecodeUTF8
@@ -729,6 +853,7 @@
-U _PyPy_FindMethod
-U _PyPy_FrozenFlag
-U _PyPy_GenericAlias
+-U _PyPy_GenericAliasType
-U _PyPy_GetProgramName
-U _PyPy_GetRecursionLimit
-U _PyPy_GetVersion
@@ -737,6 +862,7 @@
-U _PyPy_IncRef
-U _PyPy_InspectFlag
-U _PyPy_InteractiveFlag
+-U _PyPy_Is
-U _PyPy_IsInitialized
-U _PyPy_IsolatedFlag
-U _PyPy_LeaveRecursiveCall
@@ -768,8 +894,10 @@
-U _PyPy_UnbufferedStdioFlag
-U _PyPy_VaBuildValue
-U _PyPy_VerboseFlag
+-U _PyPy_Version
-U _PySlice_AdjustIndices
-U _PyState_FindModule
+-U _PyThreadState_GetInterpreter
-U _PyThread_tss_alloc
-U _PyThread_tss_create
-U _PyThread_tss_delete
@@ -794,10 +922,13 @@
-U __PyArg_UnpackStack
-U __PyArg_VaParseTupleAndKeywordsFast
-U __PyArg_VaParseTupleAndKeywordsFast_SizeT
+-U __PyDeadline_Get
+-U __PyDeadline_Init
-U __PyExc_ArithmeticError
-U __PyExc_AssertionError
-U __PyExc_AttributeError
-U __PyExc_BaseException
+-U __PyExc_BaseExceptionGroup
-U __PyExc_BlockingIOError
-U __PyExc_BrokenPipeError
-U __PyExc_BufferError
@@ -809,7 +940,9 @@
-U __PyExc_ConnectionResetError
-U __PyExc_DeprecationWarning
-U __PyExc_EOFError
+-U __PyExc_EncodingWarning
-U __PyExc_Exception
+-U __PyExc_ExceptionGroup
-U __PyExc_FileExistsError
-U __PyExc_FileNotFoundError
-U __PyExc_FloatingPointError
@@ -881,13 +1014,17 @@
-U __PyPyDict_HasOnlyStringKeys
-U __PyPyErr_FormatFromCause
-U __PyPyErr_WriteUnraisableMsg
+-U __PyPyEval_GetAsyncGenFinalizer
+-U __PyPyEval_GetAsyncGenFirstiter
-U __PyPyEval_SliceIndex
+-U __PyPyFloat_InitState
-U __PyPyFloat_Unpack4
-U __PyPyFloat_Unpack8
-U __PyPyImport_AcquireLock
-U __PyPyImport_ReleaseLock
-U __PyPyList_Extend
-U __PyPyLong_AsByteArrayO
+-U __PyPyLong_AsInt
-U __PyPyLong_FromByteArray
-U __PyPyLong_NumBits
-U __PyPyLong_Sign
@@ -909,19 +1046,35 @@
-U __PyPyPy_Malloc
-U __PyPySet_Next
-U __PyPySet_NextEntry
+-U __PyPyThreadState_GetDict
-U __PyPyThreadState_UncheckedGet
-U __PyPyTimeZone_FromTimeZone
-U __PyPyTime_FromTime
-U __PyPyTime_FromTimeAndFold
-U __PyPyTuple_Resize
-U __PyPyType_Lookup
+-U __PyPyType_Name
-U __PyPyUnicode_EQ
-U __PyPyUnicode_EqualToASCIIString
+-U __PyPyUnicode_IsAlpha
+-U __PyPyUnicode_IsDecimalDigit
+-U __PyPyUnicode_IsDigit
+-U __PyPyUnicode_IsLowercase
+-U __PyPyUnicode_IsNumeric
+-U __PyPyUnicode_IsPrintable
+-U __PyPyUnicode_IsTitlecase
+-U __PyPyUnicode_IsUppercase
-U __PyPyUnicode_Ready
+-U __PyPyUnicode_ToDecimalDigit
+-U __PyPyUnicode_ToDigit
+-U __PyPyUnicode_ToLowercase
+-U __PyPyUnicode_ToTitlecase
+-U __PyPyUnicode_ToUppercase
-U __PyPy_BuildValue_SizeT
-U __PyPy_Dealloc
-U __PyPy_EllipsisObject
-U __PyPy_FalseStruct
+-U __PyPy_FatalErrorFunc
-U __PyPy_HashDouble
-U __PyPy_HashPointer
-U __PyPy_IsFinalizing
@@ -940,26 +1093,43 @@
-U __PyPy_subtype_dealloc
-U __PyPy_tuple_dealloc
-U __PyPy_tuple_new
+-U __PyTime_Add
-U __PyTime_AsMicroseconds
-U __PyTime_AsMilliseconds
+-U __PyTime_AsNanoseconds
-U __PyTime_AsNanosecondsObject
-U __PyTime_AsSecondsDouble
+-U __PyTime_AsTimespec
+-U __PyTime_AsTimespec_clamp
-U __PyTime_AsTimeval
-U __PyTime_AsTimevalTime_t
+-U __PyTime_AsTimeval_clamp
-U __PyTime_AsTimeval_noraise
-U __PyTime_FromMillisecondsObject
-U __PyTime_FromNanoseconds
-U __PyTime_FromNanosecondsObject
-U __PyTime_FromSeconds
-U __PyTime_FromSecondsObject
+-U __PyTime_FromTimespec
+-U __PyTime_FromTimeval
-U __PyTime_GetMonotonicClock
-U __PyTime_GetMonotonicClockWithInfo
+-U __PyTime_GetPerfCounter
+-U __PyTime_GetPerfCounterWithInfo
-U __PyTime_GetSystemClock
-U __PyTime_GetSystemClockWithInfo
-U __PyTime_Init
+-U __PyTime_MulDiv
-U __PyTime_ObjectToTime_t
-U __PyTime_ObjectToTimespec
-U __PyTime_ObjectToTimeval
-U __PyTime_gmtime
-U __PyTime_localtime
-U __PyType_Name
+-U __PyUnicode_IsLinebreak
+-U __PyUnicode_IsWhitespace
+-U __PyUnicode_ToNumeric
+-U __Py_NewReference
+-U __Py_VaBuildStack
+-U __Py_VaBuildStack_SizeT
+-U __Py_ascii_whitespace
diff --git a/extern/nanobind/cmake/nanobind-config.cmake b/extern/nanobind/cmake/nanobind-config.cmake
index bc680e577..4baa3b7f9 100644
--- a/extern/nanobind/cmake/nanobind-config.cmake
+++ b/extern/nanobind/cmake/nanobind-config.cmake
@@ -4,6 +4,10 @@ if (NOT TARGET Python::Module)
message(FATAL_ERROR "You must invoke 'find_package(Python COMPONENTS Interpreter Development REQUIRED)' prior to including nanobind.")
endif()
+if (Python_VERSION VERSION_LESS "3.9")
+ message(FATAL_ERROR "nanobind requires Python 3.9 or newer (found Python ${Python_VERSION}).")
+endif()
+
# Determine the right suffix for ordinary and stable ABI extensions.
# We always need to know the extension
@@ -49,6 +53,14 @@ endif()
# Extract Python version and extensions (e.g. free-threaded build)
string(REGEX REPLACE "[^-]*-([^-]*)-.*" "\\1" NB_ABI "${NB_SOABI}")
+# Determine whether the interpreter was built without the GIL using the ABI tag
+# (free-threaded builds encode this using a trailing 't').
+set(NB_FREE_THREADED 0)
+
+if(NB_ABI MATCHES "[0-9]t")
+ set(NB_FREE_THREADED 1)
+endif()
+
# If either suffix is missing, call Python to compute it
if(NOT DEFINED NB_SUFFIX OR NOT DEFINED NB_SUFFIX_S)
# Query Python directly to get the right suffix.
@@ -79,9 +91,10 @@ if(NOT DEFINED NB_SUFFIX OR NOT DEFINED NB_SUFFIX_S)
endif()
# Stash these for later use
-set(NB_SUFFIX ${NB_SUFFIX} CACHE INTERNAL "")
-set(NB_SUFFIX_S ${NB_SUFFIX_S} CACHE INTERNAL "")
-set(NB_ABI ${NB_ABI} CACHE INTERNAL "")
+set(NB_SUFFIX ${NB_SUFFIX} CACHE INTERNAL "")
+set(NB_SUFFIX_S ${NB_SUFFIX_S} CACHE INTERNAL "")
+set(NB_ABI ${NB_ABI} CACHE INTERNAL "")
+set(NB_FREE_THREADED ${NB_FREE_THREADED} CACHE INTERNAL "")
get_filename_component(NB_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
get_filename_component(NB_DIR "${NB_DIR}" PATH)
@@ -189,13 +202,16 @@ function (nanobind_build_library TARGET_NAME)
${NB_DIR}/src/nb_ndarray.cpp
${NB_DIR}/src/nb_static_property.cpp
${NB_DIR}/src/nb_ft.h
- ${NB_DIR}/src/nb_ft.cpp
${NB_DIR}/src/common.cpp
${NB_DIR}/src/error.cpp
${NB_DIR}/src/trampoline.cpp
${NB_DIR}/src/implicit.cpp
)
+ if (NB_FREE_THREADED)
+ target_sources(${TARGET_NAME} PRIVATE ${NB_DIR}/src/nb_ft.cpp)
+ endif()
+
if (TARGET_TYPE STREQUAL "SHARED")
nanobind_link_options(${TARGET_NAME})
target_compile_definitions(${TARGET_NAME} PRIVATE -DNB_BUILD)
@@ -246,15 +262,19 @@ function (nanobind_build_library TARGET_NAME)
# However, if the directory _does_ exist, then the user is free to choose
# whether nanobind uses them (based on `NB_USE_SUBMODULE_DEPS`), with a
# preference to choose them if `NB_USE_SUBMODULE_DEPS` is not defined
- if (NOT IS_DIRECTORY ${NB_DIR}/ext/robin_map/include OR
- (DEFINED NB_USE_SUBMODULE_DEPS AND NOT NB_USE_SUBMODULE_DEPS))
+ if(IS_DIRECTORY ${NB_DIR}/ext/robin_map/include
+ AND (NOT DEFINED NB_USE_SUBMODULE_DEPS OR NB_USE_SUBMODULE_DEPS)
+ AND NOT TARGET tsl::robin_map)
+ add_library(tsl::robin_map INTERFACE IMPORTED)
+ set_target_properties(tsl::robin_map PROPERTIES
+ INTERFACE_INCLUDE_DIRECTORIES ${NB_DIR}/ext/robin_map/include)
+ endif()
+
+ if(NOT TARGET tsl::robin_map)
include(CMakeFindDependencyMacro)
- find_dependency(tsl-robin-map)
- target_link_libraries(${TARGET_NAME} PRIVATE tsl::robin_map)
- else()
- target_include_directories(${TARGET_NAME} PRIVATE
- ${NB_DIR}/ext/robin_map/include)
+ find_dependency(tsl-robin-map CONFIG REQUIRED)
endif()
+ target_link_libraries(${TARGET_NAME} PRIVATE tsl::robin_map)
target_include_directories(${TARGET_NAME} ${AS_SYSINCLUDE} PUBLIC
${Python_INCLUDE_DIRS}
@@ -352,7 +372,7 @@ function(nanobind_add_module name)
set(ARG_STABLE_ABI FALSE)
endif()
- if (NB_ABI MATCHES "t")
+ if (NB_ABI MATCHES "[0-9]t")
# Free-threaded Python interpreters don't support building a nanobind
# module that uses the stable ABI.
set(ARG_STABLE_ABI FALSE)
@@ -590,7 +610,7 @@ endfunction()
# ---------------------------------------------------------------------------
function (nanobind_add_stub name)
- cmake_parse_arguments(PARSE_ARGV 1 ARG "VERBOSE;INCLUDE_PRIVATE;EXCLUDE_DOCSTRINGS;INSTALL_TIME;RECURSIVE;EXCLUDE_FROM_ALL" "MODULE;COMPONENT;PATTERN_FILE;OUTPUT_PATH" "PYTHON_PATH;DEPENDS;MARKER_FILE;OUTPUT")
+ cmake_parse_arguments(PARSE_ARGV 1 ARG "VERBOSE;INCLUDE_PRIVATE;EXCLUDE_DOCSTRINGS;EXCLUDE_VALUES;INSTALL_TIME;RECURSIVE;EXCLUDE_FROM_ALL" "MODULE;COMPONENT;PATTERN_FILE;OUTPUT_PATH" "PYTHON_PATH;DEPENDS;MARKER_FILE;OUTPUT")
if (EXISTS ${NB_DIR}/src/stubgen.py)
set(NB_STUBGEN "${NB_DIR}/src/stubgen.py")
@@ -614,6 +634,10 @@ function (nanobind_add_stub name)
list(APPEND NB_STUBGEN_ARGS -D)
endif()
+ if (ARG_EXCLUDE_VALUES)
+ list(APPEND NB_STUBGEN_ARGS --exclude-values)
+ endif()
+
if (ARG_RECURSIVE)
list(APPEND NB_STUBGEN_ARGS -r)
endif()
diff --git a/extern/nanobind/docs/api_bazel.rst b/extern/nanobind/docs/api_bazel.rst
index 6aca46e88..4cd1bba9c 100644
--- a/extern/nanobind/docs/api_bazel.rst
+++ b/extern/nanobind/docs/api_bazel.rst
@@ -30,6 +30,9 @@ The main tool to build nanobind extensions is the ``nanobind_extension`` rule.
srcs = [],
copts = [],
deps = [],
+ dynamic_deps = [],
+ linkstatic = True,
+ nanobind_link_mode = "auto",
local_defines = [],
**kwargs):
@@ -42,6 +45,8 @@ The main tool to build nanobind extensions is the ``nanobind_extension`` rule.
different ABI domain, as described in the :ref:`FAQ `
section.
+ *New in nanobind-bazel v2.10.2: Added the "nanobind_link_mode" attribute.*
+
To generate typing stubs for an extension, you can use the ``nanobind_stubgen``
rule.
@@ -64,9 +69,9 @@ rule.
exclude_docstrings = False,
recursive = False):
- It generates a `py_binary `__
- rule with a corresponding runfiles distribution, which invokes nanobind's
- builtin stubgen script, outputs a stub file and, optionally,
+ It generates a `py_binary `__
+ rule with a corresponding runfiles distribution, which invokes nanobind's
+ builtin stubgen script, outputs a stub file and, optionally,
a typing marker file into ``output_directory`` (defaults to
the build output directory, commonly called "bindir" in Bazel terms).
@@ -92,6 +97,8 @@ To build a C++ library with nanobind as a dependency, use the
name,
copts = [],
deps = [],
+ linkstatic = False,
+ nanobind_link_mode = "auto",
**kwargs):
It corresponds directly to the builtin
@@ -99,6 +106,8 @@ To build a C++ library with nanobind as a dependency, use the
with all keyword arguments being directly forwarded to a ``cc_library``
target.
+ *New in nanobind-bazel v2.10.2: Added the "nanobind_link_mode" attribute.*
+
To build a C++ shared library with nanobind as a dependency, use the
``nanobind_shared_library`` rule.
@@ -134,12 +143,12 @@ To build a C++ static library containing nanobind, use the
def nanobind_static_library(name, deps, **kwargs):
It corresponds directly to the builtin
- `cc_static_library `__
+ `cc_static_library `__
rule, with all keyword arguments being directly
forwarded to a ``cc_static_library`` target.
NB: This macro requires Bazel 7.4.0 or greater to use, as well as setting the
- ``--experimental_cc_static_library`` flag for the build, since the
+ ``--experimental_cc_static_library`` flag for the build, since the
``cc_static_library`` rule is considered experimental.
*New in nanobind-bazel version 2.7.0.*
@@ -156,12 +165,17 @@ To build a C++ test target requiring nanobind, use the ``nanobind_test`` rule.
name,
copts = [],
deps = [],
+ dynamic_deps = [],
+ linkstatic = False,
+ nanobind_link_mode = "auto",
**kwargs):
It corresponds directly to the builtin
`cc_test `__ rule, with all
keyword arguments being directly forwarded to a ``cc_test`` target.
+ *New in nanobind-bazel v2.10.2: Added the "nanobind_link_mode" attribute.*
+
.. _flags-bazel:
Flags
@@ -181,5 +195,5 @@ following flag settings.
Build nanobind extensions against the stable ABI of the configured Python
version. Allowed values are ``"cp312"``, ``"cp313"``, and ``"cp314"``, which
- target the stable ABI starting from CPython 3.12, 3.13, or 3.14 respectively.
+ target the stable ABI starting from CPython 3.12, 3.13, or 3.14 respectively.
By default, all extensions are built without any ABI limitations.
diff --git a/extern/nanobind/docs/api_core.rst b/extern/nanobind/docs/api_core.rst
index 23048737d..3b1850644 100644
--- a/extern/nanobind/docs/api_core.rst
+++ b/extern/nanobind/docs/api_core.rst
@@ -2158,9 +2158,6 @@ declarations in generated :ref:`stubs `,
See the section on :ref:`creating generic types `
for an example.
- This feature is only supported on Python 3.9+. Nanobind will ignore
- the attribute in Python 3.8 builds.
-
.. cpp:struct:: template supplement
Indicate that ``sizeof(T)`` bytes of memory should be set aside to
@@ -2190,6 +2187,9 @@ declarations in generated :ref:`stubs `,
Declares a callback that will be invoked when a C++ instance is first
cast into a Python object.
+.. cpp:struct:: never_destruct
+
+ Disables destroying the instance.
.. _enum_binding_annotations:
diff --git a/extern/nanobind/docs/api_extra.rst b/extern/nanobind/docs/api_extra.rst
index 83b17e990..47f4a9e75 100644
--- a/extern/nanobind/docs/api_extra.rst
+++ b/extern/nanobind/docs/api_extra.rst
@@ -1108,6 +1108,11 @@ convert into an equivalent representation in one of the following frameworks:
Builtin Python ``memoryview`` for CPU-resident data.
+.. cpp:class:: array_api
+
+ An object that both implements the buffer protocol and also has the
+ ``__dlpack__`` and ``__dlpack_device__`` attributes.
+
Eigen convenience type aliases
------------------------------
@@ -1561,6 +1566,16 @@ include directive:
`__
(i.e., an instance of ``typing.TypeVarTuple``).
+.. cpp:function:: template object param_spec(Args&&... args)
+
+ Analogous to :cpp:func:`type_var`, create a `parameter specification variable
+ `__
+ (i.e., an instance of ``typing.ParamSpec``).
+
+ .. code-block:: cpp
+
+ m.attr("P") = nb::param_spec("P");
+
.. cpp:function:: object any_type()
Convenience wrapper, which returns ``typing.Any``.
diff --git a/extern/nanobind/docs/bazel.rst b/extern/nanobind/docs/bazel.rst
index 13d7b6de8..d820b360e 100644
--- a/extern/nanobind/docs/bazel.rst
+++ b/extern/nanobind/docs/bazel.rst
@@ -27,8 +27,8 @@ in your MODULE.bazel file:
# Place this in your MODULE.bazel file.
# The major version of nanobind-bazel is equal to the version
# of the internally used nanobind.
- # In this case, we are building bindings with nanobind v2.8.0.
- bazel_dep(name = "nanobind_bazel", version = "2.8.0")
+ # In this case, we are building bindings with nanobind v2.11.0.
+ bazel_dep(name = "nanobind_bazel", version = "2.11.0")
To instead use a development version from GitHub, you can declare the
dependency as a ``git_override()`` in your MODULE.bazel:
@@ -139,6 +139,26 @@ Naturally, since stub generation relies on the given shared object files, the
actual extensions are built in the process before invocation of the stub
generation script.
+Controlling shared vs. static library production
+------------------------------------------------
+
+You can control how nanobind is linked to your extensions and libraries with the
+``nanobind_link_mode`` attribute of the ``nanobind_extension``, ``nanobind_library``,
+and ``nanobind_test`` macros.
+
+Setting ``nanobind_link_mode = "static"`` will link nanobind statically, while
+``nanobind_link_mode = "shared"`` will request linkage against a shared ``libnanobind.so``.
+The default, ``nanobind_link_mode = "auto"`` , will set the linkage for nanobind automatically
+based on the value of the given ``linkstatic`` attribute (where ``True`` requests static linkage,
+while ``False`` requests dynamic linkage).
+
+.. note::
+
+ Linking ``nanobind_extension`` s dynamically on macOS can fail because of undefined libpython
+ symbols referenced in the extension's object files. In that case, you can supply a linker
+ response file by using the ``nb_library_linkopts`` function from ``@nanobind_bazel//:helpers.bzl``
+ when setting your extension's ``linkopts``.
+
Building extensions for free-threaded Python
--------------------------------------------
diff --git a/extern/nanobind/docs/building.rst b/extern/nanobind/docs/building.rst
index 346ef5f01..91e5479ed 100644
--- a/extern/nanobind/docs/building.rst
+++ b/extern/nanobind/docs/building.rst
@@ -20,7 +20,7 @@ Preliminaries
Begin by creating a new file named ``CMakeLists.txt`` in the root directory of
your project. It should start with the following lines that declare a project
name and tested CMake version range. The third line line searches for Python >=
-3.8 including the ``Development.Module`` component required by nanobind. The
+3.9 including the ``Development.Module`` component required by nanobind. The
name of this module changed across CMake versions, hence the additional
conditional check.
@@ -35,7 +35,7 @@ conditional check.
set(DEV_MODULE Development.Module)
endif()
- find_package(Python 3.8 COMPONENTS Interpreter ${DEV_MODULE} REQUIRED)
+ find_package(Python 3.9 COMPONENTS Interpreter ${DEV_MODULE} REQUIRED)
Add the following lines below. They configure CMake to perform an optimized
*release* build by default unless another build type is specified. Without this
diff --git a/extern/nanobind/docs/changelog.rst b/extern/nanobind/docs/changelog.rst
index 06193a956..44bdd8531 100644
--- a/extern/nanobind/docs/changelog.rst
+++ b/extern/nanobind/docs/changelog.rst
@@ -15,6 +15,187 @@ case, both modules must use the same nanobind ABI version, or they will be
isolated from each other. Releases that don't explicitly mention an ABI version
below inherit that of the preceding release.
+Version 2.11.0 (Jan 29, 2026)
+-----------------------------
+
+- This release improves binding performance using CPython's *adaptive
+ specializing interpreter* (`PEP 659 `__).
+ The speedups are automatic and require no changes to binding code:
+
+ .. list-table::
+ :header-rows: 1
+
+ * - Operation
+ - Speedup
+ - Requirements
+ * - Method calls
+ - **1.22x** faster
+ - Python 3.11+
+ * - Static attribute lookups
+ - **1.63x** faster
+ - Python 3.14+
+
+ This was achieved by making a number of nanobind-internal classes
+ (``nb_func``, ``nb_method``, ``nb_meta``, etc.) immutable, which allows
+ CPython to specialize generic ``LOAD_ATTR`` opcodes to faster type-specific
+ versions (``LOAD_ATTR_METHOD`` for method calls, ``LOAD_ATTR_CLASS`` for
+ static attribute lookups). (PR `#1257
+ `__).
+
+- Added the :cpp:class:`nb::never_destruct ` class binding
+ annotation to inform nanobind that it should not bind the destructor. (PR
+ `#1251 `__, commit `4ba51f
+ `__).
+
+- Argument annotations for ``std::optional``-typed arguments now implicitly
+ have the :cpp:func:`.none() ` annotation applied (i.e., no need to
+ additionally specify ``nb::arg("..").none()``). (PR `#1262
+ `__, commit `425ca1
+ `__).
+
+- Removed a redundant hash table type, reducing the size of libnanobind by
+ 2.5KiB. (commit `4d53cd
+ `__).
+
+- Added Python 3.12-3.14 symbols to linker scripts. (commit `36d4a6
+ `__).
+
+- Fixed a bug where ``call_guard`` could cause an extra copy of the return
+ value. (PR `#1249 `__).
+
+- Don't link ``nb_ft.cpp`` in non-free-threaded builds to avoid linker warnings
+ about empty compilation units. (PR `#1271
+ `__).
+
+- ABI version 18.
+
+- **Eigen type caster improvements**:
+
+ - Fixed conversion of size-zero vectors to ``Eigen::Map``/``Eigen::Ref`` on
+ NumPy 2.4. (PR `#1268 `__).
+
+ - Fixed move construction of dense Eigen arrays. (commit `cb90753
+ `__).
+
+- **Stub generation improvements**:
+
+ - Fixed *O(n²)* string concatenation performance issue.
+ (PR `#1275 `__).
+
+ - Fixed enumerations with entries named ``name`` or ``value``.
+ (issue `#1246 `__).
+
+ - Stubgen now preserves module-level docstrings. (commit `88771b
+ `__).
+
+ - Extended the skip list by two additional enum attributes.
+ (PR `#1255 `__).
+
+Version 2.10.2 (Dec 10, 2025)
+----------------------------
+
+- Fixes a regression that broke compilation on 32-bit architectures.
+ (PR `#1239 `__).
+
+Version 2.10.1 (Dec 8, 2025)
+----------------------------
+
+- Nanobind now officially supports the **MinGW-w64** and **Intel ICX**
+ compilers. (PR `#1188 `__).
+
+- Version 2.10 drops support for Python 3.8, which reached *End-Of-Life* in
+ October 2025. (PR `#1236 `__).
+
+- The new :cpp:class:`nb::array_api ` framework tag can be used to
+ create an nd-array wrapper object that supports both the Python buffer
+ protocol and the DLPack methods ``__dlpack__`` and ``__dlpack_device__``.
+
+ Furthermore, nanobind now supports importing/exporting tensors via the legacy
+ (unversioned) DLPack interface, as well a new versioned interface. The latter
+ provides a flag indicating whether an nd-array is read-only. (PR `#1175
+ `__).
+
+- Added ``bfloat`` to the nd-array import conversion code, fixing imports of
+ bfloat16 tensors. (PR `#1228
+ `__).
+
+- nanobind now uses per-module precomputed constants, particularly strings, to
+ avoid costs from creating these repeatedly. This improves the performance of
+ nd-array and enumeration casts. (PR `#1184
+ `__).
+
+- Fixed a segfault in garbage collection traversal of Python subclasses of
+ class bindings with :cpp:class:`nb::is_weak_referenceable
+ `. (PR `#1206
+ `__).
+
+- Fixed a potential reference leak in the ``std::array`` type caster. (commit
+ `bfacaf7
+ `__).
+
+- STL type casters now directly reject incorrectly sized inputs, which avoids
+ performance pitfalls when passing large arrays. (commit `edf5753
+ `__,
+ `dc35d69
+ `__).
+
+- Fixed ``__new__`` overloads with variadic positional arguments but no
+ variadic keyword arguments, which incorrectly prevented nullary calls. (PR
+ `#1172 `__).
+
+- Removed zero-length arrays to improve compiler compatibility. (PR `#1158
+ `__).
+
+- Fixed a data race related caused by writes to a bit-field in free-threaded
+ extension builds (PR `#1191
+ `__)
+
+- ABI version 17.
+
+- **Stub generation improvements**:
+
+ - Added a new ``--exclude-values`` flag that forces all values to be rendered
+ as ``...`` in stub files. (PR `#1185
+ `__).
+
+ - Added support for ``typing.ParamSpec`` in generated stubs.
+ (PR `#1194 `__).
+
+ - NumPy boolean arrays now use ``np.bool_`` dtype in generated stubs instead
+ of deprecated alternatives.
+ (commit `20fab93 `__).
+
+ - Auto-generated enum APIs are now excluded from stub files.
+ (PR `#1182 `__).
+
+ - Pattern files now support ``__prefix__`` and ``__suffix__`` patterns
+ within classes for further customization of class stubs.
+ (PR `#1235 `__).
+
+ - Various minor improvements to the stub generator.
+ (PR `#1179 `__).
+
+- Fixed a regression in 2.10.0 (yanked release) related to handling of the ``NB_USE_SUBMODULE_DEPS``
+ flag that could cause CMake build system failures (commit `06aaa3
+ `__).
+
+- Minor/miscellaneous fixes: PRs `#1157
+ `__, `#1186
+ `__, `#1193
+ `__, `#1198
+ `__, `#1212
+ `__, `#1218
+ `__, `#1223
+ `__, `#1225
+ `__, commit `cf289b
+ `__.
+
+
+Version 2.10.0 (Dec 8, 2025)
+----------------------------
+
+This release was yanked due to a regression.
+
Version 2.9.2 (Sep 4, 2025)
---------------------------
@@ -26,6 +207,8 @@ This is a patch release to fix an issue in the new recursive stub generation fea
submodules. However, the implemented submodule test was far too conservative
and interpreted any imported module (e.g. ``import os``) as a submodule. The
patch release fixes this.
+ (commit `a65e1b
+ `__).
Version 2.9.1 (Sep 4, 2025)
---------------------------
diff --git a/extern/nanobind/docs/classes.rst b/extern/nanobind/docs/classes.rst
index fb6fc8abb..c760d7540 100644
--- a/extern/nanobind/docs/classes.rst
+++ b/extern/nanobind/docs/classes.rst
@@ -1141,3 +1141,43 @@ Two limitations of :cpp:struct:`nb::new_ ` are worth noting:
just helps unpickling work. If your first :cpp:struct:`nb::new_ `
method is one that takes no arguments, then nanobind won't add its own,
and you'll have to deal with unpickling some other way.
+
+Preventing object destruction
+-----------------------------
+
+In rare cases, you may need to bind a class that should never be destructed
+by nanobind:
+
+.. code-block:: cpp
+
+ class Singleton {
+ public:
+ static Singleton &get_instance();
+ };
+
+You may use the :cpp:class:`nb::never_destruct ` annotation to
+make nanobind aware of this. This feature is particularly helpful when attempts
+to bind the destructor would fail with a compilation error (e.g., because this
+would require access to implementation details that are not available in the
+current compilation unit).
+
+.. code-block:: cpp
+
+ nb::class_(m, "Singleton", nb::never_destruct())
+ .def_static("get_instance", &Singleton::get_instance, nb::rv_policy::reference);
+
+.. warning::
+
+ Instance class marked with :cpp:class:`nb::never_destruct `
+ must be returned using the :cpp:enumerator:`reference
+ ` return value policy. Otherwise, nanobind will assume
+ ownership, which includes the requirement of destructing the object at
+ a later point.
+
+ Similarly, you must not bind constructors or copy constructors, as the
+ eventual garbage collection of constructed instances would require calling
+ the destructor.
+
+ nanobind will abort with a fatal error if it is ever put into a situation
+ where an object with the :cpp:class:`nb::never_destruct `
+ annotation must be destructed.
diff --git a/extern/nanobind/docs/exceptions.rst b/extern/nanobind/docs/exceptions.rst
index 3e97df67f..5b14276a1 100644
--- a/extern/nanobind/docs/exceptions.rst
+++ b/extern/nanobind/docs/exceptions.rst
@@ -38,6 +38,8 @@ that convert to specific Python exceptions as shown below:
- ``ValueError``
* - ``std::overflow_error``
- ``OverflowError``
+ * - ``std::runtime_error``
+ - ``RuntimeError``
* - :cpp:func:`nb::stop_iteration `
- ``StopIteration`` (used to implement custom iterator)
* - :cpp:func:`nb::index_error `
@@ -264,8 +266,8 @@ Should they throw or fail to catch any exceptions in their call graph,
the C++ runtime calls ``std::terminate()`` to abort immediately.
Similarly, Python exceptions raised in a class's ``__del__`` method do not
-propagate, but are logged by Python as an unraisable error. In Python 3.8+, a
-`system hook is triggered
+propagate, but are logged by Python as an unraisable error. A `system hook is
+triggered
`_
and an auditing event is logged.
diff --git a/extern/nanobind/docs/index.rst b/extern/nanobind/docs/index.rst
index f90ddea80..c715b8522 100644
--- a/extern/nanobind/docs/index.rst
+++ b/extern/nanobind/docs/index.rst
@@ -54,12 +54,13 @@ similar runtime performance).
nanobinds depends on
-- **Python 3.8+** or **PyPy 7.3.10+** (the *3.8* and *3.9* PyPy flavors are
+- **Python 3.9+** or **PyPy 7.3.10+** (the *3.9* and *3.10* PyPy flavors are
supported, though there are :ref:`some limitations `).
- **CMake 3.15+**.
-- **A C++17 compiler**: Clang 8+, GCC 8+, MSVC2019+, and the CUDA NVCC compiler
- are officially supported. Others (MinGW, Cygwin, Intel, ..) may work as well
- but will not receive support.
+- **A C++17 compiler**: Clang 8+, GCC 8+, MSVC2019+, MinGW-w64, Intel ICX
+ (the modern Clang-based Intel compiler), and the CUDA NVCC compiler are
+ officially supported. Others (Cygwin, older Intel compilers, ..) may work
+ as well but will not receive support.
.. only:: not latex
diff --git a/extern/nanobind/docs/ndarray.rst b/extern/nanobind/docs/ndarray.rst
index e62fcb373..f4362d7f7 100644
--- a/extern/nanobind/docs/ndarray.rst
+++ b/extern/nanobind/docs/ndarray.rst
@@ -275,12 +275,19 @@ desired Python type.
- :cpp:class:`nb::tensorflow `: create a ``tensorflow.python.framework.ops.EagerTensor``.
- :cpp:class:`nb::jax `: create a ``jaxlib.xla_extension.DeviceArray``.
- :cpp:class:`nb::cupy `: create a ``cupy.ndarray``.
+- :cpp:class:`nb::memview `: create a Python ``memoryview``.
+- :cpp:class:`nb::array_api `: create an object that supports the
+ Python buffer protocol (i.e., is accepted as an argument to ``memoryview()``)
+ and also has the DLPack attributes ``__dlpack__`` and ``__dlpack_device__``
+ (i.e., it is accepted as an argument to a framework's ``from_dlpack()``
+ function).
- No framework annotation. In this case, nanobind will create a raw Python
``dltensor`` `capsule `__
- representing the `DLPack `__ metadata.
+ representing the `DLPack `__ metadata of
+ a ``DLManagedTensor``.
This annotation also affects the auto-generated docstring of the function,
-which in this case becomes:
+which in this example's case becomes:
.. code-block:: python
@@ -458,6 +465,21 @@ interpreted as follows:
- :cpp:enumerator:`rv_policy::move` is unsupported and demoted to
:cpp:enumerator:`rv_policy::copy`.
+Note that when a copy is returned, the copy is made by the framework, not by
+nanobind itself.
+For example, ``numpy.array()`` is passed the keyword argument ``copy`` with
+value ``True``, or the PyTorch tensor's ``clone()`` method is immediately
+called to create the copy.
+This design has a couple of advantages.
+First, nanobind does not have a build-time dependency on the libraries and
+frameworks (NumPy, PyTorch, CUDA, etc.) that would otherwise be necessary
+to perform the copy.
+Second, frameworks have the opportunity to optimize how the copy is created.
+The copy is owned by the framework, so the framework can choose to use a custom
+memory allocator, over-align the data, etc. based on the nd-array's size,
+the specific CPU, GPU, or memory types detected, etc.
+
+
.. _ndarray-temporaries:
Returning temporaries
@@ -643,26 +665,80 @@ support inter-framework data exchange, custom array types should implement the
- `__dlpack__ `__ and
- `__dlpack_device__ `__
-methods. This is easy thanks to the nd-array integration in nanobind. An example is shown below:
+methods.
+These, as well as the buffer protocol, are implemented in the object returned
+by nanobind when specifying :cpp:class:`nb::array_api ` as the
+framework template parameter.
+For example:
+
+.. code-block:: cpp
+
+ class MyArray {
+ double* d;
+ public:
+ MyArray() { d = new double[5] { 0.0, 1.0, 2.0, 3.0, 4.0 }; }
+ ~MyArray() { delete[] d; }
+ double* data() const { return d; }
+ };
+
+ nb::class_(m, "MyArray")
+ .def(nb::init<>())
+ .def("array_api", [](const MyArray& self) {
+ return nb::ndarray(self.data(), {5});
+ }, nb::rv_policy::reference_internal);
+
+which can be used as follows:
+
+.. code-block:: pycon
+
+ >>> import my_extension
+ >>> ma = my_extension.MyArray()
+ >>> aa = ma.array_api()
+ >>> aa.__dlpack_device__()
+ (1, 0)
+ >>> import numpy as np
+ >>> x = np.from_dlpack(aa)
+ >>> x
+ array([0., 1., 2., 3., 4.])
+
+The DLPack methods can also be provided for the class itself, by implementing
+``__dlpack__()`` as a wrapper function.
+For example, by adding the following lines to the binding:
.. code-block:: cpp
- nb::class_(m, "MyArray")
- // ...
- .def("__dlpack__", [](nb::kwargs kwargs) {
- return nb::ndarray<>( /* ... */);
- })
- .def("__dlpack_device__", []() {
- return std::make_pair(nb::device::cpu::value, 0);
- });
+ .def("__dlpack__", [](nb::pointer_and_handle self,
+ nb::kwargs kwargs) {
+ using array_api_t = nb::ndarray;
+ nb::object aa = nb::cast(array_api_t(self.p->data(), {5}),
+ nb::rv_policy::reference_internal,
+ self.h);
+ return aa.attr("__dlpack__")(**kwargs);
+ })
+ .def("__dlpack_device__", [](nb::handle /*self*/) {
+ return std::make_pair(nb::device::cpu::value, 0);
+ })
-Returning a raw :cpp:class:`nb::ndarray ` without framework annotation
-will produce a DLPack capsule, which is what the interface expects.
+the class can be used as follows:
+
+.. code-block:: pycon
+
+ >>> import my_extension
+ >>> ma = my_extension.MyArray()
+ >>> ma.__dlpack_device__()
+ (1, 0)
+ >>> import numpy as np
+ >>> y = np.from_dlpack(ma)
+ >>> y
+ array([0., 1., 2., 3., 4.])
+
+
+The ``kwargs`` argument in the implementation of ``__dlpack__`` above can be
+used to support additional parameters (e.g., to allow the caller to request a
+copy). See
+`__dlpack__() `__
+in the Python array API standard for details.
-The ``kwargs`` argument can be used to provide additional parameters (for
-example to request a copy), please see the DLPack documentation for details.
-Note that nanobind does not yet implement the versioned DLPack protocol. The
-version number should be ignored for now.
Frequently asked questions
--------------------------
@@ -708,7 +784,3 @@ be more restrictive. Presently supported dtypes include signed/unsigned
integers, floating point values, complex numbers, and boolean values. Some
:ref:`nonstandard arithmetic types ` can be supported as
well.
-
-Nanobind can receive and return *read-only* arrays via the buffer protocol when
-exhanging data with NumPy. The DLPack interface currently ignores this
-annotation.
diff --git a/extern/nanobind/docs/packaging.rst b/extern/nanobind/docs/packaging.rst
index 95d949d48..b63c5e496 100644
--- a/extern/nanobind/docs/packaging.rst
+++ b/extern/nanobind/docs/packaging.rst
@@ -101,7 +101,7 @@ An example is shown below:
version = "0.0.1"
description = "A brief description of what this project does"
readme = "README.md"
- requires-python = ">=3.8"
+ requires-python = ">=3.9"
authors = [
{ name = "Your Name", email = "your.email@address.com" },
]
@@ -178,7 +178,7 @@ component that can be used to create `stable ABI
.. code-block:: cmake
# Try to import all Python components potentially needed by nanobind
- find_package(Python 3.8
+ find_package(Python 3.9
REQUIRED COMPONENTS Interpreter Development.Module
OPTIONAL_COMPONENTS Development.SABIModule)
@@ -316,7 +316,6 @@ block to remove incompatible configurations from the matrix:
.. code-block:: toml
- skip = ["cp38-*", "pp38-*"] # Skip CPython and PyPy 3.8
archs = ["auto64"] # Only target 64 bit architectures
The `cibuildwheel documentation
diff --git a/extern/nanobind/docs/porting.rst b/extern/nanobind/docs/porting.rst
index eb3b9061c..368d05db6 100644
--- a/extern/nanobind/docs/porting.rst
+++ b/extern/nanobind/docs/porting.rst
@@ -149,6 +149,11 @@ by always passing such objects across the Python/C++ boundary as
``std::shared_ptr`` rather than as ``T*``. See the :ref:`advanced section
on object ownership ` for more details.
+``py::nodelete`` was used with holders in pybind11 to prevent destroying
+instances. nanobind can automatically detect this in some cases, but to
+be certain you can use the :cpp:class:`never_destruct` when binding the
+class.
+
Custom constructors
-------------------
In pybind11, custom constructors (i.e. ones that do not already exist in the
diff --git a/extern/nanobind/docs/refleaks.rst b/extern/nanobind/docs/refleaks.rst
index 1624fa8d3..1ce5e950f 100644
--- a/extern/nanobind/docs/refleaks.rst
+++ b/extern/nanobind/docs/refleaks.rst
@@ -337,11 +337,9 @@ Here is an example of the required code for a ``Wrapper`` type:
struct Wrapper { std::shared_ptr value; };
int wrapper_tp_traverse(PyObject *self, visitproc visit, void *arg) {
- // On Python 3.9+, we must traverse the implicit dependency
- // of an object on its associated type object.
- #if PY_VERSION_HEX >= 0x03090000
- Py_VISIT(Py_TYPE(self));
- #endif
+ // We must traverse the implicit dependency of an object on its
+ // associated type object.
+ Py_VISIT(Py_TYPE(self));
// The tp_traverse method may be called after __new__ but before or during
// __init__, before the C++ constructor has been completed. We must not
@@ -454,28 +452,6 @@ how deal with them. For completeness, let's consider some other possibilities.
should be fixed in the responsible framework so that leak warnings aren't
cluttered with flukes and can be more broadly useful.
-- **Older Python versions**: Very old Python versions (e.g., 3.8) don't
- do a good job cleaning up global references when the interpreter shuts down.
- The following code may leak a reference if it is a top-level statement in a
- Python file or the REPL.
-
- .. code-block:: python
-
- a = my_ext.MyObject()
-
- Such a warning is benign and does not indicate an actual leak. It simply
- highlights a flaws in the interpreter shutdown logic of old Python versions.
- Wrap your code into a function to address this issue even on such versions:
-
- .. code-block:: python
-
- def run():
- a = my_ext.MyObject()
- # ...
-
- if __name__ == '__main__':
- run()
-
- **Exceptions**. Some exceptions such as ``AttributeError`` have been observed
to hold references, e.g. to the object which lacked the desired attribute. If
the last exception raised by the program references a nanobind instance, then
diff --git a/extern/nanobind/docs/typing.rst b/extern/nanobind/docs/typing.rst
index 92b5af55e..cb89d9be6 100644
--- a/extern/nanobind/docs/typing.rst
+++ b/extern/nanobind/docs/typing.rst
@@ -540,7 +540,7 @@ The program has the following command line options:
.. code-block:: text
usage: python -m nanobind.stubgen [-h] [-o FILE] [-O PATH] [-i PATH] [-m MODULE]
- [-r] [-M FILE] [-P] [-D] [-q]
+ [-r] [-M FILE] [-P] [-D] [--exclude-values] [-q]
Generate stubs for nanobind-based extensions.
@@ -559,6 +559,7 @@ The program has the following command line options:
-P, --include-private include private members (with single leading or
trailing underscore)
-D, --exclude-docstrings exclude docstrings from the generated stub
+ --exclude-values force the use of ... for values
-q, --quiet do not generate any output in the absence of failures
@@ -713,6 +714,6 @@ you may use the special ``\from`` escape code to import them:
def lookup(array: Array[T], index: Literal[0] = 0) -> _Opt[T]:
\doc
-You may also add free-form text the beginning or the end of the generated stub.
-To do so, add an entry that matches on ``module_name.__prefix__`` or
-``module_name.__suffix__``.
+You may also add free-form text the beginning or the end of the generated stub
+module or of a class. To do so, add an entry that matches on ``name.__prefix__``
+or ``name.__suffix__`` where ``name`` is the name of the module or class.
diff --git a/extern/nanobind/docs/why.rst b/extern/nanobind/docs/why.rst
index 5f58e2c6e..79e955c75 100644
--- a/extern/nanobind/docs/why.rst
+++ b/extern/nanobind/docs/why.rst
@@ -122,14 +122,12 @@ nanobind includes a number of quality-of-life improvements for developers:
- **Stable ABI**: nanobind can target Python's `stable ABI interface
`__ starting with Python 3.12.
- This means that extension modules will be compatible with future version of
- Python without having to compile separate binaries per interpreter. That
- vision is still relatively far out, however: it will require Python 3.12+ to
- be widely deployed.
+ This means that extension modules are compatible with later version of
+ Python without having to compile separate binaries per interpreter.
- **Stub generation**: nanobind ships with a custom :ref:`stub generator
- ` and CMake integration to automatically create high quality stubs as
- part of the build process. `Stubs
+ ` and CMake integration to automatically create high quality type
+ stubs as part of the build process. `Stubs
`__ make compiled
extension code compatible with visual autocomplete in editors like `Visual
Studio Code `__ and static type checkers like
@@ -140,9 +138,9 @@ nanobind includes a number of quality-of-life improvements for developers:
- **Smart pointers, ownership, etc.**: corner cases in pybind11 related to
smart/unique pointers and callbacks could lead to undefined behavior. A later
pybind11 redesign (``smart_holder``) was able to address these problems, but
- this came at the cost of further increased runtime overheads. The object
- ownership model of nanobind avoids this undefined behavior without penalizing
- runtime performance.
+ this came at the cost of further increased binary size and runtime overheads.
+ The object ownership model of nanobind avoids this undefined behavior without
+ penalizing performance.
- **Leak warnings**: When the Python interpreter shuts down, nanobind reports
instance, type, and function leaks related to bindings, which is useful for
@@ -178,42 +176,6 @@ Minor additions
The following lists minor-but-useful additions relative to pybind11.
-- **Finding Python objects associated with a C++ instance**: In addition to all
- of the return value policies supported by pybind11, nanobind provides one
- additional policy named :cpp:enumerator:`nb::rv_policy::none
- ` that *only* succeeds when the return value is already a
- known/registered Python object. In other words, this policy will never
- attempt to move, copy, or reference a C++ instance by constructing a new
- Python object.
-
- The new :cpp:func:`nb::find() ` function encapsulates this behavior. It
- resembles :cpp:func:`nb::cast() ` in the sense that it returns the
- Python object associated with a C++ instance. But while :cpp:func:`nb::cast()
- ` will create that Python object if it doesn't yet exist,
- :cpp:func:`nb::find() ` will return a ``nullptr`` object. This function
- is useful to interface with Python's :ref:`cyclic garbage collector
- `.
-
-- **Parameterized wrappers**: The :cpp:class:`nb::handle_t\ ` type
- behaves just like the :cpp:class:`nb::handle ` class and wraps a
- ``PyObject *`` pointer. However, when binding a function that takes such an
- argument, nanobind will only call the associated function overload when the
- underlying Python object wraps a C++ instance of type ``T``.
-
- Similarly, the :cpp:class:`nb::type_object_t\ ` type
- behaves just like the :cpp:class:`nb::type_object ` class and
- wraps a ``PyTypeObject *`` pointer. However, when binding a function that
- takes such an argument, nanobind will only call the associated function
- overload when the underlying Python type object is a subtype of the C++ type
- ``T``.
-
- Finally, the :cpp:class:`nb::typed\ ` annotation can
- parameterize any other type. The feature exists to improve the
- expressiveness of type signatures (e.g., to turn ``list`` into
- ``list[int]``). Note, however, that nanobind does not perform additional
- runtime checks in this case. Please see the section on :ref:`parameterizing
- generics ` for further details.
-
- **Signature overrides**: it may sometimes be necessary to tweak the
type signature of a class or function to provide richer type information to
static type checkers like `MyPy `__ or
@@ -240,6 +202,42 @@ The following lists minor-but-useful additions relative to pybind11.
` and :ref:`class signatures
` for further details.
+- **Parameterized wrappers**: The :cpp:class:`nb::handle_t\ ` type
+ behaves just like the :cpp:class:`nb::handle ` class and wraps a
+ ``PyObject *`` pointer. However, when binding a function that takes such an
+ argument, nanobind will only call the associated function overload when the
+ underlying Python object wraps a C++ instance of type ``T``.
+
+ Similarly, the :cpp:class:`nb::type_object_t\ ` type
+ behaves just like the :cpp:class:`nb::type_object ` class and
+ wraps a ``PyTypeObject *`` pointer. However, when binding a function that
+ takes such an argument, nanobind will only call the associated function
+ overload when the underlying Python type object is a subtype of the C++ type
+ ``T``.
+
+ Finally, the :cpp:class:`nb::typed\ ` annotation can
+ parameterize any other type. The feature exists to improve the
+ expressiveness of type signatures (e.g., to turn ``list`` into
+ ``list[int]``). Note, however, that nanobind does not perform additional
+ runtime checks in this case. Please see the section on :ref:`parameterizing
+ generics ` for further details.
+
+- **Finding Python objects associated with a C++ instance**: In addition to all
+ of the return value policies supported by pybind11, nanobind provides one
+ additional policy named :cpp:enumerator:`nb::rv_policy::none
+ ` that *only* succeeds when the return value is already a
+ known/registered Python object. In other words, this policy will never
+ attempt to move, copy, or reference a C++ instance by constructing a new
+ Python object.
+
+ The new :cpp:func:`nb::find() ` function encapsulates this behavior. It
+ resembles :cpp:func:`nb::cast() ` in the sense that it returns the
+ Python object associated with a C++ instance. But while :cpp:func:`nb::cast()
+ ` will create that Python object if it doesn't yet exist,
+ :cpp:func:`nb::find() ` will return a ``nullptr`` object. This function
+ is useful to interface with Python's :ref:`cyclic garbage collector
+ `.
+
TLDR
----
@@ -248,5 +246,8 @@ nanobind. Fixing all the long-standing issues in pybind11 (see above list)
would require a substantial redesign and years of careful work by a team of C++
metaprogramming experts. At the same time, changing anything in pybind11 is
extremely hard because of the large number of downstream users and their
-requirements on API/ABI stability. I personally don't have the time and
-energy to fix pybind11 and have moved my focus to this project.
+requirements on API/ABI stability. I personally don't have the time and energy
+to fix pybind11 and have moved my focus to this project. The `testimonials
+section
+` lists
+the experience of a number of large projects that made the switch.
diff --git a/extern/nanobind/ext/robin_map/.github/workflows/ci.yml b/extern/nanobind/ext/robin_map/.github/workflows/ci.yml
index 96b698a66..1f7c2657a 100644
--- a/extern/nanobind/ext/robin_map/.github/workflows/ci.yml
+++ b/extern/nanobind/ext/robin_map/.github/workflows/ci.yml
@@ -29,13 +29,13 @@ jobs:
}
- {
name: macos-x64-gcc,
- os: macos-13,
+ os: macos-latest,
cxx: g++,
cmake-build-type: Release
}
- {
name: macos-x64-clang,
- os: macos-13,
+ os: macos-latest,
cxx: clang++,
cmake-build-type: Release
}
@@ -55,32 +55,16 @@ jobs:
cmake-build-type: Debug
}
- {
- name: windows-x64-vs-2019,
- os: windows-2019,
- cmake-build-type: Release,
- cmake-generator: Visual Studio 16 2019,
- cmake-platform: x64,
- vcpkg-triplet: x64-windows-static-md
- }
- - {
- name: windows-x86-vs-2019,
- os: windows-2019,
- cmake-build-type: Release,
- cmake-generator: Visual Studio 16 2019,
- cmake-platform: Win32,
- vcpkg-triplet: x86-windows-static-md
- }
- - {
- name: windows-x64-vs-2022,
- os: windows-2022,
+ name: windows-x64-vs-2025,
+ os: windows-2025,
cmake-build-type: Release,
cmake-generator: Visual Studio 17 2022,
cmake-platform: x64,
vcpkg-triplet: x64-windows-static-md
}
- {
- name: windows-x86-vs-2022,
- os: windows-2022,
+ name: windows-x86-vs-2025,
+ os: windows-2025,
cmake-build-type: Release,
cmake-generator: Visual Studio 17 2022,
cmake-platform: Win32,
diff --git a/extern/nanobind/ext/robin_map/CMakeLists.txt b/extern/nanobind/ext/robin_map/CMakeLists.txt
index be1a3ff1d..f7659fd41 100644
--- a/extern/nanobind/ext/robin_map/CMakeLists.txt
+++ b/extern/nanobind/ext/robin_map/CMakeLists.txt
@@ -1,6 +1,6 @@
-cmake_minimum_required(VERSION 3.5)
+cmake_minimum_required(VERSION 3.10)
-project(tsl-robin-map VERSION 1.4.0 LANGUAGES CXX)
+project(tsl-robin-map VERSION 1.4.1 LANGUAGES CXX)
include(GNUInstallDirs)
diff --git a/extern/nanobind/ext/robin_map/include/tsl/robin_growth_policy.h b/extern/nanobind/ext/robin_map/include/tsl/robin_growth_policy.h
index 787e19eb4..9abba3be2 100644
--- a/extern/nanobind/ext/robin_map/include/tsl/robin_growth_policy.h
+++ b/extern/nanobind/ext/robin_map/include/tsl/robin_growth_policy.h
@@ -43,7 +43,7 @@
#define TSL_RH_VERSION_MINOR 4
// A change of the patch version indicates a bugfix without additional
// functionality
-#define TSL_RH_VERSION_PATCH 0
+#define TSL_RH_VERSION_PATCH 1
#ifdef TSL_DEBUG
#define tsl_rh_assert(expr) assert(expr)
diff --git a/extern/nanobind/ext/robin_map/tests/CMakeLists.txt b/extern/nanobind/ext/robin_map/tests/CMakeLists.txt
index 6d3bbcd09..c573f2e82 100644
--- a/extern/nanobind/ext/robin_map/tests/CMakeLists.txt
+++ b/extern/nanobind/ext/robin_map/tests/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.8)
+cmake_minimum_required(VERSION 3.10)
project(tsl_robin_map_tests)
diff --git a/extern/nanobind/include/nanobind/eigen/dense.h b/extern/nanobind/include/nanobind/eigen/dense.h
index b8224cb58..6570ff44c 100644
--- a/extern/nanobind/include/nanobind/eigen/dense.h
+++ b/extern/nanobind/include/nanobind/eigen/dense.h
@@ -182,7 +182,7 @@ struct type_caster &&
object owner;
if (policy == rv_policy::move) {
- T *temp = new T(std::move(v));
+ T *temp = new T((T&&) v);
owner = capsule(temp, [](void *p) noexcept { delete (T *) p; });
ptr = temp->data();
policy = rv_policy::reference;
@@ -324,6 +324,13 @@ struct type_caster,
if constexpr (IS == 0)
inner = 0;
+ // Starting from numpy 2.4, dl_tensors' stride field is *always* set (for ndim > 0).
+ // This also includes when shape=(0,0), when numpy reports the stride to be zero.
+ // This creates an incompatibility with Eigen compile-time vectors, which expect
+ // runtime and compile-time strides to be identical (e.g. for Eigen::VectorXi, equal to 1).
+ if (ndim_v == 1 && caster.value.shape(0) == 0)
+ inner = IS;
+
if constexpr (OS == 0)
outer = 0;
diff --git a/extern/nanobind/include/nanobind/intrusive/ref.h b/extern/nanobind/include/nanobind/intrusive/ref.h
index a6f78b8a0..9e3ef70f5 100644
--- a/extern/nanobind/include/nanobind/intrusive/ref.h
+++ b/extern/nanobind/include/nanobind/intrusive/ref.h
@@ -119,7 +119,7 @@ template class ref {
T *m_ptr = nullptr;
};
-// Registar a type caster for ``ref`` if nanobind was previously #included
+// Register a type caster for ``ref`` if nanobind was previously #included
#if defined(NB_VERSION_MAJOR)
NAMESPACE_BEGIN(detail)
template struct type_caster> {
diff --git a/extern/nanobind/include/nanobind/nanobind.h b/extern/nanobind/include/nanobind/nanobind.h
index 66d2014f3..12807690f 100644
--- a/extern/nanobind/include/nanobind/nanobind.h
+++ b/extern/nanobind/include/nanobind/nanobind.h
@@ -22,13 +22,14 @@
#endif
#define NB_VERSION_MAJOR 2
-#define NB_VERSION_MINOR 9
-#define NB_VERSION_PATCH 2
+#define NB_VERSION_MINOR 11
+#define NB_VERSION_PATCH 0
#define NB_VERSION_DEV 0 // A value > 0 indicates a development release
// Core C++ headers that nanobind depends on
#include
#include
+#include
#include
#include
#include
diff --git a/extern/nanobind/include/nanobind/nb_attr.h b/extern/nanobind/include/nanobind/nb_attr.h
index a69df8693..5ff5b706c 100644
--- a/extern/nanobind/include/nanobind/nb_attr.h
+++ b/extern/nanobind/include/nanobind/nb_attr.h
@@ -123,6 +123,7 @@ struct is_final {};
struct is_generic {};
struct kw_only {};
struct lock_self {};
+struct never_destruct {};
template struct keep_alive {};
template struct supplement {};
@@ -219,7 +220,7 @@ struct arg_data {
uint8_t flag;
};
-template struct func_data_prelim {
+struct func_data_prelim_base {
// A small amount of space to capture data used by the function/closure
void *capture[3];
@@ -245,12 +246,12 @@ template struct func_data_prelim {
/// for each of these.
uint16_t nargs;
- /// Number of paramters to the C++ function that may be filled from
- /// Python positional arguments without additional ceremony. nb::args and
- /// nb::kwargs parameters are not counted in this total, nor are any
- /// parameters after nb::args or after a nb::kw_only annotation.
- /// The parameters counted here may be either named (nb::arg("name"))
- /// or unnamed (nb::arg()). If unnamed, they are effectively positional-only.
+ /// Number of parameters to the C++ function that may be filled from
+ /// Python positional arguments without additional ceremony.
+ /// nb::args and nb::kwargs parameters are not counted in this total, nor
+ /// are any parameters after nb::args or after a nb::kw_only annotation.
+ /// The parameters counted here may be either named (nb::arg("name")) or
+ /// unnamed (nb::arg()). If unnamed, they are effectively positional-only.
/// nargs_pos is always <= nargs.
uint16_t nargs_pos;
@@ -259,42 +260,15 @@ template struct func_data_prelim {
const char *name;
const char *doc;
PyObject *scope;
+};
- // *WARNING*: nanobind regularly receives requests from users who run it
- // through Clang-Tidy, or who compile with increased warnings levels, like
- //
- // -Wpedantic, -Wcast-qual, -Wsign-conversion, etc.
- //
- // (i.e., beyond -Wall -Wextra and /W4 that are currently already used)
- //
- // Their next step is to open a big pull request needed to silence all of
- // the resulting messages. This comment is strategically placed here
- // because the zero-length array construction below will almost certainly
- // be flagged in this process.
- //
- // My policy on this is as follows: I am always happy to fix issues in the
- // codebase. However, many of the resulting change requests are in the
- // "ritual purification" category: things that cause churn, decrease
- // readability, and which don't fix actual problems. It's a never-ending
- // cycle because each new revision of such tooling adds further warnings
- // and purification rites.
- //
- // So just to be clear: I do not wish to pepper this codebase with
- // "const_cast" and #pragmas/comments to avoid warnings in external
- // tooling just so those users can have a "silent" build. I don't think it
- // is reasonable for them to impose their own style on this project.
- //
- // As a workaround it is likely possible to restrict the scope of style
- // checks to particular C++ namespaces or source code locations.
-#if defined(_MSC_VER)
- // MSVC doesn't support zero-length arrays
- arg_data args[Size == 0 ? 1 : Size];
-#else
- // GCC and Clang do.
+template struct func_data_prelim : func_data_prelim_base {
arg_data args[Size];
-#endif
};
+template<> struct func_data_prelim<0> : func_data_prelim_base {};
+
+
template
NB_INLINE void func_extra_apply(F &f, const name &name, size_t &) {
f.name = name.value;
@@ -354,7 +328,7 @@ NB_INLINE void func_extra_apply(F &f, const arg &a, size_t &index) {
flag |= (uint8_t) cast_flags::convert;
arg_data &arg = f.args[index];
- arg.flag = flag;
+ arg.flag |= flag;
arg.name = a.name_;
arg.signature = a.signature_;
arg.value = nullptr;
diff --git a/extern/nanobind/include/nanobind/nb_call.h b/extern/nanobind/include/nanobind/nb_call.h
index 64206f370..c849e0433 100644
--- a/extern/nanobind/include/nanobind/nb_call.h
+++ b/extern/nanobind/include/nanobind/nb_call.h
@@ -96,7 +96,7 @@ NB_INLINE void call_init(PyObject **args, PyObject *kwnames, size_t &nargs,
args[0] = nullptr; \
args_p = args + 1; \
} \
- nargs |= NB_VECTORCALL_ARGUMENTS_OFFSET; \
+ nargs |= PY_VECTORCALL_ARGUMENTS_OFFSET; \
return steal(obj_vectorcall(base, args_p, nargs, kwnames, method_call))
template
diff --git a/extern/nanobind/include/nanobind/nb_cast.h b/extern/nanobind/include/nanobind/nb_cast.h
index 8cf039dd6..1c26cbf30 100644
--- a/extern/nanobind/include/nanobind/nb_cast.h
+++ b/extern/nanobind/include/nanobind/nb_cast.h
@@ -349,21 +349,7 @@ template struct typed_base_name {
static constexpr auto Name = type_caster::Name;
};
-#if PY_VERSION_HEX < 0x03090000
-#define NB_TYPED_NAME_PYTHON38(type, name) \
- template <> struct typed_base_name { \
- static constexpr auto Name = detail::const_name(name); \
- };
-
-NB_TYPED_NAME_PYTHON38(nanobind::tuple, NB_TYPING_TUPLE)
-NB_TYPED_NAME_PYTHON38(list, NB_TYPING_LIST)
-NB_TYPED_NAME_PYTHON38(set, NB_TYPING_SET)
-NB_TYPED_NAME_PYTHON38(dict, NB_TYPING_DICT)
-NB_TYPED_NAME_PYTHON38(type_object, NB_TYPING_TYPE)
-#endif
-
-// Base case: typed renders as T[Ts...], with some adjustments to
-// T for older versions of Python (typing.List instead of list, for example)
+// Base case: typed renders as T[Ts...]
template struct typed_name {
static constexpr auto Name =
typed_base_name>::Name + const_name("[") +
@@ -385,7 +371,7 @@ template
struct typed_name {
using Ret = std::conditional_t, void_type, R>;
static constexpr auto Name =
- const_name(NB_TYPING_CALLABLE "[[") +
+ const_name("collections.abc.Callable[[") +
concat(make_caster::Name...) + const_name("], ") +
make_caster::Name + const_name("]");
};
@@ -394,7 +380,7 @@ template
struct typed_name {
using Ret = std::conditional_t, void_type, R>;
static constexpr auto Name =
- const_name(NB_TYPING_CALLABLE "[..., ") +
+ const_name("collections.abc.Callable[..., ") +
make_caster::Name + const_name("]");
};
diff --git a/extern/nanobind/include/nanobind/nb_class.h b/extern/nanobind/include/nanobind/nb_class.h
index 7733ab01f..40368b070 100644
--- a/extern/nanobind/include/nanobind/nb_class.h
+++ b/extern/nanobind/include/nanobind/nb_class.h
@@ -128,10 +128,8 @@ struct type_data {
};
void (*set_self_py)(void *, PyObject *) noexcept;
bool (*keep_shared_from_this_alive)(PyObject *) noexcept;
-#if defined(Py_LIMITED_API)
uint32_t dictoffset;
uint32_t weaklistoffset;
-#endif
};
/// Information about a type that is only relevant when it is being created
@@ -186,6 +184,10 @@ NB_INLINE void type_extra_apply(type_init_data & t, const sig &s) {
t.name = s.value;
}
+NB_INLINE void type_extra_apply(type_init_data &, never_destruct) {
+ // intentionally empty
+}
+
template
NB_INLINE void type_extra_apply(type_init_data &t, supplement) {
static_assert(std::is_trivially_default_constructible_v,
@@ -588,7 +590,9 @@ class class_ : public object {
}
}
- if constexpr (std::is_destructible_v) {
+ constexpr bool has_never_destruct = (std::is_same_v || ...);
+
+ if constexpr (std::is_destructible_v && !has_never_destruct) {
d.flags |= (uint32_t) detail::type_flags::is_destructible;
if constexpr (!std::is_trivially_destructible_v) {
diff --git a/extern/nanobind/include/nanobind/nb_defs.h b/extern/nanobind/include/nanobind/nb_defs.h
index 5dc1361bb..d3b50712a 100644
--- a/extern/nanobind/include/nanobind/nb_defs.h
+++ b/extern/nanobind/include/nanobind/nb_defs.h
@@ -28,6 +28,7 @@
# define NB_INLINE __forceinline
# define NB_NOINLINE __declspec(noinline)
# define NB_INLINE_LAMBDA
+# define NB_NOUNROLL
#else
# define NB_EXPORT __attribute__ ((visibility("default")))
# define NB_IMPORT NB_EXPORT
@@ -35,8 +36,14 @@
# define NB_NOINLINE __attribute__((noinline))
# if defined(__clang__)
# define NB_INLINE_LAMBDA __attribute__((always_inline))
+# define NB_NOUNROLL _Pragma("nounroll")
# else
# define NB_INLINE_LAMBDA
+# if defined(__GNUC__)
+# define NB_NOUNROLL _Pragma("GCC unroll 0")
+# else
+# define NB_NOUNROLL
+# endif
# endif
#endif
@@ -74,58 +81,12 @@
# define NB_HAS_U8STRING
#endif
-#if defined(Py_TPFLAGS_HAVE_VECTORCALL)
-# define NB_VECTORCALL PyObject_Vectorcall
-# define NB_HAVE_VECTORCALL Py_TPFLAGS_HAVE_VECTORCALL
-#elif defined(_Py_TPFLAGS_HAVE_VECTORCALL)
-# define NB_VECTORCALL _PyObject_Vectorcall
-# define NB_HAVE_VECTORCALL _Py_TPFLAGS_HAVE_VECTORCALL
-#else
-# define NB_HAVE_VECTORCALL (1UL << 11)
-#endif
-
-#if defined(PY_VECTORCALL_ARGUMENTS_OFFSET)
-# define NB_VECTORCALL_ARGUMENTS_OFFSET PY_VECTORCALL_ARGUMENTS_OFFSET
-# define NB_VECTORCALL_NARGS PyVectorcall_NARGS
-#else
-# define NB_VECTORCALL_ARGUMENTS_OFFSET ((size_t) 1 << (8 * sizeof(size_t) - 1))
-# define NB_VECTORCALL_NARGS(n) ((n) & ~NB_VECTORCALL_ARGUMENTS_OFFSET)
-#endif
-
-#if PY_VERSION_HEX < 0x03090000
-# define NB_TYPING_ABC "typing."
-# define NB_TYPING_TUPLE "typing.Tuple"
-# define NB_TYPING_LIST "typing.List"
-# define NB_TYPING_DICT "typing.Dict"
-# define NB_TYPING_SET "typing.Set"
-# define NB_TYPING_TYPE "typing.Type"
-#else
-# define NB_TYPING_ABC "collections.abc."
-# define NB_TYPING_TUPLE "tuple"
-# define NB_TYPING_LIST "list"
-# define NB_TYPING_DICT "dict"
-# define NB_TYPING_SET "set"
-# define NB_TYPING_TYPE "type"
-#endif
-
#if PY_VERSION_HEX < 0x030D0000
# define NB_TYPING_CAPSULE "typing_extensions.CapsuleType"
#else
# define NB_TYPING_CAPSULE "types.CapsuleType"
#endif
-#define NB_TYPING_SEQUENCE NB_TYPING_ABC "Sequence"
-#define NB_TYPING_MAPPING NB_TYPING_ABC "Mapping"
-#define NB_TYPING_CALLABLE NB_TYPING_ABC "Callable"
-#define NB_TYPING_ITERATOR NB_TYPING_ABC "Iterator"
-#define NB_TYPING_ITERABLE NB_TYPING_ABC "Iterable"
-
-#if PY_VERSION_HEX < 0x03090000
-# define NB_TYPING_ABSTRACT_SET "typing.AbstractSet"
-#else
-# define NB_TYPING_ABSTRACT_SET "collections.abc.Set"
-#endif
-
#if defined(Py_LIMITED_API)
# if PY_VERSION_HEX < 0x030C0000 || defined(PYPY_VERSION)
# error "nanobind can target Python's limited API, but this requires CPython >= 3.12"
@@ -179,6 +140,12 @@
# define NB_TYPE_GET_SLOT_IMPL 1
#endif
+#if defined(Py_LIMITED_API)
+# define NB_DYNAMIC_VERSION Py_Version
+#else
+# define NB_DYNAMIC_VERSION PY_VERSION_HEX
+#endif
+
#define NB_MODULE_SLOTS_0 { 0, nullptr }
#if PY_VERSION_HEX < 0x030C0000
@@ -202,12 +169,14 @@
X(const X &) = delete; \
X &operator=(const X &) = delete;
+#define NB_MOD_STATE_SIZE (12 * sizeof(PyObject*))
+
// Helper macros to ensure macro arguments are expanded before token pasting/stringification
#define NB_MODULE_IMPL(name, variable) NB_MODULE_IMPL2(name, variable)
#define NB_MODULE_IMPL2(name, variable) \
static void nanobind_##name##_exec_impl(nanobind::module_); \
static int nanobind_##name##_exec(PyObject *m) { \
- nanobind::detail::init(NB_DOMAIN_STR); \
+ nanobind::detail::nb_module_exec(NB_DOMAIN_STR, m); \
try { \
nanobind_##name##_exec_impl( \
nanobind::borrow(m)); \
@@ -227,8 +196,9 @@
NB_MODULE_SLOTS_2 \
}; \
static struct PyModuleDef nanobind_##name##_module = { \
- PyModuleDef_HEAD_INIT, #name, nullptr, 0, nullptr, \
- nanobind_##name##_slots, nullptr, nullptr, nullptr \
+ PyModuleDef_HEAD_INIT, #name, nullptr, NB_MOD_STATE_SIZE, nullptr, \
+ nanobind_##name##_slots, nanobind::detail::nb_module_traverse, \
+ nanobind::detail::nb_module_clear, nanobind::detail::nb_module_free \
}; \
extern "C" [[maybe_unused]] NB_EXPORT PyObject *PyInit_##name(void); \
extern "C" PyObject *PyInit_##name(void) { \
diff --git a/extern/nanobind/include/nanobind/nb_func.h b/extern/nanobind/include/nanobind/nb_func.h
index 10eb39945..5daab0dc2 100644
--- a/extern/nanobind/include/nanobind/nb_func.h
+++ b/extern/nanobind/include/nanobind/nb_func.h
@@ -75,7 +75,7 @@ NB_INLINE PyObject *func_create(Func &&func, Return (*)(Args...),
if constexpr (CheckGuard && !std::is_same_v) {
return func_create(
- [func = (forward_t) func](Args... args) NB_INLINE_LAMBDA {
+ [func = (forward_t) func](Args... args) NB_INLINE_LAMBDA -> Return {
typename Info::call_guard::type g;
(void) g;
return func((forward_t) args...);
@@ -95,6 +95,8 @@ NB_INLINE PyObject *func_create(Func &&func, Return (*)(Args...),
kwargs_pos_n = index_n_v, kwargs>...>,
nargs = sizeof...(Args);
+ constexpr bool has_arg_defaults = (detail::has_arg_defaults_v || ... || false);
+
// Determine the number of nb::arg/nb::arg_v annotations
constexpr size_t nargs_provided =
(std::is_base_of_v + ... + 0);
@@ -102,7 +104,7 @@ NB_INLINE PyObject *func_create(Func &&func, Return (*)(Args...),
(std::is_same_v + ... + 0) != 0;
constexpr bool is_getter_det =
(std::is_same_v + ... + 0) != 0;
- constexpr bool has_arg_annotations = nargs_provided > 0 && !is_getter_det;
+ constexpr bool has_arg_annotations = has_arg_defaults || (nargs_provided > 0 && !is_getter_det);
// Determine the number of potentially-locked function arguments
constexpr bool lock_self_det =
@@ -128,7 +130,7 @@ NB_INLINE PyObject *func_create(Func &&func, Return (*)(Args...),
// A few compile-time consistency checks
static_assert(args_pos_1 == args_pos_n && kwargs_pos_1 == kwargs_pos_n,
"Repeated use of nb::kwargs or nb::args in the function signature!");
- static_assert(!has_arg_annotations || nargs_provided + is_method_det == nargs,
+ static_assert(!has_arg_annotations || has_arg_defaults || nargs_provided + is_method_det == nargs,
"The number of nb::arg annotations must match the argument count!");
static_assert(kwargs_pos_1 == nargs || kwargs_pos_1 + 1 == nargs,
"nb::kwargs must be the last element of the function signature!");
@@ -188,7 +190,20 @@ NB_INLINE PyObject *func_create(Func &&func, Return (*)(Args...),
};
// The following temporary record will describe the function in detail
- func_data_prelim f;
+ func_data_prelim f;
+
+ // Initialize argument flags. The first branch turns std::optional<> types
+ // into implicit nb::none() anotations.
+ if constexpr (has_arg_defaults) {
+ size_t i = 0;
+ ((f.args[i++] = { nullptr, nullptr, nullptr, nullptr,
+ has_arg_defaults_v ? (uint8_t) cast_flags::accepts_none
+ : (uint8_t) 0 }), ...);
+ } else if constexpr (nargs_provided > 0) {
+ for (size_t i = 0; i < nargs_provided; ++i)
+ f.args[i].flag = 0;
+ }
+
f.flags = (args_pos_1 < nargs ? (uint32_t) func_flags::has_var_args : 0) |
(kwargs_pos_1 < nargs ? (uint32_t) func_flags::has_var_kwargs : 0) |
(ReturnRef ? (uint32_t) func_flags::return_ref : 0) |
@@ -311,7 +326,7 @@ NB_INLINE PyObject *func_create(Func &&func, Return (*)(Args...),
(void) arg_index;
- return nb_func_new((const void *) &f);
+ return nb_func_new(&f);
}
NAMESPACE_END(detail)
diff --git a/extern/nanobind/include/nanobind/nb_lib.h b/extern/nanobind/include/nanobind/nb_lib.h
index 2ae1c12ce..8fd35a1a2 100644
--- a/extern/nanobind/include/nanobind/nb_lib.h
+++ b/extern/nanobind/include/nanobind/nb_lib.h
@@ -9,8 +9,17 @@
NAMESPACE_BEGIN(NB_NAMESPACE)
+NAMESPACE_BEGIN(dlpack)
+
+// The version of DLPack that is supported by libnanobind
+static constexpr uint32_t major_version = 1;
+static constexpr uint32_t minor_version = 1;
+
// Forward declarations for types in ndarray.h (1)
-namespace dlpack { struct dltensor; struct dtype; }
+struct dltensor;
+struct dtype;
+
+NAMESPACE_END(dlpack)
NAMESPACE_BEGIN(detail)
@@ -107,7 +116,10 @@ NB_CORE void raise_next_overload_if_null(void *p);
// ========================================================================
-NB_CORE void init(const char *domain);
+NB_CORE void nb_module_exec(const char *domain, PyObject *m);
+NB_CORE int nb_module_traverse(PyObject *m, visitproc visit, void *arg);
+NB_CORE int nb_module_clear(PyObject *m);
+NB_CORE void nb_module_free(void *m);
// ========================================================================
@@ -273,8 +285,11 @@ NB_CORE PyObject *capsule_new(const void *ptr, const char *name,
// ========================================================================
+// Forward declaration for type in nb_attr.h
+struct func_data_prelim_base;
+
/// Create a Python function object for the given function record
-NB_CORE PyObject *nb_func_new(const void *data) noexcept;
+NB_CORE PyObject *nb_func_new(const func_data_prelim_base *f) noexcept;
// ========================================================================
@@ -452,9 +467,6 @@ NB_CORE PyObject *module_import(const char *name);
/// Try to import a Python extension module, raises an exception upon failure
NB_CORE PyObject *module_import(PyObject *name);
-/// Create a new extension module with the given name
-NB_CORE PyObject *module_new(const char *name, PyModuleDef *def) noexcept;
-
/// Create a submodule of an existing module
NB_CORE PyObject *module_new_submodule(PyObject *base, const char *name,
const char *doc) noexcept;
@@ -469,7 +481,7 @@ NB_CORE ndarray_handle *ndarray_import(PyObject *o,
cleanup_list *cleanup) noexcept;
// Describe a local ndarray object using a DLPack capsule
-NB_CORE ndarray_handle *ndarray_create(void *value, size_t ndim,
+NB_CORE ndarray_handle *ndarray_create(void *data, size_t ndim,
const size_t *shape, PyObject *owner,
const int64_t *strides,
dlpack::dtype dtype, bool ro,
diff --git a/extern/nanobind/include/nanobind/nb_python.h b/extern/nanobind/include/nanobind/nb_python.h
index 356500c70..54ee2f0bc 100644
--- a/extern/nanobind/include/nanobind/nb_python.h
+++ b/extern/nanobind/include/nanobind/nb_python.h
@@ -56,6 +56,6 @@
# pragma warning(pop)
#endif
-#if PY_VERSION_HEX < 0x03080000
-# error The nanobind library requires Python 3.8 (or newer)
+#if PY_VERSION_HEX < 0x03090000
+# error The nanobind library requires Python 3.9 (or newer)
#endif
diff --git a/extern/nanobind/include/nanobind/nb_traits.h b/extern/nanobind/include/nanobind/nb_traits.h
index 4480c868a..acfe5b58a 100644
--- a/extern/nanobind/include/nanobind/nb_traits.h
+++ b/extern/nanobind/include/nanobind/nb_traits.h
@@ -195,6 +195,12 @@ struct is_complex
inline constexpr bool is_complex_v = is_complex::value;
+template
+struct has_arg_defaults : std::false_type {};
+
+template
+constexpr bool has_arg_defaults_v = has_arg_defaults>::value;
+
NAMESPACE_END(detail)
template
diff --git a/extern/nanobind/include/nanobind/nb_types.h b/extern/nanobind/include/nanobind/nb_types.h
index 49a840564..a0e303ae8 100644
--- a/extern/nanobind/include/nanobind/nb_types.h
+++ b/extern/nanobind/include/nanobind/nb_types.h
@@ -162,6 +162,33 @@ template class api : public api_tag {
NAMESPACE_END(detail)
+// *WARNING*: nanobind regularly receives requests from users who run it
+// through Clang-Tidy, or who compile with increased warnings levels, like
+//
+// -Wcast-qual, -Wsign-conversion, etc.
+//
+// (i.e., beyond -Wall -Wextra and /W4 that are currently already used)
+//
+// Their next step is to open a big pull request needed to silence all of
+// the resulting messages. This comment is strategically placed here
+// because the (PyObject *) casts below cast away the const qualifier and
+// will almost certainly be flagged in this process.
+//
+// My policy on this is as follows: I am always happy to fix issues in the
+// codebase. However, many of the resulting change requests are in the
+// "ritual purification" category: things that cause churn, decrease
+// readability, and which don't fix actual problems. It's a never-ending
+// cycle because each new revision of such tooling adds further warnings
+// and purification rites.
+//
+// So just to be clear: I do not wish to pepper this codebase with
+// "const_cast" and #pragmas/comments to avoid warnings in external
+// tooling just so those users can have a "silent" build. I don't think it
+// is reasonable for them to impose their own style on this project.
+//
+// As a workaround it is likely possible to restrict the scope of style
+// checks to particular C++ namespaces or source code locations.
+
class handle : public detail::api {
friend class python_error;
friend struct detail::str_attr;
@@ -460,13 +487,8 @@ NAMESPACE_END(literals)
class bytearray : public object {
NB_OBJECT(bytearray, object, "bytearray", PyByteArray_Check)
-#if PY_VERSION_HEX >= 0x03090000
bytearray()
: object(PyObject_CallNoArgs((PyObject *)&PyByteArray_Type), detail::steal_t{}) { }
-#else
- bytearray()
- : object(PyObject_CallObject((PyObject *)&PyByteArray_Type, NULL), detail::steal_t{}) { }
-#endif
explicit bytearray(handle h)
: object(detail::bytearray_from_obj(h.ptr()), detail::steal_t{}) { }
@@ -604,11 +626,11 @@ class frozenset : public object {
};
class sequence : public object {
- NB_OBJECT_DEFAULT(sequence, object, NB_TYPING_SEQUENCE, PySequence_Check)
+ NB_OBJECT_DEFAULT(sequence, object, "collections.abc.Sequence", PySequence_Check)
};
class mapping : public object {
- NB_OBJECT_DEFAULT(mapping, object, NB_TYPING_MAPPING, PyMapping_Check)
+ NB_OBJECT_DEFAULT(mapping, object, "collections.abc.Mapping", PyMapping_Check)
list keys() const { return steal(detail::obj_op_1(m_ptr, PyMapping_Keys)); }
list values() const { return steal(detail::obj_op_1(m_ptr, PyMapping_Values)); }
list items() const { return steal(detail::obj_op_1(m_ptr, PyMapping_Items)); }
@@ -630,7 +652,7 @@ class iterator : public object {
using reference = const handle;
using pointer = const handle *;
- NB_OBJECT_DEFAULT(iterator, object, NB_TYPING_ITERATOR, PyIter_Check)
+ NB_OBJECT_DEFAULT(iterator, object, "collections.abc.Iterator", PyIter_Check)
iterator& operator++() {
m_value = steal(detail::obj_iter_next(m_ptr));
@@ -662,7 +684,7 @@ class iterator : public object {
class iterable : public object {
public:
- NB_OBJECT_DEFAULT(iterable, object, NB_TYPING_ITERABLE, detail::iterable_check)
+ NB_OBJECT_DEFAULT(iterable, object, "collections.abc.Iterable", detail::iterable_check)
};
/// Retrieve the Python type object associated with a C++ class
@@ -749,7 +771,7 @@ class not_implemented : public object {
class callable : public object {
public:
- NB_OBJECT(callable, object, NB_TYPING_CALLABLE, PyCallable_Check)
+ NB_OBJECT(callable, object, "collections.abc.Callable", PyCallable_Check)
using object::object;
};
@@ -793,7 +815,7 @@ struct fallback : public handle {
template class type_object_t : public type_object {
public:
- static constexpr auto Name = detail::const_name(NB_TYPING_TYPE "[") +
+ static constexpr auto Name = detail::const_name("type[") +
detail::make_caster::Name +
detail::const_name("]");
diff --git a/extern/nanobind/include/nanobind/ndarray.h b/extern/nanobind/include/nanobind/ndarray.h
index f71dc7e5f..63802963d 100644
--- a/extern/nanobind/include/nanobind/ndarray.h
+++ b/extern/nanobind/include/nanobind/ndarray.h
@@ -18,11 +18,16 @@
NAMESPACE_BEGIN(NB_NAMESPACE)
-/// dlpack API/ABI data structures are part of a separate namespace
+/// DLPack API/ABI data structures are part of a separate namespace.
NAMESPACE_BEGIN(dlpack)
enum class dtype_code : uint8_t {
- Int = 0, UInt = 1, Float = 2, Bfloat = 4, Complex = 5, Bool = 6
+ Int = 0, UInt = 1, Float = 2, Bfloat = 4, Complex = 5, Bool = 6,
+ Float8_E3M4 = 7, Float8_E4M3 = 8, Float8_E4M3B11FNUZ = 9,
+ Float8_E4M3FN = 10, Float8_E4M3FNUZ = 11, Float8_E5M2 = 12,
+ Float8_E5M2FNUZ = 13, Float8_E8M0FNU = 14,
+ Float6_E2M3FN = 15, Float6_E3M2FN = 16,
+ Float4_E2M1FN = 17
};
struct device {
@@ -86,6 +91,7 @@ NB_FRAMEWORK(tensorflow, 3, "tensorflow.python.framework.ops.EagerTensor");
NB_FRAMEWORK(jax, 4, "jaxlib.xla_extension.DeviceArray");
NB_FRAMEWORK(cupy, 5, "cupy.ndarray");
NB_FRAMEWORK(memview, 6, "memoryview");
+NB_FRAMEWORK(array_api, 7, "ArrayLike");
NAMESPACE_BEGIN(device)
NB_DEVICE(none, 0); NB_DEVICE(cpu, 1); NB_DEVICE(cuda, 2);
diff --git a/extern/nanobind/include/nanobind/stl/chrono.h b/extern/nanobind/include/nanobind/stl/chrono.h
index 75a4a6ea9..4bedf32e5 100644
--- a/extern/nanobind/include/nanobind/stl/chrono.h
+++ b/extern/nanobind/include/nanobind/stl/chrono.h
@@ -99,13 +99,8 @@ template class duration_caster {
return pack_timedelta(dd.count(), ss.count(), us.count());
}
- #if PY_VERSION_HEX < 0x03090000
- NB_TYPE_CASTER(type, io_name("typing.Union[datetime.timedelta, float]",
- "datetime.timedelta"))
- #else
- NB_TYPE_CASTER(type, io_name("datetime.timedelta | float",
- "datetime.timedelta"))
- #endif
+ NB_TYPE_CASTER(type, io_name("datetime.timedelta | float",
+ "datetime.timedelta"))
};
template
@@ -214,13 +209,8 @@ class type_caster>
localtime.tm_sec,
(int) us.count());
}
- #if PY_VERSION_HEX < 0x03090000
- NB_TYPE_CASTER(type, io_name("typing.Union[datetime.datetime, datetime.date, datetime.time]",
- "datetime.datetime"))
- #else
- NB_TYPE_CASTER(type, io_name("datetime.datetime | datetime.date | datetime.time",
- "datetime.datetime"))
- #endif
+ NB_TYPE_CASTER(type, io_name("datetime.datetime | datetime.date | datetime.time",
+ "datetime.datetime"))
};
// Other clocks that are not the system clock are not measured as
diff --git a/extern/nanobind/include/nanobind/stl/detail/nb_array.h b/extern/nanobind/include/nanobind/stl/detail/nb_array.h
index 728f9c56c..191a90dca 100644
--- a/extern/nanobind/include/nanobind/stl/detail/nb_array.h
+++ b/extern/nanobind/include/nanobind/stl/detail/nb_array.h
@@ -6,7 +6,7 @@ NAMESPACE_BEGIN(NB_NAMESPACE)
NAMESPACE_BEGIN(detail)
template struct array_caster {
- NB_TYPE_CASTER(Array, io_name(NB_TYPING_SEQUENCE, NB_TYPING_LIST) +
+ NB_TYPE_CASTER(Array, io_name("collections.abc.Sequence", "list") +
const_name("[") + make_caster::Name +
const_name("]"))
@@ -33,10 +33,10 @@ template struct array_caster {
value[i] = caster.operator cast_t();
}
-
- Py_XDECREF(temp);
}
+ Py_XDECREF(temp);
+
return success;
}
diff --git a/extern/nanobind/include/nanobind/stl/detail/nb_dict.h b/extern/nanobind/include/nanobind/stl/detail/nb_dict.h
index 24f77ea21..e38952876 100644
--- a/extern/nanobind/include/nanobind/stl/detail/nb_dict.h
+++ b/extern/nanobind/include/nanobind/stl/detail/nb_dict.h
@@ -15,7 +15,7 @@ NAMESPACE_BEGIN(NB_NAMESPACE)
NAMESPACE_BEGIN(detail)
template struct dict_caster {
- NB_TYPE_CASTER(Dict, io_name(NB_TYPING_MAPPING, NB_TYPING_DICT) +
+ NB_TYPE_CASTER(Dict, io_name("collections.abc.Mapping", "dict") +
const_name("[") + make_caster::Name +
const_name(", ") + make_caster::Name +
const_name("]"))
diff --git a/extern/nanobind/include/nanobind/stl/detail/nb_list.h b/extern/nanobind/include/nanobind/stl/detail/nb_list.h
index 5874f7d05..95823a3e1 100644
--- a/extern/nanobind/include/nanobind/stl/detail/nb_list.h
+++ b/extern/nanobind/include/nanobind/stl/detail/nb_list.h
@@ -15,7 +15,7 @@ NAMESPACE_BEGIN(NB_NAMESPACE)
NAMESPACE_BEGIN(detail)
template struct list_caster {
- NB_TYPE_CASTER(List, io_name(NB_TYPING_SEQUENCE, NB_TYPING_LIST) +
+ NB_TYPE_CASTER(List, io_name("collections.abc.Sequence", "list") +
const_name("[") + make_caster::Name +
const_name("]"))
diff --git a/extern/nanobind/include/nanobind/stl/detail/nb_set.h b/extern/nanobind/include/nanobind/stl/detail/nb_set.h
index d3a3250e4..0266531ba 100644
--- a/extern/nanobind/include/nanobind/stl/detail/nb_set.h
+++ b/extern/nanobind/include/nanobind/stl/detail/nb_set.h
@@ -15,7 +15,7 @@ NAMESPACE_BEGIN(NB_NAMESPACE)
NAMESPACE_BEGIN(detail)
template struct set_caster {
- NB_TYPE_CASTER(Set, io_name(NB_TYPING_ABSTRACT_SET, NB_TYPING_SET) +
+ NB_TYPE_CASTER(Set, io_name("collections.abc.Set", "set") +
const_name("[") + make_caster::Name +
const_name("]"))
diff --git a/extern/nanobind/include/nanobind/stl/filesystem.h b/extern/nanobind/include/nanobind/stl/filesystem.h
index 78af55092..bdc231457 100644
--- a/extern/nanobind/include/nanobind/stl/filesystem.h
+++ b/extern/nanobind/include/nanobind/stl/filesystem.h
@@ -71,11 +71,7 @@ struct type_caster {
return success;
}
-#if PY_VERSION_HEX < 0x03090000
- NB_TYPE_CASTER(std::filesystem::path, io_name("typing.Union[str, os.PathLike]", "pathlib.Path"))
-#else
NB_TYPE_CASTER(std::filesystem::path, io_name("str | os.PathLike", "pathlib.Path"))
-#endif
private:
static str to_py_str(const std::string &s) {
diff --git a/extern/nanobind/include/nanobind/stl/function.h b/extern/nanobind/include/nanobind/stl/function.h
index b96234028..6e61c56f8 100644
--- a/extern/nanobind/include/nanobind/stl/function.h
+++ b/extern/nanobind/include/nanobind/stl/function.h
@@ -50,7 +50,7 @@ struct type_caster> {
std::conditional_t, void_type, Return>>;
NB_TYPE_CASTER(std::function ,
- const_name(NB_TYPING_CALLABLE "[[") +
+ const_name("collections.abc.Callable[[") +
concat(make_caster::Name...) + const_name("], ") +
ReturnCaster::Name + const_name("]"))
diff --git a/extern/nanobind/include/nanobind/stl/optional.h b/extern/nanobind/include/nanobind/stl/optional.h
index 61ca923da..27eb81ade 100644
--- a/extern/nanobind/include/nanobind/stl/optional.h
+++ b/extern/nanobind/include/nanobind/stl/optional.h
@@ -23,5 +23,8 @@ struct type_caster> : optional_caster> {};
template <> struct type_caster : none_caster { };
+template
+struct has_arg_defaults> : std::true_type {};
+
NAMESPACE_END(detail)
NAMESPACE_END(NB_NAMESPACE)
diff --git a/extern/nanobind/include/nanobind/stl/pair.h b/extern/nanobind/include/nanobind/stl/pair.h
index 596b3d3a4..ebcbf4ed1 100644
--- a/extern/nanobind/include/nanobind/stl/pair.h
+++ b/extern/nanobind/include/nanobind/stl/pair.h
@@ -30,7 +30,7 @@ template struct type_caster> {
// Value name for docstring generation
static constexpr auto Name =
- const_name(NB_TYPING_TUPLE "[") + concat(Caster1::Name, Caster2::Name) + const_name("]");
+ const_name("tuple[") + concat(Caster1::Name, Caster2::Name) + const_name("]");
/// Python -> C++ caster, populates `caster1` and `caster2` upon success
bool from_python(handle src, uint8_t flags,
diff --git a/extern/nanobind/include/nanobind/stl/tuple.h b/extern/nanobind/include/nanobind/stl/tuple.h
index ed9960274..b4af58e82 100644
--- a/extern/nanobind/include/nanobind/stl/tuple.h
+++ b/extern/nanobind/include/nanobind/stl/tuple.h
@@ -23,7 +23,7 @@ template struct type_caster> {
using Indices = std::make_index_sequence;
static constexpr auto Name =
- const_name(NB_TYPING_TUPLE "[") +
+ const_name("tuple[") +
const_name(const_name("()"), concat(make_caster::Name...)) +
const_name("]");
diff --git a/extern/nanobind/include/nanobind/trampoline.h b/extern/nanobind/include/nanobind/trampoline.h
index 2789e5556..9b18f89b9 100644
--- a/extern/nanobind/include/nanobind/trampoline.h
+++ b/extern/nanobind/include/nanobind/trampoline.h
@@ -26,7 +26,7 @@ NB_CORE void trampoline_leave(ticket *ticket) noexcept;
template struct trampoline {
mutable void *data[2 * Size + 1];
- NB_INLINE trampoline(void *ptr) { trampoline_new(data, Size, ptr); }
+ NB_INLINE constexpr trampoline(void *ptr) { trampoline_new(data, Size, ptr); }
NB_INLINE ~trampoline() { trampoline_release(data, Size); }
NB_INLINE handle base() const { return (PyObject *) data[0]; }
diff --git a/extern/nanobind/include/nanobind/typing.h b/extern/nanobind/include/nanobind/typing.h
index ed5ab90d6..be1173713 100644
--- a/extern/nanobind/include/nanobind/typing.h
+++ b/extern/nanobind/include/nanobind/typing.h
@@ -28,4 +28,9 @@ object type_var_tuple(Args&&... args) {
return typing().attr("TypeVarTuple")((detail::forward_t) args...);
}
+template
+object param_spec(Args&&... args) {
+ return typing().attr("ParamSpec")((detail::forward_t) args...);
+}
+
NAMESPACE_END(NB_NAMESPACE)
diff --git a/extern/nanobind/pyproject.toml b/extern/nanobind/pyproject.toml
index 6430c649d..d711cbaf1 100644
--- a/extern/nanobind/pyproject.toml
+++ b/extern/nanobind/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"
[project]
name = "nanobind"
-version = "2.9.2"
+version = "2.11.0"
description = "nanobind: tiny and efficient C++/Python bindings"
readme.content-type = "text/markdown"
readme.text = """
diff --git a/extern/nanobind/src/__init__.py b/extern/nanobind/src/__init__.py
index 43b1e263b..0b2c021e7 100644
--- a/extern/nanobind/src/__init__.py
+++ b/extern/nanobind/src/__init__.py
@@ -1,8 +1,8 @@
import sys
import os
-if sys.version_info < (3, 8):
- raise ImportError("nanobind does not support Python < 3.8.")
+if sys.version_info < (3, 9):
+ raise ImportError("nanobind does not support Python < 3.9.")
def source_dir() -> str:
"Return the path to the nanobind source directory."
@@ -16,7 +16,7 @@ def cmake_dir() -> str:
"Return the path to the nanobind CMake module directory."
return os.path.join(os.path.abspath(os.path.dirname(__file__)), "cmake")
-__version__ = "2.9.2"
+__version__ = "2.11.0"
__all__ = (
"__version__",
diff --git a/extern/nanobind/src/common.cpp b/extern/nanobind/src/common.cpp
index ff394d56e..1a33cedcb 100644
--- a/extern/nanobind/src/common.cpp
+++ b/extern/nanobind/src/common.cpp
@@ -141,16 +141,6 @@ void cleanup_list::expand() noexcept {
// ========================================================================
-PyObject *module_new(const char *name, PyModuleDef *def) noexcept {
- memset(def, 0, sizeof(PyModuleDef));
- def->m_name = name;
- def->m_size = -1;
- PyObject *m = PyModule_Create(def);
-
- check(m, "nanobind::detail::module_new(): allocation failed!");
- return m;
-}
-
PyObject *module_import(const char *name) {
PyObject *res = PyImport_ImportModule(name);
if (!res)
@@ -287,7 +277,7 @@ PyObject *obj_vectorcall(PyObject *base, PyObject *const *args, size_t nargsf,
PyObject *res = nullptr;
bool gil_error = false, cast_error = false;
- size_t nargs_total = (size_t) (NB_VECTORCALL_NARGS(nargsf) +
+ size_t nargs_total = (size_t) (PyVectorcall_NARGS(nargsf) +
(kwnames ? NB_TUPLE_GET_SIZE(kwnames) : 0));
#if !defined(Py_LIMITED_API)
@@ -304,20 +294,8 @@ PyObject *obj_vectorcall(PyObject *base, PyObject *const *args, size_t nargsf,
}
}
-#if PY_VERSION_HEX < 0x03090000
- if (method_call) {
- PyObject *self = PyObject_GetAttr(args[0], /* name = */ base);
- if (self) {
- res = _PyObject_Vectorcall(self, (PyObject **) args + 1, nargsf - 1, kwnames);
- Py_DECREF(self);
- }
- } else {
- res = _PyObject_Vectorcall(base, (PyObject **) args, nargsf, kwnames);
- }
-#else
res = (method_call ? PyObject_VectorcallMethod
: PyObject_Vectorcall)(base, args, nargsf, kwnames);
-#endif
end:
for (size_t i = 0; i < nargs_total; ++i)
@@ -461,13 +439,23 @@ void setattr(PyObject *obj, PyObject *key, PyObject *value) {
}
void delattr(PyObject *obj, const char *key) {
+#if defined(Py_LIMITED_API) && PY_LIMITED_API < 0x030D0000
+ int rv = PyObject_SetAttrString(obj, key, nullptr);
+#else
int rv = PyObject_DelAttrString(obj, key);
+#endif
+
if (rv)
raise_python_error();
}
void delattr(PyObject *obj, PyObject *key) {
+#if defined(Py_LIMITED_API) && PY_LIMITED_API < 0x030D0000
+ int rv = PyObject_SetAttr(obj, key, nullptr);
+#else
int rv = PyObject_DelAttr(obj, key);
+#endif
+
if (rv)
raise_python_error();
}
@@ -766,7 +754,7 @@ PyObject **seq_get(PyObject *seq, size_t *size_out, PyObject **temp_out) noexcep
if (temp) {
size = (size_t) size_seq;
- } else if (!temp) {
+ } else {
PyErr_Clear();
for (Py_ssize_t i = 0; i < size_seq; ++i)
Py_DECREF(result[i]);
@@ -817,12 +805,17 @@ PyObject **seq_get_with_size(PyObject *seq, size_t size,
}
# endif
} else if (PySequence_Check(seq)) {
- temp = PySequence_Tuple(seq);
-
- if (temp)
- result = seq_get_with_size(temp, size, temp_out);
- else
- PyErr_Clear();
+ Py_ssize_t size_seq = PySequence_Size(seq);
+ if (size_seq != (Py_ssize_t) size) {
+ if (size_seq == -1)
+ PyErr_Clear();
+ } else {
+ temp = PySequence_Tuple(seq);
+ if (temp)
+ result = seq_get_with_size(temp, size, temp_out);
+ else
+ PyErr_Clear();
+ }
}
#else
/* There isn't a nice way to get a PyObject** in Py_LIMITED_API. This
diff --git a/extern/nanobind/src/error.cpp b/extern/nanobind/src/error.cpp
index 5d1d6666e..c45a39dd1 100644
--- a/extern/nanobind/src/error.cpp
+++ b/extern/nanobind/src/error.cpp
@@ -167,22 +167,13 @@ const char *python_error::what() const noexcept {
while (frame) {
frames.push_back(frame);
-#if PY_VERSION_HEX >= 0x03090000
frame = PyFrame_GetBack(frame);
-#else
- frame = frame->f_back;
- Py_XINCREF(frame);
-#endif
}
buf.put("Traceback (most recent call last):\n");
for (auto it = frames.rbegin(); it != frames.rend(); ++it) {
frame = *it;
-#if PY_VERSION_HEX >= 0x03090000
PyCodeObject *f_code = PyFrame_GetCode(frame);
-#else
- PyCodeObject *f_code = frame->f_code;
-#endif
buf.put(" File \"");
buf.put_dstr(borrow(f_code->co_filename).c_str());
buf.put("\", line ");
@@ -190,9 +181,7 @@ const char *python_error::what() const noexcept {
buf.put(", in ");
buf.put_dstr(borrow(f_code->co_name).c_str());
buf.put('\n');
-#if PY_VERSION_HEX >= 0x03090000
Py_DECREF(f_code);
-#endif
Py_DECREF(frame);
}
}
diff --git a/extern/nanobind/src/implicit.cpp b/extern/nanobind/src/implicit.cpp
index dff87bddb..10702a06a 100644
--- a/extern/nanobind/src/implicit.cpp
+++ b/extern/nanobind/src/implicit.cpp
@@ -15,13 +15,13 @@ NAMESPACE_BEGIN(detail)
void implicitly_convertible(const std::type_info *src,
const std::type_info *dst) noexcept {
- nb_internals *internals_ {internals};
- type_data *t {nb_type_c2p(internals_, dst)};
+ nb_internals *internals_ = internals;
+ type_data *t = nb_type_c2p(internals_, dst);
check(t, "nanobind::detail::implicitly_convertible(src=%s, dst=%s): "
"destination type unknown!", type_name(src), type_name(dst));
lock_internals guard(internals_);
- size_t size {0};
+ size_t size = 0;
if (t->flags & (uint32_t) type_flags::has_implicit_conversions) {
while (t->implicit.cpp && t->implicit.cpp[size])
@@ -45,13 +45,13 @@ void implicitly_convertible(const std::type_info *src,
void implicitly_convertible(bool (*predicate)(PyTypeObject *, PyObject *,
cleanup_list *),
const std::type_info *dst) noexcept {
- nb_internals *internals_ {internals};
- type_data *t {nb_type_c2p(internals_, dst)};
+ nb_internals *internals_ = internals;
+ type_data *t = nb_type_c2p(internals_, dst);
check(t, "nanobind::detail::implicitly_convertible(src=, dst=%s): "
"destination type unknown!", type_name(dst));
lock_internals guard(internals_);
- size_t size {0};
+ size_t size = 0;
if (t->flags & (uint32_t) type_flags::has_implicit_conversions) {
while (t->implicit.py && t->implicit.py[size])
diff --git a/extern/nanobind/src/nb_abi.h b/extern/nanobind/src/nb_abi.h
index da704d99f..8eccefa59 100644
--- a/extern/nanobind/src/nb_abi.h
+++ b/extern/nanobind/src/nb_abi.h
@@ -14,7 +14,7 @@
/// Tracks the version of nanobind's internal data structures
#ifndef NB_INTERNALS_VERSION
-# define NB_INTERNALS_VERSION 16
+# define NB_INTERNALS_VERSION 18
#endif
#if defined(__MINGW32__)
diff --git a/extern/nanobind/src/nb_combined.cpp b/extern/nanobind/src/nb_combined.cpp
index f565ce09f..6abb636a7 100644
--- a/extern/nanobind/src/nb_combined.cpp
+++ b/extern/nanobind/src/nb_combined.cpp
@@ -78,7 +78,9 @@
#include "nb_enum.cpp"
#include "nb_ndarray.cpp"
#include "nb_static_property.cpp"
-#include "nb_ft.cpp"
+#if defined(Py_GIL_DISABLED)
+# include "nb_ft.cpp"
+#endif
#include "error.cpp"
#include "common.cpp"
#include "implicit.cpp"
diff --git a/extern/nanobind/src/nb_enum.cpp b/extern/nanobind/src/nb_enum.cpp
index 427c0d85d..92e26e4f6 100644
--- a/extern/nanobind/src/nb_enum.cpp
+++ b/extern/nanobind/src/nb_enum.cpp
@@ -92,7 +92,7 @@ PyObject *enum_create(enum_init_data *ed) noexcept {
internals_->type_c2p_slow[ed->type] = t;
#if !defined(NB_FREE_THREADED)
- internals_->type_c2p_fast[ed->type] = t;
+ internals_->type_c2p_fast[(void *) ed->type] = t;
#endif
}
@@ -190,7 +190,8 @@ bool enum_from_python(const std::type_info *tp, PyObject *o, int64_t *out, uint8
return false;
if ((t->flags & (uint32_t) enum_flags::is_flag) != 0 && Py_TYPE(o) == t->type_py) {
- PyObject *value_o = PyObject_GetAttrString(o, "value");
+ PyObject *value_o =
+ PyObject_GetAttr(o, static_pyobjects[pyobj_name::value_str]);
if (value_o == nullptr) {
PyErr_Clear();
return false;
diff --git a/extern/nanobind/src/nb_func.cpp b/extern/nanobind/src/nb_func.cpp
index 915b2fca8..4d39f3ef5 100644
--- a/extern/nanobind/src/nb_func.cpp
+++ b/extern/nanobind/src/nb_func.cpp
@@ -197,15 +197,12 @@ char *strdup_check(const char *s) {
*
* This is an implementation detail of nanobind::cpp_function.
*/
-PyObject *nb_func_new(const void *in_) noexcept {
- func_data_prelim<0> *f = (func_data_prelim<0> *) in_;
- arg_data *args_in = std::launder((arg_data *) f->args);
-
+PyObject *nb_func_new(const func_data_prelim_base *f) noexcept {
bool has_scope = f->flags & (uint32_t) func_flags::has_scope,
has_name = f->flags & (uint32_t) func_flags::has_name,
has_args = f->flags & (uint32_t) func_flags::has_args,
- has_var_args = f->flags & (uint32_t) func_flags::has_var_kwargs,
- has_var_kwargs = f->flags & (uint32_t) func_flags::has_var_args,
+ has_var_args = f->flags & (uint32_t) func_flags::has_var_args,
+ has_var_kwargs = f->flags & (uint32_t) func_flags::has_var_kwargs,
can_mutate_args = f->flags & (uint32_t) func_flags::can_mutate_args,
has_doc = f->flags & (uint32_t) func_flags::has_doc,
has_signature = f->flags & (uint32_t) func_flags::has_signature,
@@ -217,6 +214,10 @@ PyObject *nb_func_new(const void *in_) noexcept {
is_new = false,
is_setstate = false;
+ arg_data *args_in = nullptr;
+ if (has_args)
+ args_in = std::launder((arg_data*) ((func_data_prelim<1>*) f)->args);
+
PyObject *name = nullptr;
PyObject *func_prev = nullptr;
@@ -274,12 +275,12 @@ PyObject *nb_func_new(const void *in_) noexcept {
// f->nargs = C++ argument count.
// f->descr_types = zero-terminated array of bound types among them.
// Hence of size >= 2 for constructors, where f->descr_types[1] my be null.
- // f->args = array of Python arguments (nb::arg). Non-empty if has_args.
+ // args_in = array of Python arguments (nb::arg). Non-empty if has_args.
// By contrast, fc->args below has size f->nargs.
if (is_constructor && f->nargs == 2 && f->descr_types[0] &&
f->descr_types[0] == f->descr_types[1]) {
if (has_args) {
- f->args[0].flag &= ~(uint8_t) cast_flags::convert;
+ args_in[0].flag &= ~(uint8_t) cast_flags::convert;
} else {
args_in = method_args + 1;
has_args = true;
@@ -298,7 +299,7 @@ PyObject *nb_func_new(const void *in_) noexcept {
// Check if the complex dispatch loop is needed
bool complex_call = can_mutate_args || has_var_kwargs || has_var_args ||
- f->nargs >= NB_MAXARGS_SIMPLE;
+ f->nargs > NB_MAXARGS_SIMPLE;
if (has_args) {
for (size_t i = is_method; i < f->nargs; ++i) {
@@ -362,7 +363,7 @@ PyObject *nb_func_new(const void *in_) noexcept {
#endif
func_data *fc = nb_func_data(func) + prev_overloads;
- memcpy(fc, f, sizeof(func_data_prelim<0>));
+ memcpy(fc, f, sizeof(func_data_prelim_base));
if (has_doc) {
if (fc->doc[0] == '\n')
fc->doc++;
@@ -605,7 +606,7 @@ static PyObject *nb_func_vectorcall_complex(PyObject *self,
size_t nargsf,
PyObject *kwargs_in) noexcept {
const size_t count = (size_t) Py_SIZE(self),
- nargs_in = (size_t) NB_VECTORCALL_NARGS(nargsf),
+ nargs_in = (size_t) PyVectorcall_NARGS(nargsf),
nkwargs_in = kwargs_in ? (size_t) NB_TUPLE_GET_SIZE(kwargs_in) : 0;
func_data *fr = nb_func_data(self);
@@ -689,16 +690,16 @@ static PyObject *nb_func_vectorcall_complex(PyObject *self,
entries using keyword arguments or default argument values provided
in the bindings, if available.
- 3. Ensure that either all keyword arguments were "consumed", or that
+ 2. Ensure that either all keyword arguments were "consumed", or that
the function takes a kwargs argument to accept unconsumed kwargs.
- 4. Any positional arguments still left get put into a tuple (for args),
+ 3. Any positional arguments still left get put into a tuple (for args),
and any leftover kwargs get put into a dict.
- 5. Pack everything into a vector; if we have nb::args or nb::kwargs, they are an
- extra tuple or dict at the end of the positional arguments.
+ 4. Pack everything into a vector; if we have nb::args or nb::kwargs,
+ they become a tuple or dict at the end of the positional arguments.
- 6. Call the function call dispatcher (func_data::impl)
+ 5. Call the function call dispatcher (func_data::impl)
If one of these fail, move on to the next overload and keep trying
until we get a result other than NB_NEXT_OVERLOAD.
@@ -877,7 +878,8 @@ static PyObject *nb_func_vectorcall_complex(PyObject *self,
return result;
}
-/// Simplified nb_func_vectorcall variant for functions w/o keyword arguments
+/// Simplified nb_func_vectorcall variant for functions w/o keyword arguments,
+/// w/o default arguments, with no more than NB_MAXARGS_SIMPLE arguments, etc.
static PyObject *nb_func_vectorcall_simple(PyObject *self,
PyObject *const *args_in,
size_t nargsf,
@@ -886,7 +888,7 @@ static PyObject *nb_func_vectorcall_simple(PyObject *self,
func_data *fr = nb_func_data(self);
const size_t count = (size_t) Py_SIZE(self),
- nargs_in = (size_t) NB_VECTORCALL_NARGS(nargsf);
+ nargs_in = (size_t) PyVectorcall_NARGS(nargsf);
const bool is_method = fr->flags & (uint32_t) func_flags::is_method,
is_constructor = fr->flags & (uint32_t) func_flags::is_constructor;
@@ -976,7 +978,7 @@ static PyObject *nb_func_vectorcall_simple_0(PyObject *self,
size_t nargsf,
PyObject *kwargs_in) noexcept {
func_data *fr = nb_func_data(self);
- const size_t nargs_in = (size_t) NB_VECTORCALL_NARGS(nargsf);
+ const size_t nargs_in = (size_t) PyVectorcall_NARGS(nargsf);
// Handler routine that will be invoked in case of an error condition
PyObject *(*error_handler)(PyObject *, PyObject *const *, size_t,
@@ -1016,7 +1018,7 @@ static PyObject *nb_func_vectorcall_simple_1(PyObject *self,
size_t nargsf,
PyObject *kwargs_in) noexcept {
func_data *fr = nb_func_data(self);
- const size_t nargs_in = (size_t) NB_VECTORCALL_NARGS(nargsf);
+ const size_t nargs_in = (size_t) PyVectorcall_NARGS(nargsf);
bool is_constructor = fr->flags & (uint32_t) func_flags::is_constructor;
// Handler routine that will be invoked in case of an error condition
@@ -1073,12 +1075,12 @@ static PyObject *nb_bound_method_vectorcall(PyObject *self,
size_t nargsf,
PyObject *kwargs_in) noexcept {
nb_bound_method *mb = (nb_bound_method *) self;
- size_t nargs = (size_t) NB_VECTORCALL_NARGS(nargsf);
+ size_t nargs = (size_t) PyVectorcall_NARGS(nargsf);
const size_t buf_size = 5;
PyObject **args, *args_buf[buf_size], *temp = nullptr, *result;
bool alloc = false;
- if (NB_LIKELY(nargsf & NB_VECTORCALL_ARGUMENTS_OFFSET)) {
+ if (NB_LIKELY(nargsf & PY_VECTORCALL_ARGUMENTS_OFFSET)) {
args = (PyObject **) (args_in - 1);
temp = args[0];
} else {
@@ -1095,7 +1097,8 @@ static PyObject *nb_bound_method_vectorcall(PyObject *self,
alloc = true;
}
- memcpy(args + 1, args_in, sizeof(PyObject *) * (size - 1));
+ if (size > 1)
+ memcpy(args + 1, args_in, sizeof(PyObject *) * (size - 1));
}
args[0] = mb->self;
@@ -1511,7 +1514,6 @@ PyObject *nb_func_get_doc(PyObject *self, void *) {
return PyUnicode_FromString(buf.get());
}
-// PyGetSetDef entry for __module__ is ignored in Python 3.8
PyObject *nb_func_getattro(PyObject *self, PyObject *name_) {
const char *name = PyUnicode_AsUTF8AndSize(name_, nullptr);
diff --git a/extern/nanobind/src/nb_internals.cpp b/extern/nanobind/src/nb_internals.cpp
index 4adf53004..497d4410f 100644
--- a/extern/nanobind/src/nb_internals.cpp
+++ b/extern/nanobind/src/nb_internals.cpp
@@ -32,11 +32,6 @@ extern int nb_bound_method_clear(PyObject *);
extern void nb_bound_method_dealloc(PyObject *);
extern PyObject *nb_method_descr_get(PyObject *, PyObject *, PyObject *);
-#if PY_VERSION_HEX >= 0x03090000
-# define NB_HAVE_VECTORCALL_PY39_OR_NEWER NB_HAVE_VECTORCALL
-#else
-# define NB_HAVE_VECTORCALL_PY39_OR_NEWER 0
-#endif
static PyType_Slot nb_meta_slots[] = {
{ Py_tp_base, nullptr },
@@ -47,7 +42,8 @@ static PyType_Spec nb_meta_spec = {
/* .name = */ "nanobind.nb_meta",
/* .basicsize = */ 0,
/* .itemsize = */ 0,
- /* .flags = */ Py_TPFLAGS_DEFAULT,
+ /* .flags = */ Py_TPFLAGS_DEFAULT |
+ NB_TPFLAGS_IMMUTABLETYPE,
/* .slots = */ nb_meta_slots
};
@@ -70,7 +66,6 @@ static PyType_Slot nb_func_slots[] = {
{ Py_tp_traverse, (void *) nb_func_traverse },
{ Py_tp_clear, (void *) nb_func_clear },
{ Py_tp_dealloc, (void *) nb_func_dealloc },
- { Py_tp_traverse, (void *) nb_func_traverse },
{ Py_tp_new, (void *) PyType_GenericNew },
{ Py_tp_call, (void *) PyVectorcall_Call },
{ 0, nullptr }
@@ -80,8 +75,10 @@ static PyType_Spec nb_func_spec = {
/* .name = */ "nanobind.nb_func",
/* .basicsize = */ (int) sizeof(nb_func),
/* .itemsize = */ (int) sizeof(func_data),
- /* .flags = */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
- NB_HAVE_VECTORCALL_PY39_OR_NEWER,
+ /* .flags = */ Py_TPFLAGS_DEFAULT |
+ Py_TPFLAGS_HAVE_GC |
+ Py_TPFLAGS_HAVE_VECTORCALL |
+ NB_TPFLAGS_IMMUTABLETYPE,
/* .slots = */ nb_func_slots
};
@@ -102,9 +99,11 @@ static PyType_Spec nb_method_spec = {
/*.name = */ "nanobind.nb_method",
/*.basicsize = */ (int) sizeof(nb_func),
/*.itemsize = */ (int) sizeof(func_data),
- /*.flags = */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
+ /*.flags = */ Py_TPFLAGS_DEFAULT |
+ Py_TPFLAGS_HAVE_GC |
Py_TPFLAGS_METHOD_DESCRIPTOR |
- NB_HAVE_VECTORCALL_PY39_OR_NEWER,
+ Py_TPFLAGS_HAVE_VECTORCALL |
+ NB_TPFLAGS_IMMUTABLETYPE,
/*.slots = */ nb_method_slots
};
@@ -124,7 +123,6 @@ static PyType_Slot nb_bound_method_slots[] = {
{ Py_tp_traverse, (void *) nb_bound_method_traverse },
{ Py_tp_clear, (void *) nb_bound_method_clear },
{ Py_tp_dealloc, (void *) nb_bound_method_dealloc },
- { Py_tp_traverse, (void *) nb_bound_method_traverse },
{ Py_tp_call, (void *) PyVectorcall_Call },
{ 0, nullptr }
};
@@ -133,8 +131,10 @@ static PyType_Spec nb_bound_method_spec = {
/* .name = */ "nanobind.nb_bound_method",
/* .basicsize = */ (int) sizeof(nb_bound_method),
/* .itemsize = */ 0,
- /* .flags = */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
- NB_HAVE_VECTORCALL_PY39_OR_NEWER,
+ /* .flags = */ Py_TPFLAGS_DEFAULT |
+ Py_TPFLAGS_HAVE_GC |
+ Py_TPFLAGS_HAVE_VECTORCALL |
+ NB_TPFLAGS_IMMUTABLETYPE,
/* .slots = */ nb_bound_method_slots
};
@@ -164,6 +164,73 @@ void default_exception_translator(const std::exception_ptr &p, void *) {
nb_internals *internals = nullptr;
PyTypeObject *nb_meta_cache = nullptr;
+
+static const char* interned_c_strs[pyobj_name::string_count] {
+ "value",
+ "copy",
+ "clone",
+ "array",
+ "from_dlpack",
+ "__dlpack__",
+ "max_version",
+ "dl_device",
+};
+
+PyObject **static_pyobjects = nullptr;
+
+static bool init_pyobjects(PyObject* m) {
+ PyObject** pyobjects = (PyObject**) PyModule_GetState(m);
+ if (!pyobjects)
+ return false;
+
+ NB_NOUNROLL
+ for (int i = 0; i < pyobj_name::string_count; ++i)
+ pyobjects[i] = PyUnicode_InternFromString(interned_c_strs[i]);
+
+ pyobjects[pyobj_name::copy_tpl] =
+ PyTuple_Pack(1, pyobjects[pyobj_name::copy_str]);
+ pyobjects[pyobj_name::max_version_tpl] =
+ PyTuple_Pack(1, pyobjects[pyobj_name::max_version_str]);
+
+ PyObject* one = PyLong_FromLong(1);
+ PyObject* zero = PyLong_FromLong(0);
+ pyobjects[pyobj_name::dl_cpu_tpl] = PyTuple_Pack(2, one, zero);
+ Py_DECREF(zero);
+ Py_DECREF(one);
+
+ PyObject* major = PyLong_FromLong(dlpack::major_version);
+ PyObject* minor = PyLong_FromLong(dlpack::minor_version);
+ pyobjects[pyobj_name::dl_version_tpl] = PyTuple_Pack(2, major, minor);
+ Py_DECREF(minor);
+ Py_DECREF(major);
+
+ static_pyobjects = pyobjects;
+
+ return true;
+}
+
+NB_NOINLINE int nb_module_traverse(PyObject *m, visitproc visit, void *arg) {
+ PyObject** pyobjects = (PyObject**) PyModule_GetState(m);
+ NB_NOUNROLL
+ for (int i = 0; i < pyobj_name::total_count; ++i)
+ Py_VISIT(pyobjects[i]);
+ return 0;
+}
+
+NB_NOINLINE int nb_module_clear(PyObject *m) {
+ PyObject** pyobjects = (PyObject**) PyModule_GetState(m);
+ NB_NOUNROLL
+ for (int i = 0; i < pyobj_name::total_count; ++i)
+ Py_CLEAR(pyobjects[i]);
+ return 0;
+}
+
+void nb_module_free(void *m) {
+ // Allow nanobind_##name##_exec to omit calling nb_module_clear on error.
+ (void) nb_module_clear((PyObject *) m);
+}
+
+
static bool is_alive_value = false;
static bool *is_alive_ptr = &is_alive_value;
bool is_alive() noexcept { return *is_alive_ptr; }
@@ -273,8 +340,8 @@ static void internals_cleanup() {
for (auto [f, p2] : p->funcs) {
fprintf(stderr, " - leaked function \"%s\"\n",
nb_func_data(f)->name);
+ INC_CTR;
if (ctr == 10) {
- INC_CTR;
fprintf(stderr, " - ... skipped remainder\n");
break;
}
@@ -317,29 +384,32 @@ static void internals_cleanup() {
#endif
}
-NB_NOINLINE void init(const char *name) {
+NB_NOINLINE void nb_module_exec(const char *name, PyObject *m) {
if (internals)
return;
+ check(init_pyobjects(m), "nanobind::detail::nb_module_exec(): "
+ "could not initialize module state!");
+
#if defined(PYPY_VERSION)
PyObject *dict = PyEval_GetBuiltins();
-#elif PY_VERSION_HEX < 0x03090000
- PyObject *dict = PyInterpreterState_GetDict(_PyInterpreterState_Get());
#else
PyObject *dict = PyInterpreterState_GetDict(PyInterpreterState_Get());
#endif
- check(dict, "nanobind::detail::init(): could not access internals dictionary!");
+ check(dict, "nanobind::detail::nb_module_exec(): "
+ "could not access internals dictionary!");
PyObject *key = PyUnicode_FromFormat("__nb_internals_%s_%s__",
abi_tag(), name ? name : "");
- check(key, "nanobind::detail::init(): could not create dictionary key!");
+ check(key, "nanobind::detail::nb_module_exec(): "
+ "could not create dictionary key!");
PyObject *capsule = dict_get_item_ref_or_fail(dict, key);
if (capsule) {
Py_DECREF(key);
internals = (nb_internals *) PyCapsule_GetPointer(capsule, "nb_internals");
- check(internals,
- "nanobind::detail::internals_fetch(): capsule pointer is NULL!");
+ check(internals, "nanobind::detail::nb_module_exec(): "
+ "capsule pointer is NULL!");
nb_meta_cache = internals->nb_meta;
is_alive_ptr = internals->is_alive_ptr;
Py_DECREF(capsule);
@@ -374,23 +444,9 @@ NB_NOINLINE void init(const char *name) {
PyThread_tss_create(p->nb_static_property_disabled);
#endif
- for (size_t i = 0; i < shard_count; ++i) {
- p->shards[i].keep_alive.min_load_factor(.1f);
- p->shards[i].inst_c2p.min_load_factor(.1f);
- }
-
check(p->nb_module && p->nb_meta && p->nb_type_dict && p->nb_func &&
p->nb_method && p->nb_bound_method,
- "nanobind::detail::init(): initialization failed!");
-
-#if PY_VERSION_HEX < 0x03090000
- p->nb_func->tp_flags |= NB_HAVE_VECTORCALL;
- p->nb_func->tp_vectorcall_offset = offsetof(nb_func, vectorcall);
- p->nb_method->tp_flags |= NB_HAVE_VECTORCALL;
- p->nb_method->tp_vectorcall_offset = offsetof(nb_func, vectorcall);
- p->nb_bound_method->tp_flags |= NB_HAVE_VECTORCALL;
- p->nb_bound_method->tp_vectorcall_offset = offsetof(nb_bound_method, vectorcall);
-#endif
+ "nanobind::detail::nb_module_exec(): initialization failed!");
#if defined(Py_LIMITED_API)
// Cache important functions from PyType_Type and PyProperty_Type
@@ -427,6 +483,7 @@ NB_NOINLINE void init(const char *name) {
#endif
p->translators = { default_exception_translator, nullptr, nullptr };
+
is_alive_value = true;
is_alive_ptr = &is_alive_value;
p->is_alive_ptr = is_alive_ptr;
@@ -476,7 +533,7 @@ NB_NOINLINE void init(const char *name) {
capsule = PyCapsule_New(p, "nb_internals", nullptr);
int rv = PyDict_SetItem(dict, key, capsule);
check(!rv && capsule,
- "nanobind::detail::init(): capsule creation failed!");
+ "nanobind::detail::nb_module_exec(): capsule creation failed!");
Py_DECREF(capsule);
Py_DECREF(key);
internals = p;
diff --git a/extern/nanobind/src/nb_internals.h b/extern/nanobind/src/nb_internals.h
index ca79920dd..a0f45f630 100644
--- a/extern/nanobind/src/nb_internals.h
+++ b/extern/nanobind/src/nb_internals.h
@@ -31,6 +31,12 @@
# define NB_THREAD_LOCAL __thread
#endif
+#if PY_VERSION_HEX >= 0x030A0000
+# define NB_TPFLAGS_IMMUTABLETYPE Py_TPFLAGS_IMMUTABLETYPE
+#else
+# define NB_TPFLAGS_IMMUTABLETYPE 0
+#endif
+
NAMESPACE_BEGIN(NB_NAMESPACE)
NAMESPACE_BEGIN(detail)
@@ -42,7 +48,7 @@ NAMESPACE_BEGIN(detail)
#endif
/// Nanobind function metadata (overloads, etc.)
-struct func_data : func_data_prelim<0> {
+struct func_data : func_data_prelim_base {
arg_data *args;
char *signature;
};
@@ -56,7 +62,7 @@ struct nb_inst { // usually: 24 bytes
/// State of the C++ object this instance points to: is it constructed?
/// can we use it?
- uint32_t state : 2;
+ uint8_t state : 2;
// Values for `state`. Note that the numeric values of these are relied upon
// for an optimization in `nb_type_get()`.
@@ -70,25 +76,27 @@ struct nb_inst { // usually: 24 bytes
* relative offset to a pointer that must be dereferenced to get to the
* instance data. 'direct' is 'true' in the former case.
*/
- uint32_t direct : 1;
+ uint8_t direct : 1;
/// Is the instance data co-located with the Python object?
- uint32_t internal : 1;
+ uint8_t internal : 1;
/// Should the destructor be called when this instance is GCed?
- uint32_t destruct : 1;
+ uint8_t destruct : 1;
/// Should nanobind call 'operator delete' when this instance is GCed?
- uint32_t cpp_delete : 1;
-
- /// Does this instance hold references to others? (via internals.keep_alive)
- uint32_t clear_keep_alive : 1;
+ uint8_t cpp_delete : 1;
/// Does this instance use intrusive reference counting?
- uint32_t intrusive : 1;
+ uint8_t intrusive : 1;
+
+ /// Does this instance hold references to others? (via internals.keep_alive)
+ /// This may be accessed concurrently to 'state', so it must not be in
+ /// the same bitfield as 'state'.
+ uint8_t clear_keep_alive;
// That's a lot of unused space. I wonder if there is a good use for it..
- uint32_t unused : 24;
+ uint16_t unused;
};
static_assert(sizeof(nb_inst) == sizeof(PyObject) + sizeof(uint32_t) * 2);
@@ -181,14 +189,14 @@ struct std_typeinfo_eq {
}
};
-using nb_type_map_fast = tsl::robin_map;
-using nb_type_map_slow = tsl::robin_map;
-
/// A simple pointer-to-pointer map that is reused a few times below (even if
/// not 100% ideal) to avoid template code generation bloat.
using nb_ptr_map = tsl::robin_map;
+using nb_type_map_fast = nb_ptr_map;
+using nb_type_map_slow = tsl::robin_map;
+
/// Convenience functions to deal with the pointer encoding in 'internals.inst_c2p'
/// Does this entry store a linked list of instances?
@@ -420,6 +428,32 @@ struct nb_internals {
size_t shard_count = 1;
};
+// Names for the PyObject* entries in the per-module state array.
+// These names are scoped, but will implicitly convert to int.
+struct pyobj_name {
+ enum : int {
+ value_str = 0, // string "value"
+ copy_str, // string "copy"
+ clone_str, // string "clone"
+ array_str, // string "array"
+ from_dlpack_str, // string "from_dlpack"
+ dunder_dlpack_str, // string "__dlpack__"
+ max_version_str, // string "max_version"
+ dl_device_str, // string "dl_device"
+ string_count,
+
+ copy_tpl = string_count, // tuple ("copy")
+ max_version_tpl, // tuple ("max_version")
+ dl_cpu_tpl, // tuple (1, 0), which corresponds to nb::device::cpu
+ dl_version_tpl, // tuple (dlpack::major_version, dlpack::minor_version)
+ total_count
+ };
+};
+
+static_assert(pyobj_name::total_count * sizeof(PyObject*) == NB_MOD_STATE_SIZE);
+
+extern PyObject **static_pyobjects;
+
/// Convenience macro to potentially access cached functions
#if defined(Py_LIMITED_API)
# define NB_SLOT(type, name) internals->type##_##name
@@ -466,10 +500,12 @@ inline void *inst_ptr(nb_inst *self) {
}
template struct scoped_pymalloc {
- scoped_pymalloc(size_t size = 1) {
- ptr = (T *) PyMem_Malloc(size * sizeof(T));
+ scoped_pymalloc(size_t size = 1, size_t extra_bytes = 0) {
+ // Tip: construct objects in the extra bytes using placement new.
+ ptr = (T *) PyMem_Malloc(size * sizeof(T) + extra_bytes);
if (!ptr)
- fail("scoped_pymalloc(): could not allocate %zu bytes of memory!", size);
+ fail("scoped_pymalloc(): could not allocate %llu bytes of memory!",
+ (unsigned long long) (size * sizeof(T) + extra_bytes));
}
~scoped_pymalloc() { PyMem_Free(ptr); }
T *release() {
diff --git a/extern/nanobind/src/nb_ndarray.cpp b/extern/nanobind/src/nb_ndarray.cpp
index d84177a8b..2f6d93a55 100644
--- a/extern/nanobind/src/nb_ndarray.cpp
+++ b/extern/nanobind/src/nb_ndarray.cpp
@@ -1,39 +1,139 @@
#include
#include
+#include
#include "nb_internals.h"
NAMESPACE_BEGIN(NB_NAMESPACE)
-NAMESPACE_BEGIN(detail)
+
+NAMESPACE_BEGIN(dlpack)
+
+/// Indicates the managed_dltensor_versioned is read only.
+static constexpr uint64_t flag_bitmask_read_only = 1UL << 0;
+
+struct version {
+ uint32_t major;
+ uint32_t minor;
+};
+
+NAMESPACE_END(dlpack)
// ========================================================================
+NAMESPACE_BEGIN(detail)
+
+// DLPack version 0, deprecated Feb 2024, obsoleted March 2025
struct managed_dltensor {
dlpack::dltensor dltensor;
void *manager_ctx;
void (*deleter)(managed_dltensor *);
};
-struct ndarray_handle {
- managed_dltensor *ndarray;
- std::atomic