From 1efb4810ea09d81a5984c9c481389abe44d58c68 Mon Sep 17 00:00:00 2001 From: ludmila Date: Fri, 9 Jan 2026 13:17:23 +0100 Subject: [PATCH 01/79] chore: major cleaning + init separating binding modules --- {src/.vs => .vs}/CMake Overview | 0 ...56ca19f5-7ec7-4164-887a-f5708baad850.vsidx | Bin ...8939f018-00c0-4566-b8c4-5c2231b3b9d4.vsidx | Bin ...cda6efeb-eccc-4afd-9506-518abeb485e9.vsidx | Bin {src/.vs => .vs}/Modul/v17/.wsuo | Bin {src/.vs => .vs}/Modul/v17/Browse.VC.db | Bin .../.vs => .vs}/Modul/v17/DocumentLayout.json | 0 {src/.vs => .vs}/ProjectSettings.json | 0 {src/.vs => .vs}/VSWorkspaceState.json | 0 {src/.vs => .vs}/slnx.sqlite | Bin {src/.vscode => .vscode}/settings.json | 0 CMakeLists.txt | 47 + ...eUserPresets.json => CMakeUserPresets.json | 0 src/conanfile.txt => conanfile.txt | 0 src/CMakeLists.txt | 47 - src/{python => PauliEngine}/__init__.py | 0 src/QubitHamiltonian.h | 12 - .../test_jax.py => bindings/CMakeLists.txt} | 0 src/bindings/bindings.cpp | 65 + src/nanobind/.gitignore | 43 - src/nanobind/.gitmodules | 3 - src/nanobind/.readthedocs.yaml | 18 - src/nanobind/CMakeLists.txt | 160 - src/nanobind/LICENSE | 26 - src/nanobind/README.md | 59 - src/nanobind/cmake/collect-symbols-pypy.py | 28 - src/nanobind/cmake/collect-symbols.py | 42 - src/nanobind/cmake/darwin-ld-cpython.sym | 930 ----- src/nanobind/cmake/darwin-ld-pypy.sym | 965 ----- src/nanobind/cmake/darwin-python-path.py | 15 - src/nanobind/cmake/nanobind-config.cmake | 681 ---- src/nanobind/docs/api_bazel.rst | 185 - src/nanobind/docs/api_cmake.rst | 491 --- src/nanobind/docs/api_core.rst | 3253 ----------------- src/nanobind/docs/api_extra.rst | 1562 -------- src/nanobind/docs/basics.rst | 481 --- src/nanobind/docs/bazel.rst | 198 - src/nanobind/docs/benchmark.rst | 174 - src/nanobind/docs/building.rst | 118 - src/nanobind/docs/changelog.rst | 1587 -------- src/nanobind/docs/classes.rst | 1143 ------ src/nanobind/docs/conf.py | 250 -- src/nanobind/docs/cppyy.h | 16 - src/nanobind/docs/eigen.rst | 180 - src/nanobind/docs/exceptions.rst | 291 -- src/nanobind/docs/exchanging.rst | 423 --- src/nanobind/docs/faq.rst | 398 -- src/nanobind/docs/free_threaded.rst | 326 -- src/nanobind/docs/functions.rst | 609 --- src/nanobind/docs/images/binding-dark.svg | 122 - src/nanobind/docs/images/binding-light.svg | 112 - src/nanobind/docs/images/caster-dark.svg | 118 - src/nanobind/docs/images/caster-light.svg | 108 - src/nanobind/docs/images/logo.jpg | Bin 426666 -> 0 bytes src/nanobind/docs/images/perf.svg | 1979 ---------- src/nanobind/docs/images/sizes.svg | 1909 ---------- src/nanobind/docs/images/times.svg | 1859 ---------- src/nanobind/docs/images/wrapper-dark.svg | 102 - src/nanobind/docs/images/wrapper-light.svg | 92 - src/nanobind/docs/index.rst | 147 - src/nanobind/docs/installing.rst | 48 - src/nanobind/docs/lowlevel.rst | 323 -- src/nanobind/docs/meson.rst | 116 - src/nanobind/docs/microbenchmark.ipynb | 447 --- src/nanobind/docs/ndarray.rst | 714 ---- src/nanobind/docs/ndarray_index.rst | 13 - src/nanobind/docs/ownership.rst | 400 -- src/nanobind/docs/ownership_adv.rst | 398 -- src/nanobind/docs/packaging.rst | 332 -- src/nanobind/docs/porting.rst | 360 -- src/nanobind/docs/pypy.rst | 23 - src/nanobind/docs/refleaks.rst | 485 --- src/nanobind/docs/release.rst | 25 - src/nanobind/docs/requirements.txt | 5 - src/nanobind/docs/typing.rst | 674 ---- src/nanobind/docs/utilities.rst | 59 - src/nanobind/docs/why.rst | 252 -- src/nanobind/ext/robin_map/.clang-format | 1 - src/nanobind/ext/robin_map/.codecov.yml | 5 - .../ext/robin_map/.github/workflows/ci.yml | 136 - src/nanobind/ext/robin_map/CMakeLists.txt | 87 - src/nanobind/ext/robin_map/LICENSE | 21 - src/nanobind/ext/robin_map/README.md | 521 --- .../cmake/tsl-robin-mapConfig.cmake.in | 9 - src/nanobind/ext/robin_map/doxygen.conf | 2483 ------------- .../include/tsl/robin_growth_policy.h | 415 --- .../ext/robin_map/include/tsl/robin_hash.h | 1589 -------- .../ext/robin_map/include/tsl/robin_map.h | 815 ----- .../ext/robin_map/include/tsl/robin_set.h | 668 ---- .../ext/robin_map/tests/CMakeLists.txt | 26 - .../tests/custom_allocator_tests.cpp | 138 - src/nanobind/ext/robin_map/tests/main.cpp | 26 - .../ext/robin_map/tests/policy_tests.cpp | 97 - .../ext/robin_map/tests/robin_map_tests.cpp | 1462 -------- .../ext/robin_map/tests/robin_set_tests.cpp | 174 - src/nanobind/ext/robin_map/tests/utils.h | 443 --- .../ext/robin_map/tsl-robin-map.natvis | 78 - src/nanobind/include/nanobind/eigen/dense.h | 474 --- src/nanobind/include/nanobind/eigen/sparse.h | 290 -- src/nanobind/include/nanobind/eval.h | 61 - .../include/nanobind/intrusive/counter.h | 261 -- .../include/nanobind/intrusive/counter.inl | 148 - src/nanobind/include/nanobind/intrusive/ref.h | 153 - src/nanobind/include/nanobind/make_iterator.h | 160 - src/nanobind/include/nanobind/nanobind.h | 61 - src/nanobind/include/nanobind/nb_accessor.h | 225 -- src/nanobind/include/nanobind/nb_attr.h | 471 --- src/nanobind/include/nanobind/nb_call.h | 152 - src/nanobind/include/nanobind/nb_cast.h | 764 ---- src/nanobind/include/nanobind/nb_class.h | 855 ----- src/nanobind/include/nanobind/nb_defs.h | 239 -- src/nanobind/include/nanobind/nb_descr.h | 155 - src/nanobind/include/nanobind/nb_enums.h | 26 - src/nanobind/include/nanobind/nb_error.h | 152 - src/nanobind/include/nanobind/nb_func.h | 414 --- src/nanobind/include/nanobind/nb_lib.h | 571 --- src/nanobind/include/nanobind/nb_misc.h | 125 - src/nanobind/include/nanobind/nb_python.h | 61 - src/nanobind/include/nanobind/nb_traits.h | 220 -- src/nanobind/include/nanobind/nb_tuple.h | 78 - src/nanobind/include/nanobind/nb_types.h | 984 ----- src/nanobind/include/nanobind/ndarray.h | 590 --- src/nanobind/include/nanobind/operators.h | 149 - src/nanobind/include/nanobind/stl/array.h | 22 - src/nanobind/include/nanobind/stl/bind_map.h | 181 - .../include/nanobind/stl/bind_vector.h | 225 -- src/nanobind/include/nanobind/stl/chrono.h | 239 -- src/nanobind/include/nanobind/stl/complex.h | 54 - .../include/nanobind/stl/detail/chrono.h | 299 -- .../include/nanobind/stl/detail/nb_array.h | 67 - .../include/nanobind/stl/detail/nb_dict.h | 95 - .../include/nanobind/stl/detail/nb_list.h | 83 - .../include/nanobind/stl/detail/nb_optional.h | 49 - .../include/nanobind/stl/detail/nb_set.h | 81 - .../include/nanobind/stl/detail/traits.h | 92 - .../include/nanobind/stl/filesystem.h | 93 - src/nanobind/include/nanobind/stl/function.h | 95 - src/nanobind/include/nanobind/stl/list.h | 22 - src/nanobind/include/nanobind/stl/map.h | 23 - src/nanobind/include/nanobind/stl/optional.h | 27 - src/nanobind/include/nanobind/stl/pair.h | 92 - src/nanobind/include/nanobind/stl/set.h | 24 - .../include/nanobind/stl/shared_ptr.h | 137 - src/nanobind/include/nanobind/stl/string.h | 39 - .../include/nanobind/stl/string_view.h | 39 - src/nanobind/include/nanobind/stl/tuple.h | 109 - .../include/nanobind/stl/unique_ptr.h | 178 - .../include/nanobind/stl/unordered_map.h | 23 - .../include/nanobind/stl/unordered_set.h | 24 - src/nanobind/include/nanobind/stl/variant.h | 113 - src/nanobind/include/nanobind/stl/vector.h | 22 - src/nanobind/include/nanobind/stl/wstring.h | 39 - src/nanobind/include/nanobind/trampoline.h | 76 - src/nanobind/include/nanobind/typing.h | 31 - src/nanobind/pyproject.toml | 58 - src/nanobind/src/__init__.py | 26 - src/nanobind/src/__main__.py | 36 - src/nanobind/src/buffer.h | 166 - src/nanobind/src/common.cpp | 1293 ------- src/nanobind/src/error.cpp | 325 -- src/nanobind/src/hash.h | 33 - src/nanobind/src/implicit.cpp | 75 - src/nanobind/src/nb_abi.h | 102 - src/nanobind/src/nb_combined.cpp | 85 - src/nanobind/src/nb_enum.cpp | 303 -- src/nanobind/src/nb_ft.cpp | 56 - src/nanobind/src/nb_ft.h | 39 - src/nanobind/src/nb_func.cpp | 1581 -------- src/nanobind/src/nb_internals.cpp | 498 --- src/nanobind/src/nb_internals.h | 519 --- src/nanobind/src/nb_ndarray.cpp | 827 ----- src/nanobind/src/nb_static_property.cpp | 76 - src/nanobind/src/nb_type.cpp | 2283 ------------ src/nanobind/src/stubgen.py | 1444 -------- src/nanobind/src/trampoline.cpp | 188 - src/nanobind/src/version.py | 110 - src/nanobind/tests/CMakeLists.txt | 209 -- src/nanobind/tests/common.py | 42 - src/nanobind/tests/conftest.py | 6 - src/nanobind/tests/inter_module.cpp | 9 - src/nanobind/tests/inter_module.h | 14 - src/nanobind/tests/object_py.h | 28 - src/nanobind/tests/pattern_file.nb | 19 - src/nanobind/tests/py_stub_test.py | 68 - src/nanobind/tests/py_stub_test.pyi.ref | 48 - src/nanobind/tests/test_callbacks.cpp | 137 - src/nanobind/tests/test_callbacks.py | 58 - src/nanobind/tests/test_chrono.cpp | 93 - src/nanobind/tests/test_chrono.py | 340 -- src/nanobind/tests/test_classes.cpp | 734 ---- src/nanobind/tests/test_classes.py | 943 ----- src/nanobind/tests/test_classes_ext.pyi.ref | 362 -- src/nanobind/tests/test_eigen.cpp | 281 -- src/nanobind/tests/test_eigen.py | 468 --- src/nanobind/tests/test_enum.cpp | 70 - src/nanobind/tests/test_enum.py | 186 - src/nanobind/tests/test_enum_ext.pyi.ref | 107 - src/nanobind/tests/test_eval.cpp | 87 - src/nanobind/tests/test_eval.py | 44 - src/nanobind/tests/test_exception.cpp | 62 - src/nanobind/tests/test_exception.py | 104 - src/nanobind/tests/test_functions.cpp | 507 --- src/nanobind/tests/test_functions.py | 791 ---- src/nanobind/tests/test_functions_ext.pyi.ref | 261 -- src/nanobind/tests/test_holders.cpp | 261 -- src/nanobind/tests/test_holders.py | 449 --- src/nanobind/tests/test_inter_module.py | 13 - src/nanobind/tests/test_inter_module_1.cpp | 8 - src/nanobind/tests/test_inter_module_2.cpp | 9 - src/nanobind/tests/test_intrusive.cpp | 66 - src/nanobind/tests/test_intrusive.py | 58 - src/nanobind/tests/test_intrusive_impl.cpp | 1 - src/nanobind/tests/test_issue.cpp | 68 - src/nanobind/tests/test_issue.py | 37 - src/nanobind/tests/test_jax.cpp | 22 - src/nanobind/tests/test_jax_ext.pyi.ref | 4 - src/nanobind/tests/test_make_iterator.cpp | 102 - src/nanobind/tests/test_make_iterator.py | 48 - .../tests/test_make_iterator_ext.pyi.ref | 33 - src/nanobind/tests/test_ndarray.cpp | 497 --- src/nanobind/tests/test_ndarray.py | 977 ----- src/nanobind/tests/test_ndarray_ext.pyi.ref | 196 - src/nanobind/tests/test_stl.cpp | 531 --- src/nanobind/tests/test_stl.py | 884 ----- src/nanobind/tests/test_stl_bind_map.cpp | 94 - src/nanobind/tests/test_stl_bind_map.py | 222 -- src/nanobind/tests/test_stl_bind_vector.cpp | 56 - src/nanobind/tests/test_stl_bind_vector.py | 188 - src/nanobind/tests/test_stl_ext.pyi.ref | 274 -- src/nanobind/tests/test_stubs.py | 72 - src/nanobind/tests/test_tensorflow.cpp | 25 - src/nanobind/tests/test_tensorflow.py | 0 .../tests/test_tensorflow_ext.pyi.ref | 4 - src/nanobind/tests/test_thread.cpp | 71 - src/nanobind/tests/test_thread.py | 102 - src/nanobind/tests/test_typing.cpp | 118 - src/nanobind/tests/test_typing.py | 13 - src/nanobind/tests/test_typing_ext.pyi.ref | 70 - 238 files changed, 112 insertions(+), 67436 deletions(-) rename {src/.vs => .vs}/CMake Overview (100%) rename {src/.vs => .vs}/Modul/FileContentIndex/56ca19f5-7ec7-4164-887a-f5708baad850.vsidx (100%) rename {src/.vs => .vs}/Modul/FileContentIndex/8939f018-00c0-4566-b8c4-5c2231b3b9d4.vsidx (100%) rename {src/.vs => .vs}/Modul/FileContentIndex/cda6efeb-eccc-4afd-9506-518abeb485e9.vsidx (100%) rename {src/.vs => .vs}/Modul/v17/.wsuo (100%) rename {src/.vs => .vs}/Modul/v17/Browse.VC.db (100%) rename {src/.vs => .vs}/Modul/v17/DocumentLayout.json (100%) rename {src/.vs => .vs}/ProjectSettings.json (100%) rename {src/.vs => .vs}/VSWorkspaceState.json (100%) rename {src/.vs => .vs}/slnx.sqlite (100%) rename {src/.vscode => .vscode}/settings.json (100%) create mode 100644 CMakeLists.txt rename src/CMakeUserPresets.json => CMakeUserPresets.json (100%) rename src/conanfile.txt => conanfile.txt (100%) rename src/{python => PauliEngine}/__init__.py (100%) rename src/{nanobind/tests/test_jax.py => bindings/CMakeLists.txt} (100%) create mode 100644 src/bindings/bindings.cpp delete mode 100644 src/nanobind/.gitignore delete mode 100644 src/nanobind/.gitmodules delete mode 100644 src/nanobind/.readthedocs.yaml delete mode 100644 src/nanobind/CMakeLists.txt delete mode 100644 src/nanobind/LICENSE delete mode 100644 src/nanobind/README.md delete mode 100644 src/nanobind/cmake/collect-symbols-pypy.py delete mode 100644 src/nanobind/cmake/collect-symbols.py delete mode 100644 src/nanobind/cmake/darwin-ld-cpython.sym delete mode 100644 src/nanobind/cmake/darwin-ld-pypy.sym delete mode 100644 src/nanobind/cmake/darwin-python-path.py delete mode 100644 src/nanobind/cmake/nanobind-config.cmake delete mode 100644 src/nanobind/docs/api_bazel.rst delete mode 100644 src/nanobind/docs/api_cmake.rst delete mode 100644 src/nanobind/docs/api_core.rst delete mode 100644 src/nanobind/docs/api_extra.rst delete mode 100644 src/nanobind/docs/basics.rst delete mode 100644 src/nanobind/docs/bazel.rst delete mode 100644 src/nanobind/docs/benchmark.rst delete mode 100644 src/nanobind/docs/building.rst delete mode 100644 src/nanobind/docs/changelog.rst delete mode 100644 src/nanobind/docs/classes.rst delete mode 100644 src/nanobind/docs/conf.py delete mode 100644 src/nanobind/docs/cppyy.h delete mode 100644 src/nanobind/docs/eigen.rst delete mode 100644 src/nanobind/docs/exceptions.rst delete mode 100644 src/nanobind/docs/exchanging.rst delete mode 100644 src/nanobind/docs/faq.rst delete mode 100644 src/nanobind/docs/free_threaded.rst delete mode 100644 src/nanobind/docs/functions.rst delete mode 100644 src/nanobind/docs/images/binding-dark.svg delete mode 100644 src/nanobind/docs/images/binding-light.svg delete mode 100644 src/nanobind/docs/images/caster-dark.svg delete mode 100644 src/nanobind/docs/images/caster-light.svg delete mode 100644 src/nanobind/docs/images/logo.jpg delete mode 100644 src/nanobind/docs/images/perf.svg delete mode 100644 src/nanobind/docs/images/sizes.svg delete mode 100644 src/nanobind/docs/images/times.svg delete mode 100644 src/nanobind/docs/images/wrapper-dark.svg delete mode 100644 src/nanobind/docs/images/wrapper-light.svg delete mode 100644 src/nanobind/docs/index.rst delete mode 100644 src/nanobind/docs/installing.rst delete mode 100644 src/nanobind/docs/lowlevel.rst delete mode 100644 src/nanobind/docs/meson.rst delete mode 100644 src/nanobind/docs/microbenchmark.ipynb delete mode 100644 src/nanobind/docs/ndarray.rst delete mode 100644 src/nanobind/docs/ndarray_index.rst delete mode 100644 src/nanobind/docs/ownership.rst delete mode 100644 src/nanobind/docs/ownership_adv.rst delete mode 100644 src/nanobind/docs/packaging.rst delete mode 100644 src/nanobind/docs/porting.rst delete mode 100644 src/nanobind/docs/pypy.rst delete mode 100644 src/nanobind/docs/refleaks.rst delete mode 100644 src/nanobind/docs/release.rst delete mode 100644 src/nanobind/docs/requirements.txt delete mode 100644 src/nanobind/docs/typing.rst delete mode 100644 src/nanobind/docs/utilities.rst delete mode 100644 src/nanobind/docs/why.rst delete mode 100644 src/nanobind/ext/robin_map/.clang-format delete mode 100644 src/nanobind/ext/robin_map/.codecov.yml delete mode 100644 src/nanobind/ext/robin_map/.github/workflows/ci.yml delete mode 100644 src/nanobind/ext/robin_map/CMakeLists.txt delete mode 100644 src/nanobind/ext/robin_map/LICENSE delete mode 100644 src/nanobind/ext/robin_map/README.md delete mode 100644 src/nanobind/ext/robin_map/cmake/tsl-robin-mapConfig.cmake.in delete mode 100644 src/nanobind/ext/robin_map/doxygen.conf delete mode 100644 src/nanobind/ext/robin_map/include/tsl/robin_growth_policy.h delete mode 100644 src/nanobind/ext/robin_map/include/tsl/robin_hash.h delete mode 100644 src/nanobind/ext/robin_map/include/tsl/robin_map.h delete mode 100644 src/nanobind/ext/robin_map/include/tsl/robin_set.h delete mode 100644 src/nanobind/ext/robin_map/tests/CMakeLists.txt delete mode 100644 src/nanobind/ext/robin_map/tests/custom_allocator_tests.cpp delete mode 100644 src/nanobind/ext/robin_map/tests/main.cpp delete mode 100644 src/nanobind/ext/robin_map/tests/policy_tests.cpp delete mode 100644 src/nanobind/ext/robin_map/tests/robin_map_tests.cpp delete mode 100644 src/nanobind/ext/robin_map/tests/robin_set_tests.cpp delete mode 100644 src/nanobind/ext/robin_map/tests/utils.h delete mode 100644 src/nanobind/ext/robin_map/tsl-robin-map.natvis delete mode 100644 src/nanobind/include/nanobind/eigen/dense.h delete mode 100644 src/nanobind/include/nanobind/eigen/sparse.h delete mode 100644 src/nanobind/include/nanobind/eval.h delete mode 100644 src/nanobind/include/nanobind/intrusive/counter.h delete mode 100644 src/nanobind/include/nanobind/intrusive/counter.inl delete mode 100644 src/nanobind/include/nanobind/intrusive/ref.h delete mode 100644 src/nanobind/include/nanobind/make_iterator.h delete mode 100644 src/nanobind/include/nanobind/nanobind.h delete mode 100644 src/nanobind/include/nanobind/nb_accessor.h delete mode 100644 src/nanobind/include/nanobind/nb_attr.h delete mode 100644 src/nanobind/include/nanobind/nb_call.h delete mode 100644 src/nanobind/include/nanobind/nb_cast.h delete mode 100644 src/nanobind/include/nanobind/nb_class.h delete mode 100644 src/nanobind/include/nanobind/nb_defs.h delete mode 100644 src/nanobind/include/nanobind/nb_descr.h delete mode 100644 src/nanobind/include/nanobind/nb_enums.h delete mode 100644 src/nanobind/include/nanobind/nb_error.h delete mode 100644 src/nanobind/include/nanobind/nb_func.h delete mode 100644 src/nanobind/include/nanobind/nb_lib.h delete mode 100644 src/nanobind/include/nanobind/nb_misc.h delete mode 100644 src/nanobind/include/nanobind/nb_python.h delete mode 100644 src/nanobind/include/nanobind/nb_traits.h delete mode 100644 src/nanobind/include/nanobind/nb_tuple.h delete mode 100644 src/nanobind/include/nanobind/nb_types.h delete mode 100644 src/nanobind/include/nanobind/ndarray.h delete mode 100644 src/nanobind/include/nanobind/operators.h delete mode 100644 src/nanobind/include/nanobind/stl/array.h delete mode 100644 src/nanobind/include/nanobind/stl/bind_map.h delete mode 100644 src/nanobind/include/nanobind/stl/bind_vector.h delete mode 100644 src/nanobind/include/nanobind/stl/chrono.h delete mode 100644 src/nanobind/include/nanobind/stl/complex.h delete mode 100644 src/nanobind/include/nanobind/stl/detail/chrono.h delete mode 100644 src/nanobind/include/nanobind/stl/detail/nb_array.h delete mode 100644 src/nanobind/include/nanobind/stl/detail/nb_dict.h delete mode 100644 src/nanobind/include/nanobind/stl/detail/nb_list.h delete mode 100644 src/nanobind/include/nanobind/stl/detail/nb_optional.h delete mode 100644 src/nanobind/include/nanobind/stl/detail/nb_set.h delete mode 100644 src/nanobind/include/nanobind/stl/detail/traits.h delete mode 100644 src/nanobind/include/nanobind/stl/filesystem.h delete mode 100644 src/nanobind/include/nanobind/stl/function.h delete mode 100644 src/nanobind/include/nanobind/stl/list.h delete mode 100644 src/nanobind/include/nanobind/stl/map.h delete mode 100644 src/nanobind/include/nanobind/stl/optional.h delete mode 100644 src/nanobind/include/nanobind/stl/pair.h delete mode 100644 src/nanobind/include/nanobind/stl/set.h delete mode 100644 src/nanobind/include/nanobind/stl/shared_ptr.h delete mode 100644 src/nanobind/include/nanobind/stl/string.h delete mode 100644 src/nanobind/include/nanobind/stl/string_view.h delete mode 100644 src/nanobind/include/nanobind/stl/tuple.h delete mode 100644 src/nanobind/include/nanobind/stl/unique_ptr.h delete mode 100644 src/nanobind/include/nanobind/stl/unordered_map.h delete mode 100644 src/nanobind/include/nanobind/stl/unordered_set.h delete mode 100644 src/nanobind/include/nanobind/stl/variant.h delete mode 100644 src/nanobind/include/nanobind/stl/vector.h delete mode 100644 src/nanobind/include/nanobind/stl/wstring.h delete mode 100644 src/nanobind/include/nanobind/trampoline.h delete mode 100644 src/nanobind/include/nanobind/typing.h delete mode 100644 src/nanobind/pyproject.toml delete mode 100644 src/nanobind/src/__init__.py delete mode 100644 src/nanobind/src/__main__.py delete mode 100644 src/nanobind/src/buffer.h delete mode 100644 src/nanobind/src/common.cpp delete mode 100644 src/nanobind/src/error.cpp delete mode 100644 src/nanobind/src/hash.h delete mode 100644 src/nanobind/src/implicit.cpp delete mode 100644 src/nanobind/src/nb_abi.h delete mode 100644 src/nanobind/src/nb_combined.cpp delete mode 100644 src/nanobind/src/nb_enum.cpp delete mode 100644 src/nanobind/src/nb_ft.cpp delete mode 100644 src/nanobind/src/nb_ft.h delete mode 100644 src/nanobind/src/nb_func.cpp delete mode 100644 src/nanobind/src/nb_internals.cpp delete mode 100644 src/nanobind/src/nb_internals.h delete mode 100644 src/nanobind/src/nb_ndarray.cpp delete mode 100644 src/nanobind/src/nb_static_property.cpp delete mode 100644 src/nanobind/src/nb_type.cpp delete mode 100644 src/nanobind/src/stubgen.py delete mode 100644 src/nanobind/src/trampoline.cpp delete mode 100644 src/nanobind/src/version.py delete mode 100644 src/nanobind/tests/CMakeLists.txt delete mode 100644 src/nanobind/tests/common.py delete mode 100644 src/nanobind/tests/conftest.py delete mode 100644 src/nanobind/tests/inter_module.cpp delete mode 100644 src/nanobind/tests/inter_module.h delete mode 100644 src/nanobind/tests/object_py.h delete mode 100644 src/nanobind/tests/pattern_file.nb delete mode 100644 src/nanobind/tests/py_stub_test.py delete mode 100644 src/nanobind/tests/py_stub_test.pyi.ref delete mode 100644 src/nanobind/tests/test_callbacks.cpp delete mode 100644 src/nanobind/tests/test_callbacks.py delete mode 100644 src/nanobind/tests/test_chrono.cpp delete mode 100644 src/nanobind/tests/test_chrono.py delete mode 100644 src/nanobind/tests/test_classes.cpp delete mode 100644 src/nanobind/tests/test_classes.py delete mode 100644 src/nanobind/tests/test_classes_ext.pyi.ref delete mode 100644 src/nanobind/tests/test_eigen.cpp delete mode 100644 src/nanobind/tests/test_eigen.py delete mode 100644 src/nanobind/tests/test_enum.cpp delete mode 100644 src/nanobind/tests/test_enum.py delete mode 100644 src/nanobind/tests/test_enum_ext.pyi.ref delete mode 100644 src/nanobind/tests/test_eval.cpp delete mode 100644 src/nanobind/tests/test_eval.py delete mode 100644 src/nanobind/tests/test_exception.cpp delete mode 100644 src/nanobind/tests/test_exception.py delete mode 100644 src/nanobind/tests/test_functions.cpp delete mode 100644 src/nanobind/tests/test_functions.py delete mode 100644 src/nanobind/tests/test_functions_ext.pyi.ref delete mode 100644 src/nanobind/tests/test_holders.cpp delete mode 100644 src/nanobind/tests/test_holders.py delete mode 100644 src/nanobind/tests/test_inter_module.py delete mode 100644 src/nanobind/tests/test_inter_module_1.cpp delete mode 100644 src/nanobind/tests/test_inter_module_2.cpp delete mode 100644 src/nanobind/tests/test_intrusive.cpp delete mode 100644 src/nanobind/tests/test_intrusive.py delete mode 100644 src/nanobind/tests/test_intrusive_impl.cpp delete mode 100644 src/nanobind/tests/test_issue.cpp delete mode 100644 src/nanobind/tests/test_issue.py delete mode 100644 src/nanobind/tests/test_jax.cpp delete mode 100644 src/nanobind/tests/test_jax_ext.pyi.ref delete mode 100644 src/nanobind/tests/test_make_iterator.cpp delete mode 100644 src/nanobind/tests/test_make_iterator.py delete mode 100644 src/nanobind/tests/test_make_iterator_ext.pyi.ref delete mode 100644 src/nanobind/tests/test_ndarray.cpp delete mode 100644 src/nanobind/tests/test_ndarray.py delete mode 100644 src/nanobind/tests/test_ndarray_ext.pyi.ref delete mode 100644 src/nanobind/tests/test_stl.cpp delete mode 100644 src/nanobind/tests/test_stl.py delete mode 100644 src/nanobind/tests/test_stl_bind_map.cpp delete mode 100644 src/nanobind/tests/test_stl_bind_map.py delete mode 100644 src/nanobind/tests/test_stl_bind_vector.cpp delete mode 100644 src/nanobind/tests/test_stl_bind_vector.py delete mode 100644 src/nanobind/tests/test_stl_ext.pyi.ref delete mode 100644 src/nanobind/tests/test_stubs.py delete mode 100644 src/nanobind/tests/test_tensorflow.cpp delete mode 100644 src/nanobind/tests/test_tensorflow.py delete mode 100644 src/nanobind/tests/test_tensorflow_ext.pyi.ref delete mode 100644 src/nanobind/tests/test_thread.cpp delete mode 100644 src/nanobind/tests/test_thread.py delete mode 100644 src/nanobind/tests/test_typing.cpp delete mode 100644 src/nanobind/tests/test_typing.py delete mode 100644 src/nanobind/tests/test_typing_ext.pyi.ref diff --git a/src/.vs/CMake Overview b/.vs/CMake Overview similarity index 100% rename from src/.vs/CMake Overview rename to .vs/CMake Overview diff --git a/src/.vs/Modul/FileContentIndex/56ca19f5-7ec7-4164-887a-f5708baad850.vsidx b/.vs/Modul/FileContentIndex/56ca19f5-7ec7-4164-887a-f5708baad850.vsidx similarity index 100% rename from src/.vs/Modul/FileContentIndex/56ca19f5-7ec7-4164-887a-f5708baad850.vsidx rename to .vs/Modul/FileContentIndex/56ca19f5-7ec7-4164-887a-f5708baad850.vsidx diff --git a/src/.vs/Modul/FileContentIndex/8939f018-00c0-4566-b8c4-5c2231b3b9d4.vsidx b/.vs/Modul/FileContentIndex/8939f018-00c0-4566-b8c4-5c2231b3b9d4.vsidx similarity index 100% rename from src/.vs/Modul/FileContentIndex/8939f018-00c0-4566-b8c4-5c2231b3b9d4.vsidx rename to .vs/Modul/FileContentIndex/8939f018-00c0-4566-b8c4-5c2231b3b9d4.vsidx diff --git a/src/.vs/Modul/FileContentIndex/cda6efeb-eccc-4afd-9506-518abeb485e9.vsidx b/.vs/Modul/FileContentIndex/cda6efeb-eccc-4afd-9506-518abeb485e9.vsidx similarity index 100% rename from src/.vs/Modul/FileContentIndex/cda6efeb-eccc-4afd-9506-518abeb485e9.vsidx rename to .vs/Modul/FileContentIndex/cda6efeb-eccc-4afd-9506-518abeb485e9.vsidx diff --git a/src/.vs/Modul/v17/.wsuo b/.vs/Modul/v17/.wsuo similarity index 100% rename from src/.vs/Modul/v17/.wsuo rename to .vs/Modul/v17/.wsuo diff --git a/src/.vs/Modul/v17/Browse.VC.db b/.vs/Modul/v17/Browse.VC.db similarity index 100% rename from src/.vs/Modul/v17/Browse.VC.db rename to .vs/Modul/v17/Browse.VC.db diff --git a/src/.vs/Modul/v17/DocumentLayout.json b/.vs/Modul/v17/DocumentLayout.json similarity index 100% rename from src/.vs/Modul/v17/DocumentLayout.json rename to .vs/Modul/v17/DocumentLayout.json diff --git a/src/.vs/ProjectSettings.json b/.vs/ProjectSettings.json similarity index 100% rename from src/.vs/ProjectSettings.json rename to .vs/ProjectSettings.json diff --git a/src/.vs/VSWorkspaceState.json b/.vs/VSWorkspaceState.json similarity index 100% rename from src/.vs/VSWorkspaceState.json rename to .vs/VSWorkspaceState.json diff --git a/src/.vs/slnx.sqlite b/.vs/slnx.sqlite similarity index 100% rename from src/.vs/slnx.sqlite rename to .vs/slnx.sqlite diff --git a/src/.vscode/settings.json b/.vscode/settings.json similarity index 100% rename from src/.vscode/settings.json rename to .vscode/settings.json diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..d9a74e1 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,47 @@ +cmake_minimum_required(VERSION 3.15...3.27) +cmake_policy(SET CMP0091 NEW) + +project(PauliEngine LANGUAGES CXX) + +# Standardkonfiguration +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +if (CMAKE_VERSION VERSION_LESS 3.18) + set(DEV_MODULE Development) +else() + set(DEV_MODULE Development.Module) +endif() + +if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") +endif() + +# Conan Toolchain einbinden (nach: conan install .. --install-folder=build --build=missing) +include(${CMAKE_BINARY_DIR}/conan_toolchain.cmake OPTIONAL RESULT_VARIABLE _found) + +# Python & SymEngine +find_package(Python REQUIRED COMPONENTS Interpreter Development.Module) +find_package(SymEngine REQUIRED CONFIG) + +# Nanobind einbinden +add_subdirectory(nanobind) + +# Header-only Bibliothek definieren +add_library(pauli_string INTERFACE) +target_include_directories(pauli_string INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include) + +if (SymEngine_FOUND) + target_compile_definitions(pauli_string INTERFACE HAVE_SYMENGINE) + target_link_libraries(pauli_string INTERFACE symengine) +endif() + +# Python-Modul erstellen +nanobind_add_module(PauliEngine + QubitHamiltonian.cpp +) + +target_link_libraries(PauliEngine PRIVATE + pauli_string +) diff --git a/src/CMakeUserPresets.json b/CMakeUserPresets.json similarity index 100% rename from src/CMakeUserPresets.json rename to CMakeUserPresets.json diff --git a/src/conanfile.txt b/conanfile.txt similarity index 100% rename from src/conanfile.txt rename to conanfile.txt diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d9a74e1..e69de29 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,47 +0,0 @@ -cmake_minimum_required(VERSION 3.15...3.27) -cmake_policy(SET CMP0091 NEW) - -project(PauliEngine LANGUAGES CXX) - -# Standardkonfiguration -set(CMAKE_CXX_STANDARD 20) -set(CMAKE_POSITION_INDEPENDENT_CODE ON) - -if (CMAKE_VERSION VERSION_LESS 3.18) - set(DEV_MODULE Development) -else() - set(DEV_MODULE Development.Module) -endif() - -if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE) - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") -endif() - -# Conan Toolchain einbinden (nach: conan install .. --install-folder=build --build=missing) -include(${CMAKE_BINARY_DIR}/conan_toolchain.cmake OPTIONAL RESULT_VARIABLE _found) - -# Python & SymEngine -find_package(Python REQUIRED COMPONENTS Interpreter Development.Module) -find_package(SymEngine REQUIRED CONFIG) - -# Nanobind einbinden -add_subdirectory(nanobind) - -# Header-only Bibliothek definieren -add_library(pauli_string INTERFACE) -target_include_directories(pauli_string INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include) - -if (SymEngine_FOUND) - target_compile_definitions(pauli_string INTERFACE HAVE_SYMENGINE) - target_link_libraries(pauli_string INTERFACE symengine) -endif() - -# Python-Modul erstellen -nanobind_add_module(PauliEngine - QubitHamiltonian.cpp -) - -target_link_libraries(PauliEngine PRIVATE - pauli_string -) diff --git a/src/python/__init__.py b/src/PauliEngine/__init__.py similarity index 100% rename from src/python/__init__.py rename to src/PauliEngine/__init__.py diff --git a/src/QubitHamiltonian.h b/src/QubitHamiltonian.h index 5aa5c9e..294b28e 100644 --- a/src/QubitHamiltonian.h +++ b/src/QubitHamiltonian.h @@ -1,13 +1,5 @@ #pragma once -#include -#include -#include -#include -#include -#include -#include - #include #include #include @@ -20,12 +12,8 @@ using SymEngine::Expression; #else #endif - - #define BITS_IN_INTEGER (sizeof(uint64_t) * 8) -namespace nb = nanobind; - using Pauli_structure = std::pair, std::unordered_map>; using Pauli_structure_variable = std::pair>; using Hamiltonian_structure = std::vector; diff --git a/src/nanobind/tests/test_jax.py b/src/bindings/CMakeLists.txt similarity index 100% rename from src/nanobind/tests/test_jax.py rename to src/bindings/CMakeLists.txt diff --git a/src/bindings/bindings.cpp b/src/bindings/bindings.cpp new file mode 100644 index 0000000..efb8422 --- /dev/null +++ b/src/bindings/bindings.cpp @@ -0,0 +1,65 @@ +#include +#include +#include +#include +#include +#include +#include + + + +namespace nb = nanobind; + + + +NB_MODULE(PauliEngine, m) { + nb::class_>(m, "PauliString", "Represents a Pauli string in binary symplectic form.") + .def(nb::init<>(), "Default constructor.") + .def(nb::init&, std::complex>(), + "Constructor from a map of qubit indices to Pauli operators and a complex coefficient.") + .def(nb::init&, std::string>(), + "Constructor from a map of qubit indices to Pauli operators and a complex coefficient.") + .def("to_string", &PauliString<>::to_string, "Returns a human-readable string representation of the Pauli string.") + .def("is_all_z", &PauliString<>::is_all_z, "Checks if the Pauli string consists only of Z operators.") + .def("get_coeff", &PauliString<>::get_coeff, "Returns the complex coefficient of the Pauli string.") + .def("to_dictionary", &PauliString<>::to_dictionary, "Converts the Pauli string to a dictionary with coefficient and operators.") + .def("commutator", &PauliString<>::commutator, "Computes the commutator with another Pauli string.") + .def("map_qubits", &PauliString<>::map_qubits, "Remaps qubit indices according to a given mapping.") + .def("diff", &PauliString<>::diff, "Differentiation after given Variable") + .def("__mul__", nb::overload_cast&>(&PauliString<>::operator*, nb::const_), "Multiply two PauliStrings") + .def("__mul__", nb::overload_cast>(&PauliString<>::operator*), "Scale PauliString by complex scalar") + .def("__imul__", nb::overload_cast>(&PauliString<>::operator*=), "In-place scale") + .def("__imul__", nb::overload_cast>(&PauliString<>::operator*=), "In-place multiply") + .def("__repr__", &PauliString<>::to_string, "Returns a human-readable string representation of the Pauli string.") + .def("__str__", &PauliString<>::to_string, "Returns a human-readable string representation of the Pauli string.") + .def("__eq__", &PauliString<>::equals, "Checks if 2 PauliStrings have same data and Coefficient") + .def("diff", &PauliString<>::key_openfermion, "Returns Paulistring in Openfermion format") + .def("qubits", &PauliString<>::qubits, "Returns list of qubits") + .def("equals", &PauliString<>::operator==, "Checks if 2 PauliStrings have same data") + .def("set_coeff", nb::overload_cast(&PauliString<>::set_coeff), "Sets coefficient using a SymEngine expression") + .def("set_coeff", nb::overload_cast>(&PauliString<>::set_coeff), "Sets coefficient using a complex number") + .def_static("to_complex", &PauliString<>::to_complex, "Parse SymEngine expression into complex number") + .def("copy", &PauliString<>::copy, "Create a copy of the PauliString") + .def("get_pauli_at_index", &PauliString<>::get_pauli_from_index, "Returns list of qubits"); + + nb::class_(m, "QubitHamiltonian", "Represents a Hamiltonian as a sum of Pauli strings.") + .def(nb::init>&>(), "Constructor from a vector of Pauli strings.") + .def(nb::init(), "Constructor from a Hamiltonian structure (coefficient and operator map).") + .def(nb::init(), "Constructor from a Hamiltonian structure (coefficient and operator map).") + .def("__add__", &QubitHamiltonian::operator+, "Adds two Hamiltonians.") + .def("__mul__", nb::overload_cast const>(&QubitHamiltonian::operator*, nb::const_), "Scales the Hamiltonian by a complex scalar.") + .def("__mul__", nb::overload_cast(&QubitHamiltonian::operator*, nb::const_), "Multiplies two Hamiltonians.") + .def("trace_out_qubits", &QubitHamiltonian::trace_out_qubits, "Traces out specified qubits in given states.") + .def("to_string", &QubitHamiltonian::to_string, "Converts the Hamiltonian to its full matrix representation.") + .def("diff", &QubitHamiltonian::diff, "Differentiation after given Variable") + .def("subs", &QubitHamiltonian::substitute, "Replace Variable with value") + .def("parse_python_format", &QubitHamiltonian::parse_python_format, "Konvertiert mit SymEngine-Koeffizienten"); + + + + nb::class_(m, "Expression") + .def(nb::init()) // Konstruktor aus String + .def("__str__", [](const SymEngine::Expression &e) { + return SymEngine::str(*e.get_basic()); + }); +} \ No newline at end of file diff --git a/src/nanobind/.gitignore b/src/nanobind/.gitignore deleted file mode 100644 index bc78714..0000000 --- a/src/nanobind/.gitignore +++ /dev/null @@ -1,43 +0,0 @@ -libnanobind-static.a -libnanobind-static-abi3.a -libnanobind-static-ft.a -libnanobind.so -libnanobind-abi3.so -libnanobind-ft.so -libnanobind.dylib -libnanobind-abi3.dylib -libnanobind-ft.dylib -nanobind.dll -nanobind-abi3.dll -nanobind-ft.dll - -libinter_module.dylib -libinter_module.so -inter_module.dll - -/.ninja_deps -/.ninja_log -/.cache -/build.ninja -/build -/src/nanobind/cmake -/src/nanobind/include -/src/nanobind/ext -/src/nanobind/src -/dist -/bench -compile_commands.json - -CMakeFiles -CMakeCache.txt -cmake_install.cmake -nanobind-config-version.cmake -\.DS_Store -\.cmake -__pycache__ -nanobind.egg-info -test_*_ext*.so -test_*_ext*.pyd -test_*.pyi -py\.typed -.mypy_cache diff --git a/src/nanobind/.gitmodules b/src/nanobind/.gitmodules deleted file mode 100644 index 1aa486e..0000000 --- a/src/nanobind/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "ext/robin_map"] - path = ext/robin_map - url = https://github.com/Tessil/robin-map diff --git a/src/nanobind/.readthedocs.yaml b/src/nanobind/.readthedocs.yaml deleted file mode 100644 index 7947625..0000000 --- a/src/nanobind/.readthedocs.yaml +++ /dev/null @@ -1,18 +0,0 @@ -version: 2 - -build: - os: ubuntu-22.04 - apt_packages: - - librsvg2-bin - tools: - python: "3.11" - -sphinx: - configuration: docs/conf.py - -python: - install: - - requirements: docs/requirements.txt - -formats: - - pdf diff --git a/src/nanobind/CMakeLists.txt b/src/nanobind/CMakeLists.txt deleted file mode 100644 index 1c1ebdf..0000000 --- a/src/nanobind/CMakeLists.txt +++ /dev/null @@ -1,160 +0,0 @@ -cmake_minimum_required(VERSION 3.15...3.27) - -# --------------------------------------------------------------------------- -# Read the project version from nanobind.h -# --------------------------------------------------------------------------- - -file(STRINGS "include/nanobind/nanobind.h" _nanobind_h_version REGEX "^#define NB_VERSION_.*$") -string(REGEX MATCH "#define NB_VERSION_MAJOR ([0-9]+)" _ "${_nanobind_h_version}") -set(_major ${CMAKE_MATCH_1}) -string(REGEX MATCH "#define NB_VERSION_MINOR ([0-9]+)" _ "${_nanobind_h_version}") -set(_minor ${CMAKE_MATCH_1}) -string(REGEX MATCH "#define NB_VERSION_PATCH ([0-9]+)" _ "${_nanobind_h_version}") -set(_patch ${CMAKE_MATCH_1}) - -project(nanobind LANGUAGES NONE VERSION "${_major}.${_minor}.${_patch}") - -# --------------------------------------------------------------------------- -# Only build tests by default if this is the top-level CMake project -# --------------------------------------------------------------------------- - -if (CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) - set(NB_MASTER_PROJECT ON) -else() - set(NB_MASTER_PROJECT OFF) -endif() - -option(NB_CREATE_INSTALL_RULES "Create installation rules" ${NB_MASTER_PROJECT}) -option(NB_USE_SUBMODULE_DEPS "Use the nanobind dependencies shipped as a git submodule of this repository" ON) - -option(NB_TEST "Compile nanobind tests?" ${NB_MASTER_PROJECT}) -option(NB_TEST_STABLE_ABI "Test the stable ABI interface?" OFF) -option(NB_TEST_SHARED_BUILD "Build a shared nanobind library for the test suite?" OFF) -option(NB_TEST_CUDA "Force the use of the CUDA/NVCC compiler for testing purposes" OFF) -option(NB_TEST_FREE_THREADED "Build free-threaded extensions for the test suite?" ON) - -if (NOT MSVC) - option(NB_TEST_SANITIZERS_ASAN "Build tests with the address sanitizer?" OFF) - option(NB_TEST_SANITIZERS_UBSAN "Build tests with the address undefined behavior sanitizer?" OFF) - option(NB_TEST_SANITIZERS_TSAN "Build tests with the thread sanitizer?" OFF) -endif() - -# --------------------------------------------------------------------------- -# Do a release build if nothing was specified -# --------------------------------------------------------------------------- - -if (NB_MASTER_PROJECT AND NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - message(STATUS "nanobind: setting build type to 'Release' as none was specified.") - set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE) - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" - "MinSizeRel" "RelWithDebInfo") -endif() - -# --------------------------------------------------------------------------- -# Check whether all dependencies are present -# --------------------------------------------------------------------------- - -if (NB_USE_SUBMODULE_DEPS AND NOT IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/ext/robin_map/include") - message(FATAL_ERROR "The nanobind dependencies are missing! " - "You probably did not clone the project with --recursive. It is possible to recover " - "by invoking\n$ git submodule update --init --recursive") -endif() - -# --------------------------------------------------------------------------- -# Installation rules -# --------------------------------------------------------------------------- -if(NB_CREATE_INSTALL_RULES AND NOT CMAKE_SKIP_INSTALL_RULES) - # Silence warning in GNUInstallDirs due to no enabled languages - set(CMAKE_INSTALL_LIBDIR "") - include(GNUInstallDirs) - set(NB_INSTALL_DATADIR "nanobind" - CACHE PATH "Installation path for read-only architecture-independent nanobind data files") - - # Normally these would be configurable by the user, but we can't allow that - # because the lookup paths are hard-coded in 'cmake/nanobind-config.cmake' - set(NB_INSTALL_INC_DIR "${NB_INSTALL_DATADIR}/include") - set(NB_INSTALL_SRC_DIR "${NB_INSTALL_DATADIR}/src") - set(NB_INSTALL_EXT_DIR "${NB_INSTALL_DATADIR}/ext") - set(NB_INSTALL_MODULE_DIR "${NB_INSTALL_DATADIR}") - set(NB_INSTALL_CMAKE_DIR "${NB_INSTALL_DATADIR}/cmake") - - install( - DIRECTORY include/ - DESTINATION "${NB_INSTALL_INC_DIR}" - ) - - install( - DIRECTORY src/ - DESTINATION "${NB_INSTALL_SRC_DIR}" - PATTERN "*.py" EXCLUDE - ) - - install( - DIRECTORY src/ - DESTINATION "${NB_INSTALL_MODULE_DIR}" - FILES_MATCHING PATTERN "*\.py" - PATTERN "version.py" EXCLUDE - ) - - if(NB_USE_SUBMODULE_DEPS) - install( - FILES ext/robin_map/include/tsl/robin_map.h - ext/robin_map/include/tsl/robin_hash.h - ext/robin_map/include/tsl/robin_growth_policy.h - DESTINATION "${NB_INSTALL_EXT_DIR}/robin_map/include/tsl" - ) - endif() - - install( - FILES cmake/nanobind-config.cmake - cmake/darwin-python-path.py - cmake/darwin-ld-cpython.sym - cmake/darwin-ld-pypy.sym - DESTINATION "${NB_INSTALL_CMAKE_DIR}" - ) - - include(CMakePackageConfigHelpers) - write_basic_package_version_file( - ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake - COMPATIBILITY SameMajorVersion - ) - install( - FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake - DESTINATION "${NB_INSTALL_CMAKE_DIR}" - ) -endif() - -# Return early to skip finding needless dependencies if the user only wishes to -# install nanobind -if (NB_MASTER_PROJECT AND NOT NB_TEST) - return() -else() - enable_language(CXX) -endif() - -# --------------------------------------------------------------------------- -# Find the Python interpreter and development libraries -# --------------------------------------------------------------------------- - -if (NOT TARGET Python::Module OR NOT TARGET Python::Interpreter) - set(Python_FIND_FRAMEWORK LAST) # Prefer Brew/Conda to Apple framework python - - if (CMAKE_VERSION VERSION_LESS 3.18) - set(NB_PYTHON_DEV_MODULE Development) - else() - set(NB_PYTHON_DEV_MODULE Development.Module) - endif() - - find_package(Python 3.8 - REQUIRED COMPONENTS Interpreter ${NB_PYTHON_DEV_MODULE} - OPTIONAL_COMPONENTS Development.SABIModule) -endif() - -# --------------------------------------------------------------------------- -# Include nanobind cmake functionality -# --------------------------------------------------------------------------- -include(cmake/nanobind-config.cmake) - -if (NB_TEST) - add_subdirectory(tests) -endif() diff --git a/src/nanobind/LICENSE b/src/nanobind/LICENSE deleted file mode 100644 index 3d3426d..0000000 --- a/src/nanobind/LICENSE +++ /dev/null @@ -1,26 +0,0 @@ -Copyright (c) 2022 Wenzel Jakob , All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/nanobind/README.md b/src/nanobind/README.md deleted file mode 100644 index 19abaf1..0000000 --- a/src/nanobind/README.md +++ /dev/null @@ -1,59 +0,0 @@ -# nanobind: tiny and efficient C++/Python bindings - -[![Documentation](https://img.shields.io/readthedocs/nanobind/latest)](https://nanobind.readthedocs.io/en/latest/) -[![Continuous Integration](https://img.shields.io/github/actions/workflow/status/wjakob/nanobind/ci.yml?label=tests)](https://github.com/wjakob/nanobind/actions/workflows/ci.yml) -[![](https://img.shields.io/pypi/v/nanobind.svg?color=brightgreen)](https://pypi.org/pypi/nanobind/) -![](https://img.shields.io/pypi/l/nanobind.svg?color=brightgreen) -[![](https://img.shields.io/badge/Example-Link-brightgreen)](https://github.com/wjakob/nanobind_example) -[![](https://img.shields.io/badge/Changelog-Link-brightgreen)](https://nanobind.readthedocs.io/en/latest/changelog.html) - -

- - - - nanobind logo - -

- -_nanobind_ is a small binding library that exposes C++ types in Python and vice -versa. It is reminiscent of -[Boost.Python](https://www.boost.org/doc/libs/1_64_0/libs/python/doc/html) and -[pybind11](https://github.com/pybind/pybind11) and uses near-identical syntax. -In contrast to these existing tools, nanobind is more efficient: bindings -compile in a shorter amount of time, produce smaller binaries, and have better -runtime performance. - -More concretely, -[benchmarks](https://nanobind.readthedocs.io/en/latest/benchmark.html) show up -to **~4× faster** compile time, **~5× smaller** binaries, and **~10× lower** -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). - -## Documentation - -Please see the following links for tutorial and reference documentation in -[HTML](https://nanobind.readthedocs.io/en/latest/) and -[PDF](https://nanobind.readthedocs.io/_/downloads/en/latest/pdf/) formats. - -## License and attribution - -All material in this repository is licensed under a three-clause [BSD -license](LICENSE). - -Please use the following BibTeX template to cite nanobind in scientific -discourse: - -```bibtex -@misc{nanobind, - author = {Wenzel Jakob}, - year = {2022}, - note = {https://github.com/wjakob/nanobind}, - title = {nanobind: tiny and efficient C++/Python bindings} -} -``` - -The nanobind logo was designed by [AndoTwin Studio](https://andotwinstudio.com) -(high-resolution download: -[light](https://rgl.s3.eu-central-1.amazonaws.com/media/uploads/wjakob/2023/03/27/nanobind_logo.jpg), -[dark](https://rgl.s3.eu-central-1.amazonaws.com/media/uploads/wjakob/2023/03/28/nanobind_logo_dark_1.png)). diff --git a/src/nanobind/cmake/collect-symbols-pypy.py b/src/nanobind/cmake/collect-symbols-pypy.py deleted file mode 100644 index 805490d..0000000 --- a/src/nanobind/cmake/collect-symbols-pypy.py +++ /dev/null @@ -1,28 +0,0 @@ -from urllib.request import urlopen -import tarfile -import subprocess - -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') -] - -for f in files: - fs = urlopen(f[0]) - ft = tarfile.open(fileobj=fs, mode="r|bz2") - success = False - for member in ft: # move to the next file each loop - if member.name == f[1]: - ft.extract(member, path='tmp') - success = True - assert success - - out = subprocess.check_output(['nm', '-gjU', 'tmp/' + f[1]]) - for line in out.decode().split('\n'): - if line.startswith('_Py') or line.startswith('__Py'): - funcs.add(line) - -with open("darwin-ld-pypy.sym", "w") as f: - for func in sorted(list(funcs)): - f.write(f'-U _{func}\n') diff --git a/src/nanobind/cmake/collect-symbols.py b/src/nanobind/cmake/collect-symbols.py deleted file mode 100644 index ad92da4..0000000 --- a/src/nanobind/cmake/collect-symbols.py +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env python3 -# -# This script collects a list of symbols that are considered to be part of the -# CPython API. The result is used to inform the macOS linker that it's fine for -# those symbols to be undefined when an extension module is linked, as they -# will be provided when the extension module is loaded into the interpreter. - -from urllib.request import urlopen -import re - -funcs: "set[str]" = set() - -for ver in ['3.7', '3.8', '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']: - 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): - funcs.add(match) - -funcs.remove('name') - -# Add a few more functions that nanobind uses and which aren't in the above list -funcs |= { - 'PyFrame_GetBack', - 'PyGILState_Check', - 'PyObject_LengthHint', - 'Py_CompileStringExFlags', - '_PyInterpreterState_Get', - '_PyObject_MakeTpCall', - '_PyObject_NextNotImplemented', - '_Py_CheckFunctionResult', - '_Py_RefTotal' -} - -with open("darwin-ld-cpython.sym", "w") as f: - for func in sorted(list(funcs)): - f.write(f'-U _{func}\n') diff --git a/src/nanobind/cmake/darwin-ld-cpython.sym b/src/nanobind/cmake/darwin-ld-cpython.sym deleted file mode 100644 index 0c15d6d..0000000 --- a/src/nanobind/cmake/darwin-ld-cpython.sym +++ /dev/null @@ -1,930 +0,0 @@ --U _PyAIter_Check --U _PyArg_Parse --U _PyArg_ParseTuple --U _PyArg_ParseTupleAndKeywords --U _PyArg_UnpackTuple --U _PyArg_VaParse --U _PyArg_VaParseTupleAndKeywords --U _PyArg_ValidateKeywordArguments --U _PyBaseObject_Type --U _PyBool_FromLong --U _PyBool_Type --U _PyBuffer_FillContiguousStrides --U _PyBuffer_FillInfo --U _PyBuffer_FromContiguous --U _PyBuffer_GetPointer --U _PyBuffer_IsContiguous --U _PyBuffer_Release --U _PyBuffer_SizeFromFormat --U _PyBuffer_ToContiguous --U _PyByteArrayIter_Type --U _PyByteArray_AsString --U _PyByteArray_Concat --U _PyByteArray_FromObject --U _PyByteArray_FromStringAndSize --U _PyByteArray_Resize --U _PyByteArray_Size --U _PyByteArray_Type --U _PyBytesIter_Type --U _PyBytes_AsString --U _PyBytes_AsStringAndSize --U _PyBytes_Concat --U _PyBytes_ConcatAndDel --U _PyBytes_DecodeEscape --U _PyBytes_FromFormat --U _PyBytes_FromFormatV --U _PyBytes_FromObject --U _PyBytes_FromString --U _PyBytes_FromStringAndSize --U _PyBytes_Repr --U _PyBytes_Size --U _PyBytes_Type --U _PyCFunction_Call --U _PyCFunction_ClearFreeList --U _PyCFunction_GetFlags --U _PyCFunction_GetFunction --U _PyCFunction_GetSelf --U _PyCFunction_New --U _PyCFunction_NewEx --U _PyCFunction_Type --U _PyCMethod_New --U _PyCallIter_New --U _PyCallIter_Type --U _PyCallable_Check --U _PyCapsule_GetContext --U _PyCapsule_GetDestructor --U _PyCapsule_GetName --U _PyCapsule_GetPointer --U _PyCapsule_Import --U _PyCapsule_IsValid --U _PyCapsule_New --U _PyCapsule_SetContext --U _PyCapsule_SetDestructor --U _PyCapsule_SetName --U _PyCapsule_SetPointer --U _PyCapsule_Type --U _PyClassMethodDescr_Type --U _PyCodec_BackslashReplaceErrors --U _PyCodec_Decode --U _PyCodec_Decoder --U _PyCodec_Encode --U _PyCodec_Encoder --U _PyCodec_IgnoreErrors --U _PyCodec_IncrementalDecoder --U _PyCodec_IncrementalEncoder --U _PyCodec_KnownEncoding --U _PyCodec_LookupError --U _PyCodec_NameReplaceErrors --U _PyCodec_Register --U _PyCodec_RegisterError --U _PyCodec_ReplaceErrors --U _PyCodec_StreamReader --U _PyCodec_StreamWriter --U _PyCodec_StrictErrors --U _PyCodec_Unregister --U _PyCodec_XMLCharRefReplaceErrors --U _PyComplex_AsCComplex --U _PyComplex_FromCComplex --U _PyComplex_FromDoubles --U _PyComplex_ImagAsDouble --U _PyComplex_RealAsDouble --U _PyComplex_Type --U _PyDescr_NewClassMethod --U _PyDescr_NewGetSet --U _PyDescr_NewMember --U _PyDescr_NewMethod --U _PyDictItems_Type --U _PyDictIterItem_Type --U _PyDictIterKey_Type --U _PyDictIterValue_Type --U _PyDictKeys_Type --U _PyDictProxy_New --U _PyDictProxy_Type --U _PyDictValues_Type --U _PyDict_Clear --U _PyDict_Contains --U _PyDict_Copy --U _PyDict_DelItem --U _PyDict_DelItemString --U _PyDict_GetItem --U _PyDict_GetItemString --U _PyDict_GetItemWithError --U _PyDict_Items --U _PyDict_Keys --U _PyDict_Merge --U _PyDict_MergeFromSeq2 --U _PyDict_New --U _PyDict_Next --U _PyDict_SetItem --U _PyDict_SetItemString --U _PyDict_Size --U _PyDict_Type --U _PyDict_Update --U _PyDict_Values --U _PyEllipsis_Type --U _PyEnum_Type --U _PyErr_BadArgument --U _PyErr_BadInternalCall --U _PyErr_CheckSignals --U _PyErr_Clear --U _PyErr_Display --U _PyErr_ExceptionMatches --U _PyErr_Fetch --U _PyErr_Format --U _PyErr_FormatV --U _PyErr_GetExcInfo --U _PyErr_GetHandledException --U _PyErr_GetRaisedException --U _PyErr_GivenExceptionMatches --U _PyErr_NewException --U _PyErr_NewExceptionWithDoc --U _PyErr_NoMemory --U _PyErr_NormalizeException --U _PyErr_Occurred --U _PyErr_Print --U _PyErr_PrintEx --U _PyErr_ProgramText --U _PyErr_ResourceWarning --U _PyErr_Restore --U _PyErr_SetExcFromWindowsErr --U _PyErr_SetExcFromWindowsErrWithFilename --U _PyErr_SetExcFromWindowsErrWithFilenameObject --U _PyErr_SetExcFromWindowsErrWithFilenameObjects --U _PyErr_SetExcInfo --U _PyErr_SetFromErrno --U _PyErr_SetFromErrnoWithFilename --U _PyErr_SetFromErrnoWithFilenameObject --U _PyErr_SetFromErrnoWithFilenameObjects --U _PyErr_SetFromWindowsErr --U _PyErr_SetFromWindowsErrWithFilename --U _PyErr_SetHandledException --U _PyErr_SetImportError --U _PyErr_SetImportErrorSubclass --U _PyErr_SetInterrupt --U _PyErr_SetInterruptEx --U _PyErr_SetNone --U _PyErr_SetObject --U _PyErr_SetRaisedException --U _PyErr_SetString --U _PyErr_SyntaxLocation --U _PyErr_SyntaxLocationEx --U _PyErr_WarnEx --U _PyErr_WarnExplicit --U _PyErr_WarnFormat --U _PyErr_WriteUnraisable --U _PyEval_AcquireLock --U _PyEval_AcquireThread --U _PyEval_CallFunction --U _PyEval_CallMethod --U _PyEval_CallObjectWithKeywords --U _PyEval_EvalCode --U _PyEval_EvalCodeEx --U _PyEval_EvalFrame --U _PyEval_EvalFrameEx --U _PyEval_GetBuiltins --U _PyEval_GetCallStats --U _PyEval_GetFrame --U _PyEval_GetFuncDesc --U _PyEval_GetFuncName --U _PyEval_GetGlobals --U _PyEval_GetLocals --U _PyEval_InitThreads --U _PyEval_ReInitThreads --U _PyEval_ReleaseLock --U _PyEval_ReleaseThread --U _PyEval_RestoreThread --U _PyEval_SaveThread --U _PyEval_ThreadsInitialized --U _PyExc_ArithmeticError --U _PyExc_AssertionError --U _PyExc_AttributeError --U _PyExc_BaseException --U _PyExc_BlockingIOError --U _PyExc_BrokenPipeError --U _PyExc_BufferError --U _PyExc_BytesWarning --U _PyExc_ChildProcessError --U _PyExc_ConnectionAbortedError --U _PyExc_ConnectionError --U _PyExc_ConnectionRefusedError --U _PyExc_ConnectionResetError --U _PyExc_DeprecationWarning --U _PyExc_EOFError --U _PyExc_EnvironmentError --U _PyExc_Exception --U _PyExc_FileExistsError --U _PyExc_FileNotFoundError --U _PyExc_FloatingPointError --U _PyExc_FutureWarning --U _PyExc_GeneratorExit --U _PyExc_IOError --U _PyExc_ImportError --U _PyExc_ImportWarning --U _PyExc_IndentationError --U _PyExc_IndexError --U _PyExc_InterruptedError --U _PyExc_IsADirectoryError --U _PyExc_KeyError --U _PyExc_KeyboardInterrupt --U _PyExc_LookupError --U _PyExc_MemoryError --U _PyExc_ModuleNotFoundError --U _PyExc_NameError --U _PyExc_NotADirectoryError --U _PyExc_NotImplementedError --U _PyExc_OSError --U _PyExc_OverflowError --U _PyExc_PendingDeprecationWarning --U _PyExc_PermissionError --U _PyExc_ProcessLookupError --U _PyExc_RecursionError --U _PyExc_ReferenceError --U _PyExc_ResourceWarning --U _PyExc_RuntimeError --U _PyExc_RuntimeWarning --U _PyExc_StopAsyncIteration --U _PyExc_StopIteration --U _PyExc_SyntaxError --U _PyExc_SyntaxWarning --U _PyExc_SystemError --U _PyExc_SystemExit --U _PyExc_TabError --U _PyExc_TimeoutError --U _PyExc_TypeError --U _PyExc_UnboundLocalError --U _PyExc_UnicodeDecodeError --U _PyExc_UnicodeEncodeError --U _PyExc_UnicodeError --U _PyExc_UnicodeTranslateError --U _PyExc_UnicodeWarning --U _PyExc_UserWarning --U _PyExc_ValueError --U _PyExc_Warning --U _PyExc_WindowsError --U _PyExc_ZeroDivisionError --U _PyExceptionClass_Name --U _PyException_GetCause --U _PyException_GetContext --U _PyException_GetTraceback --U _PyException_SetCause --U _PyException_SetContext --U _PyException_SetTraceback --U _PyFile_FromFd --U _PyFile_GetLine --U _PyFile_WriteObject --U _PyFile_WriteString --U _PyFilter_Type --U _PyFloat_AsDouble --U _PyFloat_FromDouble --U _PyFloat_FromString --U _PyFloat_GetInfo --U _PyFloat_GetMax --U _PyFloat_GetMin --U _PyFloat_Type --U _PyFrame_GetBack --U _PyFrame_GetCode --U _PyFrame_GetLineNumber --U _PyFrozenSet_New --U _PyFrozenSet_Type --U _PyGC_Collect --U _PyGC_Disable --U _PyGC_Enable --U _PyGC_IsEnabled --U _PyGILState_Check --U _PyGILState_Ensure --U _PyGILState_GetThisThreadState --U _PyGILState_Release --U _PyGetSetDescr_Type --U _PyGen_Type --U _PyImport_AddModule --U _PyImport_AddModuleObject --U _PyImport_AppendInittab --U _PyImport_Cleanup --U _PyImport_ExecCodeModule --U _PyImport_ExecCodeModuleEx --U _PyImport_ExecCodeModuleObject --U _PyImport_ExecCodeModuleWithPathnames --U _PyImport_GetImporter --U _PyImport_GetMagicNumber --U _PyImport_GetMagicTag --U _PyImport_GetModule --U _PyImport_GetModuleDict --U _PyImport_Import --U _PyImport_ImportFrozenModule --U _PyImport_ImportFrozenModuleObject --U _PyImport_ImportModule --U _PyImport_ImportModuleLevel --U _PyImport_ImportModuleLevelObject --U _PyImport_ImportModuleNoBlock --U _PyImport_ReloadModule --U _PyIndex_Check --U _PyInterpreterState_Clear --U _PyInterpreterState_Delete --U _PyInterpreterState_Get --U _PyInterpreterState_GetDict --U _PyInterpreterState_GetID --U _PyInterpreterState_New --U _PyIter_Check --U _PyIter_Next --U _PyIter_Send --U _PyListIter_Type --U _PyListRevIter_Type --U _PyList_Append --U _PyList_AsTuple --U _PyList_GetItem --U _PyList_GetSlice --U _PyList_Insert --U _PyList_New --U _PyList_Reverse --U _PyList_SetItem --U _PyList_SetSlice --U _PyList_Size --U _PyList_Sort --U _PyList_Type --U _PyLongRangeIter_Type --U _PyLong_AsDouble --U _PyLong_AsLong --U _PyLong_AsLongAndOverflow --U _PyLong_AsLongLong --U _PyLong_AsLongLongAndOverflow --U _PyLong_AsSize_t --U _PyLong_AsSsize_t --U _PyLong_AsUnsignedLong --U _PyLong_AsUnsignedLongLong --U _PyLong_AsUnsignedLongLongMask --U _PyLong_AsUnsignedLongMask --U _PyLong_AsVoidPtr --U _PyLong_FromDouble --U _PyLong_FromLong --U _PyLong_FromLongLong --U _PyLong_FromSize_t --U _PyLong_FromSsize_t --U _PyLong_FromString --U _PyLong_FromUnsignedLong --U _PyLong_FromUnsignedLongLong --U _PyLong_FromVoidPtr --U _PyLong_GetInfo --U _PyLong_Type --U _PyMap_Type --U _PyMapping_Check --U _PyMapping_GetItemString --U _PyMapping_HasKey --U _PyMapping_HasKeyString --U _PyMapping_Items --U _PyMapping_Keys --U _PyMapping_Length --U _PyMapping_SetItemString --U _PyMapping_Size --U _PyMapping_Values --U _PyMarshal_ReadObjectFromString --U _PyMarshal_WriteObjectToString --U _PyMem_Calloc --U _PyMem_Free --U _PyMem_Malloc --U _PyMem_Realloc --U _PyMemberDescr_Type --U _PyMember_GetOne --U _PyMember_SetOne --U _PyMemoryView_FromBuffer --U _PyMemoryView_FromMemory --U _PyMemoryView_FromObject --U _PyMemoryView_GetContiguous --U _PyMemoryView_Type --U _PyMethodDescr_Type --U _PyModuleDef_Init --U _PyModuleDef_Type --U _PyModule_AddFunctions --U _PyModule_AddIntConstant --U _PyModule_AddObject --U _PyModule_AddObjectRef --U _PyModule_AddStringConstant --U _PyModule_AddType --U _PyModule_Create2 --U _PyModule_ExecDef --U _PyModule_FromDefAndSpec2 --U _PyModule_GetDef --U _PyModule_GetDict --U _PyModule_GetFilename --U _PyModule_GetFilenameObject --U _PyModule_GetName --U _PyModule_GetNameObject --U _PyModule_GetState --U _PyModule_New --U _PyModule_NewObject --U _PyModule_SetDocString --U _PyModule_Type --U _PyNullImporter_Type --U _PyNumber_Absolute --U _PyNumber_Add --U _PyNumber_And --U _PyNumber_AsSsize_t --U _PyNumber_Check --U _PyNumber_Divmod --U _PyNumber_Float --U _PyNumber_FloorDivide --U _PyNumber_InPlaceAdd --U _PyNumber_InPlaceAnd --U _PyNumber_InPlaceFloorDivide --U _PyNumber_InPlaceLshift --U _PyNumber_InPlaceMatrixMultiply --U _PyNumber_InPlaceMultiply --U _PyNumber_InPlaceOr --U _PyNumber_InPlacePower --U _PyNumber_InPlaceRemainder --U _PyNumber_InPlaceRshift --U _PyNumber_InPlaceSubtract --U _PyNumber_InPlaceTrueDivide --U _PyNumber_InPlaceXor --U _PyNumber_Index --U _PyNumber_Invert --U _PyNumber_Long --U _PyNumber_Lshift --U _PyNumber_MatrixMultiply --U _PyNumber_Multiply --U _PyNumber_Negative --U _PyNumber_Or --U _PyNumber_Positive --U _PyNumber_Power --U _PyNumber_Remainder --U _PyNumber_Rshift --U _PyNumber_Subtract --U _PyNumber_ToBase --U _PyNumber_TrueDivide --U _PyNumber_Xor --U _PyODictItems_Type --U _PyODictIter_Type --U _PyODictKeys_Type --U _PyODictValues_Type --U _PyODict_DelItem --U _PyODict_New --U _PyODict_SetItem --U _PyODict_Type --U _PyOS_AfterFork --U _PyOS_CheckStack --U _PyOS_FSPath --U _PyOS_InitInterrupts --U _PyOS_InputHook --U _PyOS_InterruptOccurred --U _PyOS_ReadlineFunctionPointer --U _PyOS_double_to_string --U _PyOS_getsig --U _PyOS_mystricmp --U _PyOS_mystrnicmp --U _PyOS_setsig --U _PyOS_snprintf --U _PyOS_string_to_double --U _PyOS_strtol --U _PyOS_strtoul --U _PyOS_vsnprintf --U _PyObject_ASCII --U _PyObject_AsCharBuffer --U _PyObject_AsFileDescriptor --U _PyObject_AsReadBuffer --U _PyObject_AsWriteBuffer --U _PyObject_Bytes --U _PyObject_Call --U _PyObject_CallFunction --U _PyObject_CallFunctionObjArgs --U _PyObject_CallMethod --U _PyObject_CallMethodObjArgs --U _PyObject_CallNoArgs --U _PyObject_CallObject --U _PyObject_Calloc --U _PyObject_CheckBuffer --U _PyObject_CheckReadBuffer --U _PyObject_ClearWeakRefs --U _PyObject_CopyData --U _PyObject_DelAttr --U _PyObject_DelAttrString --U _PyObject_DelItem --U _PyObject_DelItemString --U _PyObject_Dir --U _PyObject_Format --U _PyObject_Free --U _PyObject_GetTypeData --U _PyObject_GetOptionalAttr --U _PyObject_GetOptionalAttrString --U _PyObject_GC_Del --U _PyObject_GC_IsFinalized --U _PyObject_GC_IsTracked --U _PyObject_GC_Track --U _PyObject_GC_UnTrack --U _PyObject_GenericGetAttr --U _PyObject_GenericGetDict --U _PyObject_GenericSetAttr --U _PyObject_GenericSetDict --U _PyObject_GetAIter --U _PyObject_GetAttr --U _PyObject_GetAttrString --U _PyObject_GetBuffer --U _PyObject_GetItem --U _PyObject_GetIter --U _PyObject_HasAttr --U _PyObject_HasAttrString --U _PyObject_Hash --U _PyObject_HashNotImplemented --U _PyObject_Init --U _PyObject_InitVar --U _PyObject_IsInstance --U _PyObject_IsSubclass --U _PyObject_IsTrue --U _PyObject_Length --U _PyObject_LengthHint --U _PyObject_Malloc --U _PyObject_Not --U _PyObject_Realloc --U _PyObject_Repr --U _PyObject_RichCompare --U _PyObject_RichCompareBool --U _PyObject_SelfIter --U _PyObject_SetAttr --U _PyObject_SetAttrString --U _PyObject_SetItem --U _PyObject_Size --U _PyObject_Str --U _PyObject_Type --U _PyObject_Vectorcall --U _PyObject_VectorcallMethod --U _PyParser_SimpleParseFileFlags --U _PyParser_SimpleParseStringFlags --U _PyParser_SimpleParseStringFlagsFilename --U _PyProperty_Type --U _PyRangeIter_Type --U _PyRange_Type --U _PyReversed_Type --U _PySeqIter_New --U _PySeqIter_Type --U _PySequence_Check --U _PySequence_Concat --U _PySequence_Contains --U _PySequence_Count --U _PySequence_DelItem --U _PySequence_DelSlice --U _PySequence_Fast --U _PySequence_GetItem --U _PySequence_GetSlice --U _PySequence_In --U _PySequence_InPlaceConcat --U _PySequence_InPlaceRepeat --U _PySequence_Index --U _PySequence_Length --U _PySequence_List --U _PySequence_Repeat --U _PySequence_SetItem --U _PySequence_SetSlice --U _PySequence_Size --U _PySequence_Tuple --U _PySetIter_Type --U _PySet_Add --U _PySet_Clear --U _PySet_Contains --U _PySet_Discard --U _PySet_New --U _PySet_Pop --U _PySet_Size --U _PySet_Type --U _PySlice_AdjustIndices --U _PySlice_GetIndices --U _PySlice_GetIndicesEx --U _PySlice_New --U _PySlice_Type --U _PySlice_Unpack --U _PySortWrapper_Type --U _PyState_AddModule --U _PyState_FindModule --U _PyState_RemoveModule --U _PyStructSequence_GetItem --U _PyStructSequence_New --U _PyStructSequence_NewType --U _PyStructSequence_SetItem --U _PySuper_Type --U _PySys_AddWarnOption --U _PySys_AddWarnOptionUnicode --U _PySys_AddXOption --U _PySys_FormatStderr --U _PySys_FormatStdout --U _PySys_GetObject --U _PySys_GetXOptions --U _PySys_HasWarnOptions --U _PySys_ResetWarnOptions --U _PySys_SetArgv --U _PySys_SetArgvEx --U _PySys_SetObject --U _PySys_SetPath --U _PySys_WriteStderr --U _PySys_WriteStdout --U _PyThreadState_Clear --U _PyThreadState_Delete --U _PyThreadState_DeleteCurrent --U _PyThreadState_Get --U _PyThreadState_GetDict --U _PyThreadState_GetFrame --U _PyThreadState_GetID --U _PyThreadState_GetInterpreter --U _PyThreadState_New --U _PyThreadState_SetAsyncExc --U _PyThreadState_Swap --U _PyThread_GetInfo --U _PyThread_ReInitTLS --U _PyThread_acquire_lock --U _PyThread_acquire_lock_timed --U _PyThread_allocate_lock --U _PyThread_create_key --U _PyThread_delete_key --U _PyThread_delete_key_value --U _PyThread_exit_thread --U _PyThread_free_lock --U _PyThread_get_key_value --U _PyThread_get_stacksize --U _PyThread_get_thread_ident --U _PyThread_get_thread_native_id --U _PyThread_init_thread --U _PyThread_release_lock --U _PyThread_set_key_value --U _PyThread_set_stacksize --U _PyThread_start_new_thread --U _PyThread_tss_alloc --U _PyThread_tss_create --U _PyThread_tss_delete --U _PyThread_tss_free --U _PyThread_tss_get --U _PyThread_tss_is_created --U _PyThread_tss_set --U _PyTraceBack_Here --U _PyTraceBack_Print --U _PyTraceBack_Type --U _PyTupleIter_Type --U _PyTuple_ClearFreeList --U _PyTuple_GetItem --U _PyTuple_GetSlice --U _PyTuple_New --U _PyTuple_Pack --U _PyTuple_SetItem --U _PyTuple_Size --U _PyTuple_Type --U _PyType_ClearCache --U _PyType_FromMetaclass --U _PyType_FromModuleAndSpec --U _PyType_FromSpec --U _PyType_FromSpecWithBases --U _PyType_GenericAlloc --U _PyType_GenericNew --U _PyType_GetFlags --U _PyType_GetModule --U _PyType_GetModuleState --U _PyType_GetName --U _PyType_GetQualName --U _PyType_GetSlot --U _PyType_IsSubtype --U _PyType_Modified --U _PyType_Ready --U _PyType_Type --U _PyType_GetTypeDataSize --U _PyUnicodeDecodeError_Create --U _PyUnicodeDecodeError_GetEncoding --U _PyUnicodeDecodeError_GetEnd --U _PyUnicodeDecodeError_GetObject --U _PyUnicodeDecodeError_GetReason --U _PyUnicodeDecodeError_GetStart --U _PyUnicodeDecodeError_SetEnd --U _PyUnicodeDecodeError_SetReason --U _PyUnicodeDecodeError_SetStart --U _PyUnicodeEncodeError_GetEncoding --U _PyUnicodeEncodeError_GetEnd --U _PyUnicodeEncodeError_GetObject --U _PyUnicodeEncodeError_GetReason --U _PyUnicodeEncodeError_GetStart --U _PyUnicodeEncodeError_SetEnd --U _PyUnicodeEncodeError_SetReason --U _PyUnicodeEncodeError_SetStart --U _PyUnicodeIter_Type --U _PyUnicodeTranslateError_GetEnd --U _PyUnicodeTranslateError_GetObject --U _PyUnicodeTranslateError_GetReason --U _PyUnicodeTranslateError_GetStart --U _PyUnicodeTranslateError_SetEnd --U _PyUnicodeTranslateError_SetReason --U _PyUnicodeTranslateError_SetStart --U _PyUnicode_Append --U _PyUnicode_AppendAndDel --U _PyUnicode_AsASCIIString --U _PyUnicode_AsCharmapString --U _PyUnicode_AsDecodedObject --U _PyUnicode_AsDecodedUnicode --U _PyUnicode_AsEncodedObject --U _PyUnicode_AsEncodedString --U _PyUnicode_AsEncodedUnicode --U _PyUnicode_AsLatin1String --U _PyUnicode_AsMBCSString --U _PyUnicode_AsRawUnicodeEscapeString --U _PyUnicode_AsUCS4 --U _PyUnicode_AsUCS4Copy --U _PyUnicode_AsUTF16String --U _PyUnicode_AsUTF32String --U _PyUnicode_AsUTF8AndSize --U _PyUnicode_AsUTF8String --U _PyUnicode_AsUnicodeEscapeString --U _PyUnicode_AsWideChar --U _PyUnicode_AsWideCharString --U _PyUnicode_BuildEncodingMap --U _PyUnicode_ClearFreeList --U _PyUnicode_Compare --U _PyUnicode_CompareWithASCIIString --U _PyUnicode_Concat --U _PyUnicode_Contains --U _PyUnicode_Count --U _PyUnicode_Decode --U _PyUnicode_DecodeASCII --U _PyUnicode_DecodeCharmap --U _PyUnicode_DecodeCodePageStateful --U _PyUnicode_DecodeFSDefault --U _PyUnicode_DecodeFSDefaultAndSize --U _PyUnicode_DecodeLatin1 --U _PyUnicode_DecodeLocale --U _PyUnicode_DecodeLocaleAndSize --U _PyUnicode_DecodeMBCS --U _PyUnicode_DecodeMBCSStateful --U _PyUnicode_DecodeRawUnicodeEscape --U _PyUnicode_DecodeUTF16 --U _PyUnicode_DecodeUTF16Stateful --U _PyUnicode_DecodeUTF32 --U _PyUnicode_DecodeUTF32Stateful --U _PyUnicode_DecodeUTF7 --U _PyUnicode_DecodeUTF7Stateful --U _PyUnicode_DecodeUTF8 --U _PyUnicode_DecodeUTF8Stateful --U _PyUnicode_DecodeUnicodeEscape --U _PyUnicode_EncodeCodePage --U _PyUnicode_EncodeFSDefault --U _PyUnicode_EncodeLocale --U _PyUnicode_FSConverter --U _PyUnicode_FSDecoder --U _PyUnicode_Find --U _PyUnicode_FindChar --U _PyUnicode_Format --U _PyUnicode_FromEncodedObject --U _PyUnicode_FromFormat --U _PyUnicode_FromFormatV --U _PyUnicode_FromObject --U _PyUnicode_FromOrdinal --U _PyUnicode_FromString --U _PyUnicode_FromStringAndSize --U _PyUnicode_FromWideChar --U _PyUnicode_GetDefaultEncoding --U _PyUnicode_GetLength --U _PyUnicode_GetSize --U _PyUnicode_InternFromString --U _PyUnicode_InternImmortal --U _PyUnicode_InternInPlace --U _PyUnicode_IsIdentifier --U _PyUnicode_Join --U _PyUnicode_Partition --U _PyUnicode_RPartition --U _PyUnicode_RSplit --U _PyUnicode_ReadChar --U _PyUnicode_Replace --U _PyUnicode_Resize --U _PyUnicode_RichCompare --U _PyUnicode_Split --U _PyUnicode_Splitlines --U _PyUnicode_Substring --U _PyUnicode_Tailmatch --U _PyUnicode_Translate --U _PyUnicode_Type --U _PyUnicode_WriteChar --U _PyVectorcall_Call --U _PyVectorcall_NARGS --U _PyWeakref_GetObject --U _PyWeakref_NewProxy --U _PyWeakref_NewRef --U _PyWrapperDescr_Type --U _PyWrapper_New --U _PyZip_Type --U _Py_AddPendingCall --U _Py_AtExit --U _Py_BuildValue --U _Py_BytesMain --U _Py_CompileString --U _Py_CompileStringExFlags --U _Py_DecRef --U _Py_DecodeLocale --U _Py_EncodeLocale --U _Py_EndInterpreter --U _Py_EnterRecursiveCall --U _Py_Exit --U _Py_FatalError --U _Py_FileSystemDefaultEncodeErrors --U _Py_FileSystemDefaultEncoding --U _Py_Finalize --U _Py_FinalizeEx --U _Py_GenericAlias --U _Py_GenericAliasType --U _Py_GetArgcArgv --U _Py_GetBuildInfo --U _Py_GetCompiler --U _Py_GetCopyright --U _Py_GetExecPrefix --U _Py_GetPath --U _Py_GetPlatform --U _Py_GetPrefix --U _Py_GetProgramFullPath --U _Py_GetProgramName --U _Py_GetPythonHome --U _Py_GetRecursionLimit --U _Py_GetVersion --U _Py_HasFileSystemDefaultEncoding --U _Py_IncRef --U _Py_Initialize --U _Py_InitializeEx --U _Py_Is --U _Py_IsFalse --U _Py_IsFinalizing --U _Py_IsInitialized --U _Py_IsNone --U _Py_IsTrue --U _Py_LeaveRecursiveCall --U _Py_Main --U _Py_MakePendingCalls --U _Py_NewInterpreter --U _Py_NewRef --U _Py_ReprEnter --U _Py_ReprLeave --U _Py_SetPath --U _Py_SetProgramName --U _Py_SetPythonHome --U _Py_SetRecursionLimit --U _Py_SymtableString --U _Py_UTF8Mode --U _Py_VaBuildValue --U _Py_XNewRef --U __PyArg_ParseTupleAndKeywords_SizeT --U __PyArg_ParseTuple_SizeT --U __PyArg_Parse_SizeT --U __PyArg_VaParseTupleAndKeywords_SizeT --U __PyArg_VaParse_SizeT --U __PyErr_BadInternalCall --U __PyInterpreterState_Get --U __PyObject_CallFunction_SizeT --U __PyObject_CallMethod_SizeT --U __PyObject_GC_Malloc --U __PyObject_GC_New --U __PyObject_GC_NewVar --U __PyObject_GC_Resize --U __PyObject_LookupAttr --U __PyObject_MakeTpCall --U __PyObject_New --U __PyObject_NewVar --U __PyObject_NextNotImplemented --U __PyState_AddModule --U __PyThreadState_Init --U __PyThreadState_Prealloc --U __PyTrash_delete_later --U __PyTrash_delete_nesting --U __PyTrash_deposit_object --U __PyTrash_destroy_chain --U __PyTrash_thread_deposit_object --U __PyTrash_thread_destroy_chain --U __PyWeakref_CallableProxyType --U __PyWeakref_ProxyType --U __PyWeakref_RefType --U __Py_IsFinalizing --U __Py_BuildValue_SizeT --U __Py_CheckFunctionResult --U __Py_CheckRecursionLimit --U __Py_CheckRecursiveCall --U __Py_Dealloc --U __Py_DecRef --U __Py_EllipsisObject --U __Py_FalseStruct --U __Py_IncRef --U __Py_NegativeRefcount --U __Py_NoneStruct --U __Py_NotImplementedStruct --U __Py_RefTotal --U __Py_SwappedOp --U __Py_TrueStruct --U __Py_VaBuildValue_SizeT --U _Py_Version --U __Py_MergeZeroLocalRefcount --U __Py_DecRefShared --U __Py_DecRefSharedDebug --U __Py_DECREF_DecRefTotal --U __Py_INCREF_IncRefTotal --U __PyObject_GetDictPtr --U _PyList_GetItemRef --U _PyDict_GetItemRef --U _PyDict_GetItemStringRef --U _PyDict_SetDefault --U _PyDict_SetDefaultRef --U _PyWeakref_GetRef --U _PyImport_AddModuleRef --U _PyUnstable_Module_SetGIL --U _PyMutex_Unlock --U _PyMutex_Lock --U _PyObject_IS_GC --U _PyCriticalSection_Begin --U _PyCriticalSection_End --U _PyCriticalSection2_Begin --U _PyCriticalSection2_End --U _PyUnicode_AsUTF8 --U _Py_REFCNT diff --git a/src/nanobind/cmake/darwin-ld-pypy.sym b/src/nanobind/cmake/darwin-ld-pypy.sym deleted file mode 100644 index 73ecb77..0000000 --- a/src/nanobind/cmake/darwin-ld-pypy.sym +++ /dev/null @@ -1,965 +0,0 @@ --U _PyArg_ValidateKeywordArguments --U _PyModule_AddType --U _PyPyAnySet_Check --U _PyPyAnySet_CheckExact --U _PyPyArg_Parse --U _PyPyArg_ParseTuple --U _PyPyArg_ParseTupleAndKeywords --U _PyPyArg_UnpackTuple --U _PyPyArg_VaParse --U _PyPyArg_VaParseTupleAndKeywords --U _PyPyBaseObject_Type --U _PyPyBool_FromLong --U _PyPyBool_Type --U _PyPyBuffer_FillInfo --U _PyPyBuffer_FromContiguous --U _PyPyBuffer_GetPointer --U _PyPyBuffer_IsContiguous --U _PyPyBuffer_Release --U _PyPyBuffer_ToContiguous --U _PyPyBufferable_Type --U _PyPyByteArray_AsString --U _PyPyByteArray_Check --U _PyPyByteArray_CheckExact --U _PyPyByteArray_Concat --U _PyPyByteArray_FromObject --U _PyPyByteArray_FromStringAndSize --U _PyPyByteArray_Resize --U _PyPyByteArray_Size --U _PyPyByteArray_Type --U _PyPyBytes_AS_STRING --U _PyPyBytes_AsString --U _PyPyBytes_AsStringAndSize --U _PyPyBytes_Concat --U _PyPyBytes_ConcatAndDel --U _PyPyBytes_FromFormat --U _PyPyBytes_FromFormatV --U _PyPyBytes_FromObject --U _PyPyBytes_FromString --U _PyPyBytes_FromStringAndSize --U _PyPyBytes_Size --U _PyPyBytes_Type --U _PyPyCFunction_Call --U _PyPyCFunction_Check --U _PyPyCFunction_GetFunction --U _PyPyCFunction_Type --U _PyPyCFunction_NewEx --U _PyPyCMethod_New --U _PyPyCallIter_New --U _PyPyCallable_Check --U _PyPyCapsule_GetContext --U _PyPyCapsule_GetDestructor --U _PyPyCapsule_GetName --U _PyPyCapsule_GetPointer --U _PyPyCapsule_Import --U _PyPyCapsule_IsValid --U _PyPyCapsule_New --U _PyPyCapsule_SetContext --U _PyPyCapsule_SetDestructor --U _PyPyCapsule_SetName --U _PyPyCapsule_SetPointer --U _PyPyCapsule_Type --U _PyPyCell_Type --U _PyPyClassMethodDescr_Type --U _PyPyClassMethod_New --U _PyPyClassMethod_Type --U _PyPyCode_Addr2Line --U _PyPyCode_Check --U _PyPyCode_CheckExact --U _PyPyCode_GetNumFree --U _PyPyCode_New --U _PyPyCode_NewEmpty --U _PyPyCodec_Decode --U _PyPyCodec_Decoder --U _PyPyCodec_Encode --U _PyPyCodec_Encoder --U _PyPyCodec_IncrementalDecoder --U _PyPyCodec_IncrementalEncoder --U _PyPyComplex_AsCComplex --U _PyPyComplex_Check --U _PyPyComplex_CheckExact --U _PyPyComplex_FromCComplex --U _PyPyComplex_FromDoubles --U _PyPyComplex_ImagAsDouble --U _PyPyComplex_RealAsDouble --U _PyPyComplex_Type --U _PyPyContextVar_Get --U _PyPyContextVar_New --U _PyPyContextVar_Set --U _PyPyCoro_Check --U _PyPyCoro_CheckExact --U _PyPyDateTimeAPI --U _PyPyDateTime_Check --U _PyPyDateTime_CheckExact --U _PyPyDateTime_DATE_GET_HOUR --U _PyPyDateTime_DATE_GET_MICROSECOND --U _PyPyDateTime_DATE_GET_MINUTE --U _PyPyDateTime_DATE_GET_SECOND --U _PyPyDateTime_DELTA_GET_DAYS --U _PyPyDateTime_DELTA_GET_MICROSECONDS --U _PyPyDateTime_DELTA_GET_SECONDS --U _PyPyDateTime_FromTimestamp --U _PyPyDateTime_GET_DAY --U _PyPyDateTime_GET_FOLD --U _PyPyDateTime_GET_MONTH --U _PyPyDateTime_GET_YEAR --U _PyPyDateTime_TIME_GET_FOLD --U _PyPyDateTime_TIME_GET_HOUR --U _PyPyDateTime_TIME_GET_MICROSECOND --U _PyPyDateTime_TIME_GET_MINUTE --U _PyPyDateTime_TIME_GET_SECOND --U _PyPyDate_Check --U _PyPyDate_CheckExact --U _PyPyDate_FromTimestamp --U _PyPyDelta_Check --U _PyPyDelta_CheckExact --U _PyPyDescr_NewClassMethod --U _PyPyDescr_NewGetSet --U _PyPyDescr_NewMethod --U _PyPyDictKeys_Type --U _PyPyDictProxy_Check --U _PyPyDictProxy_CheckExact --U _PyPyDictProxy_New --U _PyPyDictProxy_Type --U _PyPyDictValues_Type --U _PyPyDict_Clear --U _PyPyDict_Contains --U _PyPyDict_Copy --U _PyPyDict_DelItem --U _PyPyDict_DelItemString --U _PyPyDict_GetItem --U _PyPyDict_GetItemString --U _PyPyDict_GetItemWithError --U _PyPyDict_Items --U _PyPyDict_Keys --U _PyPyDict_Merge --U _PyPyDict_New --U _PyPyDict_Next --U _PyPyDict_SetDefault --U _PyPyDict_SetItem --U _PyPyDict_SetItemString --U _PyPyDict_Size --U _PyPyDict_Type --U _PyPyDict_Update --U _PyPyDict_Values --U _PyPyErr_BadArgument --U _PyPyErr_BadInternalCall --U _PyPyErr_CheckSignals --U _PyPyErr_Clear --U _PyPyErr_Display --U _PyPyErr_ExceptionMatches --U _PyPyErr_Fetch --U _PyPyErr_Format --U _PyPyErr_GetExcInfo --U _PyPyErr_GivenExceptionMatches --U _PyPyErr_NewException --U _PyPyErr_NewExceptionWithDoc --U _PyPyErr_NoMemory --U _PyPyErr_NormalizeException --U _PyPyErr_Occurred --U _PyPyErr_Print --U _PyPyErr_PrintEx --U _PyPyErr_Restore --U _PyPyErr_SetExcInfo --U _PyPyErr_SetFromErrno --U _PyPyErr_SetFromErrnoWithFilename --U _PyPyErr_SetFromErrnoWithFilenameObject --U _PyPyErr_SetFromErrnoWithFilenameObjects --U _PyPyErr_SetInterrupt --U _PyPyErr_SetNone --U _PyPyErr_SetObject --U _PyPyErr_SetString --U _PyPyErr_Warn --U _PyPyErr_WarnEx --U _PyPyErr_WarnExplicit --U _PyPyErr_WarnFormat --U _PyPyErr_WriteUnraisable --U _PyPyEval_AcquireThread --U _PyPyEval_CallFunction --U _PyPyEval_CallMethod --U _PyPyEval_CallObjectWithKeywords --U _PyPyEval_EvalCode --U _PyPyEval_GetBuiltins --U _PyPyEval_GetFrame --U _PyPyEval_GetGlobals --U _PyPyEval_GetLocals --U _PyPyEval_InitThreads --U _PyPyEval_MergeCompilerFlags --U _PyPyEval_ReleaseThread --U _PyPyEval_RestoreThread --U _PyPyEval_SaveThread --U _PyPyEval_ThreadsInitialized --U _PyPyExc_ArithmeticError --U _PyPyExc_AssertionError --U _PyPyExc_AttributeError --U _PyPyExc_BaseException --U _PyPyExc_BlockingIOError --U _PyPyExc_BrokenPipeError --U _PyPyExc_BufferError --U _PyPyExc_BytesWarning --U _PyPyExc_ChildProcessError --U _PyPyExc_ConnectionAbortedError --U _PyPyExc_ConnectionError --U _PyPyExc_ConnectionRefusedError --U _PyPyExc_ConnectionResetError --U _PyPyExc_DeprecationWarning --U _PyPyExc_EOFError --U _PyPyExc_Exception --U _PyPyExc_FileExistsError --U _PyPyExc_FileNotFoundError --U _PyPyExc_FloatingPointError --U _PyPyExc_FutureWarning --U _PyPyExc_GeneratorExit --U _PyPyExc_ImportError --U _PyPyExc_ImportWarning --U _PyPyExc_IndentationError --U _PyPyExc_IndexError --U _PyPyExc_InterruptedError --U _PyPyExc_IsADirectoryError --U _PyPyExc_KeyError --U _PyPyExc_KeyboardInterrupt --U _PyPyExc_LookupError --U _PyPyExc_MemoryError --U _PyPyExc_ModuleNotFoundError --U _PyPyExc_NameError --U _PyPyExc_NotADirectoryError --U _PyPyExc_NotImplementedError --U _PyPyExc_OSError --U _PyPyExc_OverflowError --U _PyPyExc_PendingDeprecationWarning --U _PyPyExc_PermissionError --U _PyPyExc_ProcessLookupError --U _PyPyExc_RecursionError --U _PyPyExc_ReferenceError --U _PyPyExc_ResourceWarning --U _PyPyExc_RuntimeError --U _PyPyExc_RuntimeWarning --U _PyPyExc_StopAsyncIteration --U _PyPyExc_StopIteration --U _PyPyExc_SyntaxError --U _PyPyExc_SyntaxWarning --U _PyPyExc_SystemError --U _PyPyExc_SystemExit --U _PyPyExc_TabError --U _PyPyExc_TimeoutError --U _PyPyExc_TypeError --U _PyPyExc_UnboundLocalError --U _PyPyExc_UnicodeDecodeError --U _PyPyExc_UnicodeEncodeError --U _PyPyExc_UnicodeError --U _PyPyExc_UnicodeTranslateError --U _PyPyExc_UnicodeWarning --U _PyPyExc_UserWarning --U _PyPyExc_ValueError --U _PyPyExc_Warning --U _PyPyExc_ZeroDivisionError --U _PyPyExceptionInstance_Class --U _PyPyException_GetCause --U _PyPyException_GetContext --U _PyPyException_GetTraceback --U _PyPyException_SetCause --U _PyPyException_SetContext --U _PyPyException_SetTraceback --U _PyPyFile_FromFd --U _PyPyFile_FromString --U _PyPyFile_GetLine --U _PyPyFile_WriteObject --U _PyPyFile_WriteString --U _PyPyFloat_AS_DOUBLE --U _PyPyFloat_AsDouble --U _PyPyFloat_Check --U _PyPyFloat_CheckExact --U _PyPyFloat_FromDouble --U _PyPyFloat_FromString --U _PyPyFloat_Type --U _PyPyFrame_New --U _PyPyFrozenSet_Check --U _PyPyFrozenSet_CheckExact --U _PyPyFrozenSet_New --U _PyPyFrozenSet_Type --U _PyPyFunction_Check --U _PyPyFunction_CheckExact --U _PyPyFunction_GetCode --U _PyPyFunction_Type --U _PyPyGILState_Check --U _PyPyGILState_Ensure --U _PyPyGILState_Release --U _PyPyGen_Check --U _PyPyGen_CheckExact --U _PyPyGetSetDescr_Type --U _PyPyImport_AddModule --U _PyPyImport_ExecCodeModule --U _PyPyImport_ExecCodeModuleEx --U _PyPyImport_GetModule --U _PyPyImport_GetModuleDict --U _PyPyImport_Import --U _PyPyImport_ImportModule --U _PyPyImport_ImportModuleLevelObject --U _PyPyImport_ImportModuleNoBlock --U _PyPyImport_ReloadModule --U _PyPyIndex_Check --U _PyPyInstanceMethod_Check --U _PyPyInstanceMethod_Function --U _PyPyInstanceMethod_GET_FUNCTION --U _PyPyInstanceMethod_New --U _PyPyInstanceMethod_Type --U _PyPyInterpreterState_GetID --U _PyPyInterpreterState_Head --U _PyPyInterpreterState_Next --U _PyPyIter_Check --U _PyPyIter_Next --U _PyPyList_Append --U _PyPyList_AsTuple --U _PyPyList_GET_ITEM --U _PyPyList_GET_SIZE --U _PyPyList_GetItem --U _PyPyList_GetSlice --U _PyPyList_Insert --U _PyPyList_New --U _PyPyList_Reverse --U _PyPyList_SET_ITEM --U _PyPyList_SetItem --U _PyPyList_SetSlice --U _PyPyList_Size --U _PyPyList_Sort --U _PyPyList_Type --U _PyPyLong_AsDouble --U _PyPyLong_AsLong --U _PyPyLong_AsLongAndOverflow --U _PyPyLong_AsLongLong --U _PyPyLong_AsLongLongAndOverflow --U _PyPyLong_AsSize_t --U _PyPyLong_AsSsize_t --U _PyPyLong_AsUnsignedLong --U _PyPyLong_AsUnsignedLongLong --U _PyPyLong_AsUnsignedLongLongMask --U _PyPyLong_AsUnsignedLongMask --U _PyPyLong_AsVoidPtr --U _PyPyLong_FromDouble --U _PyPyLong_FromLong --U _PyPyLong_FromLongLong --U _PyPyLong_FromSize_t --U _PyPyLong_FromSsize_t --U _PyPyLong_FromString --U _PyPyLong_FromUnicode --U _PyPyLong_FromUnicodeObject --U _PyPyLong_FromUnsignedLong --U _PyPyLong_FromUnsignedLongLong --U _PyPyLong_FromVoidPtr --U _PyPyLong_Type --U _PyPyMapping_Check --U _PyPyMapping_GetItemString --U _PyPyMapping_HasKey --U _PyPyMapping_HasKeyString --U _PyPyMapping_Items --U _PyPyMapping_Keys --U _PyPyMapping_Length --U _PyPyMapping_SetItemString --U _PyPyMapping_Size --U _PyPyMapping_Values --U _PyPyMarshal_ReadObjectFromString --U _PyPyMarshal_WriteObjectToString --U _PyPyMem_Calloc --U _PyPyMem_Free --U _PyPyMem_Malloc --U _PyPyMem_RawCalloc --U _PyPyMem_RawFree --U _PyPyMem_RawMalloc --U _PyPyMem_RawRealloc --U _PyPyMem_Realloc --U _PyPyMemberDescr_Type --U _PyPyMember_GetOne --U _PyPyMember_SetOne --U _PyPyMemoryView_Check --U _PyPyMemoryView_CheckExact --U _PyPyMemoryView_FromBuffer --U _PyPyMemoryView_FromMemory --U _PyPyMemoryView_FromObject --U _PyPyMemoryView_GetContiguous --U _PyPyMemoryView_Type --U _PyPyMethodDescr_Check --U _PyPyMethodDescr_CheckExact --U _PyPyMethodDescr_Type --U _PyPyMethod_Check --U _PyPyMethod_CheckExact --U _PyPyMethod_Function --U _PyPyMethod_New --U _PyPyMethod_Self --U _PyPyMethod_Type --U _PyPyModuleDef_Init --U _PyPyModule_AddFunctions --U _PyPyModule_AddIntConstant --U _PyPyModule_AddObject --U _PyPyModule_AddStringConstant --U _PyPyModule_Check --U _PyPyModule_CheckExact --U _PyPyModule_Create2 --U _PyPyModule_ExecDef --U _PyPyModule_GetDef --U _PyPyModule_GetDict --U _PyPyModule_GetName --U _PyPyModule_GetState --U _PyPyModule_New --U _PyPyModule_NewObject --U _PyPyModule_Type --U _PyPyNumber_Absolute --U _PyPyNumber_Add --U _PyPyNumber_And --U _PyPyNumber_AsSsize_t --U _PyPyNumber_Check --U _PyPyNumber_Divide --U _PyPyNumber_Divmod --U _PyPyNumber_Float --U _PyPyNumber_FloorDivide --U _PyPyNumber_InPlaceAdd --U _PyPyNumber_InPlaceAnd --U _PyPyNumber_InPlaceDivide --U _PyPyNumber_InPlaceFloorDivide --U _PyPyNumber_InPlaceLshift --U _PyPyNumber_InPlaceMatrixMultiply --U _PyPyNumber_InPlaceMultiply --U _PyPyNumber_InPlaceOr --U _PyPyNumber_InPlacePower --U _PyPyNumber_InPlaceRemainder --U _PyPyNumber_InPlaceRshift --U _PyPyNumber_InPlaceSubtract --U _PyPyNumber_InPlaceTrueDivide --U _PyPyNumber_InPlaceXor --U _PyPyNumber_Index --U _PyPyNumber_Invert --U _PyPyNumber_Long --U _PyPyNumber_Lshift --U _PyPyNumber_MatrixMultiply --U _PyPyNumber_Multiply --U _PyPyNumber_Negative --U _PyPyNumber_Or --U _PyPyNumber_Positive --U _PyPyNumber_Power --U _PyPyNumber_Remainder --U _PyPyNumber_Rshift --U _PyPyNumber_Subtract --U _PyPyNumber_ToBase --U _PyPyNumber_TrueDivide --U _PyPyNumber_Xor --U _PyPyOS_AfterFork --U _PyPyOS_FSPath --U _PyPyOS_InputHook --U _PyPyOS_InterruptOccurred --U _PyPyOS_double_to_string --U _PyPyOS_getsig --U _PyPyOS_setsig --U _PyPyOS_snprintf --U _PyPyOS_string_to_double --U _PyPyOS_vsnprintf --U _PyPyObject_ASCII --U _PyPyObject_AsCharBuffer --U _PyPyObject_AsFileDescriptor --U _PyPyObject_AsReadBuffer --U _PyPyObject_AsWriteBuffer --U _PyPyObject_Bytes --U _PyPyObject_Call --U _PyPyObject_CallFinalizerFromDealloc --U _PyPyObject_CallFunction --U _PyPyObject_CallFunctionObjArgs --U _PyPyObject_CallMethod --U _PyPyObject_CallMethodNoArgs --U _PyPyObject_CallMethodObjArgs --U _PyPyObject_CallMethodOneArg --U _PyPyObject_CallNoArgs --U _PyPyObject_CallObject --U _PyPyObject_CallOneArg --U _PyPyObject_Calloc --U _PyPyObject_CheckReadBuffer --U _PyPyObject_ClearWeakRefs --U _PyPyObject_Del --U _PyPyObject_DelAttr --U _PyPyObject_DelAttrString --U _PyPyObject_DelItem --U _PyPyObject_DelItemString --U _PyPyObject_Dir --U _PyPyObject_Format --U _PyPyObject_Free --U _PyPyObject_GC_Del --U _PyPyObject_GenericGetAttr --U _PyPyObject_GenericGetDict --U _PyPyObject_GenericSetAttr --U _PyPyObject_GenericSetDict --U _PyPyObject_GetAttr --U _PyPyObject_GetAttrString --U _PyPyObject_GetBuffer --U _PyPyObject_GetItem --U _PyPyObject_GetIter --U _PyPyObject_HasAttr --U _PyPyObject_HasAttrString --U _PyPyObject_Hash --U _PyPyObject_HashNotImplemented --U _PyPyObject_Init --U _PyPyObject_InitVar --U _PyPyObject_IsInstance --U _PyPyObject_IsSubclass --U _PyPyObject_IsTrue --U _PyPyObject_LengthHint --U _PyPyObject_Malloc --U _PyPyObject_Not --U _PyPyObject_Print --U _PyPyObject_Realloc --U _PyPyObject_Repr --U _PyPyObject_RichCompare --U _PyPyObject_RichCompareBool --U _PyPyObject_SelfIter --U _PyPyObject_SetAttr --U _PyPyObject_SetAttrString --U _PyPyObject_SetItem --U _PyPyObject_Size --U _PyPyObject_Str --U _PyPyObject_Type --U _PyPyObject_Unicode --U _PyPyObject_Vectorcall --U _PyPyObject_VectorcallDict --U _PyPyObject_VectorcallMethod --U _PyPyProperty_Type --U _PyPyRange_Type --U _PyPyReversed_Type --U _PyPyRun_File --U _PyPyRun_SimpleString --U _PyPyRun_String --U _PyPyRun_StringFlags --U _PyPySeqIter_New --U _PyPySequence_Check --U _PyPySequence_Concat --U _PyPySequence_Contains --U _PyPySequence_DelItem --U _PyPySequence_DelSlice --U _PyPySequence_Fast --U _PyPySequence_Fast_GET_ITEM --U _PyPySequence_Fast_GET_SIZE --U _PyPySequence_Fast_ITEMS --U _PyPySequence_GetItem --U _PyPySequence_GetSlice --U _PyPySequence_ITEM --U _PyPySequence_InPlaceConcat --U _PyPySequence_InPlaceRepeat --U _PyPySequence_Index --U _PyPySequence_Length --U _PyPySequence_List --U _PyPySequence_Repeat --U _PyPySequence_SetItem --U _PyPySequence_SetSlice --U _PyPySequence_Size --U _PyPySequence_Tuple --U _PyPySet_Add --U _PyPySet_Check --U _PyPySet_CheckExact --U _PyPySet_Clear --U _PyPySet_Contains --U _PyPySet_Discard --U _PyPySet_GET_SIZE --U _PyPySet_New --U _PyPySet_Pop --U _PyPySet_Size --U _PyPySet_Type --U _PyPySlice_AdjustIndices --U _PyPySlice_GetIndices --U _PyPySlice_GetIndicesEx --U _PyPySlice_New --U _PyPySlice_Type --U _PyPySlice_Unpack --U _PyPyState_AddModule --U _PyPyState_RemoveModule --U _PyPyStaticMethod_New --U _PyPyStaticMethod_Type --U _PyPyStructSequence_GetItem --U _PyPyStructSequence_InitType --U _PyPyStructSequence_InitType2 --U _PyPyStructSequence_New --U _PyPyStructSequence_NewType --U _PyPyStructSequence_SetItem --U _PyPyStructSequence_UnnamedField --U _PyPySys_GetObject --U _PyPySys_SetObject --U _PyPySys_WriteStderr --U _PyPySys_WriteStdout --U _PyPyTZInfo_Check --U _PyPyTZInfo_CheckExact --U _PyPyThreadState_Clear --U _PyPyThreadState_Delete --U _PyPyThreadState_DeleteCurrent --U _PyPyThreadState_Get --U _PyPyThreadState_GetDict --U _PyPyThreadState_New --U _PyPyThreadState_SetAsyncExc --U _PyPyThreadState_Swap --U _PyPyThread_ReInitTLS --U _PyPyThread_acquire_lock --U _PyPyThread_allocate_lock --U _PyPyThread_create_key --U _PyPyThread_delete_key --U _PyPyThread_delete_key_value --U _PyPyThread_exit_thread --U _PyPyThread_free_lock --U _PyPyThread_get_key_value --U _PyPyThread_get_thread_ident --U _PyPyThread_init_thread --U _PyPyThread_release_lock --U _PyPyThread_set_key_value --U _PyPyThread_start_new_thread --U _PyPyTime_Check --U _PyPyTime_CheckExact --U _PyPyTraceBack_Check --U _PyPyTraceBack_Here --U _PyPyTraceBack_Print --U _PyPyTraceBack_Type --U _PyPyTraceMalloc_Track --U _PyPyTraceMalloc_Untrack --U _PyPyTuple_GetItem --U _PyPyTuple_GetSlice --U _PyPyTuple_New --U _PyPyTuple_Pack --U _PyPyTuple_SetItem --U _PyPyTuple_Size --U _PyPyTuple_Type --U _PyPyType_FromModuleAndSpec --U _PyPyType_FromSpec --U _PyPyType_FromSpecWithBases --U _PyPyType_GenericAlloc --U _PyPyType_GenericNew --U _PyPyType_GetModule --U _PyPyType_GetModuleState --U _PyPyType_GetName --U _PyPyType_GetSlot --U _PyPyType_IsSubtype --U _PyPyType_Modified --U _PyPyType_Ready --U _PyPyType_Type --U _PyPyUnicode_Append --U _PyPyUnicode_AppendAndDel --U _PyPyUnicode_AsASCIIString --U _PyPyUnicode_AsEncodedObject --U _PyPyUnicode_AsEncodedString --U _PyPyUnicode_AsLatin1String --U _PyPyUnicode_AsUCS4 --U _PyPyUnicode_AsUCS4Copy --U _PyPyUnicode_AsUTF16String --U _PyPyUnicode_AsUTF32String --U _PyPyUnicode_AsUTF8 --U _PyPyUnicode_AsUTF8AndSize --U _PyPyUnicode_AsUTF8String --U _PyPyUnicode_AsUnicode --U _PyPyUnicode_AsUnicodeAndSize --U _PyPyUnicode_AsUnicodeEscapeString --U _PyPyUnicode_AsWideChar --U _PyPyUnicode_AsWideCharString --U _PyPyUnicode_Check --U _PyPyUnicode_CheckExact --U _PyPyUnicode_Compare --U _PyPyUnicode_CompareWithASCIIString --U _PyPyUnicode_Concat --U _PyPyUnicode_Contains --U _PyPyUnicode_Count --U _PyPyUnicode_Decode --U _PyPyUnicode_DecodeASCII --U _PyPyUnicode_DecodeFSDefault --U _PyPyUnicode_DecodeFSDefaultAndSize --U _PyPyUnicode_DecodeLatin1 --U _PyPyUnicode_DecodeLocale --U _PyPyUnicode_DecodeLocaleAndSize --U _PyPyUnicode_DecodeUTF16 --U _PyPyUnicode_DecodeUTF32 --U _PyPyUnicode_DecodeUTF8 --U _PyPyUnicode_EncodeASCII --U _PyPyUnicode_EncodeDecimal --U _PyPyUnicode_EncodeFSDefault --U _PyPyUnicode_EncodeLatin1 --U _PyPyUnicode_EncodeLocale --U _PyPyUnicode_EncodeUTF8 --U _PyPyUnicode_FSConverter --U _PyPyUnicode_FSDecoder --U _PyPyUnicode_Find --U _PyPyUnicode_FindChar --U _PyPyUnicode_Format --U _PyPyUnicode_FromEncodedObject --U _PyPyUnicode_FromFormat --U _PyPyUnicode_FromFormatV --U _PyPyUnicode_FromKindAndData --U _PyPyUnicode_FromObject --U _PyPyUnicode_FromOrdinal --U _PyPyUnicode_FromString --U _PyPyUnicode_FromStringAndSize --U _PyPyUnicode_FromUnicode --U _PyPyUnicode_FromWideChar --U _PyPyUnicode_GetDefaultEncoding --U _PyPyUnicode_GetLength --U _PyPyUnicode_GetMax --U _PyPyUnicode_GetSize --U _PyPyUnicode_InternFromString --U _PyPyUnicode_InternInPlace --U _PyPyUnicode_Join --U _PyPyUnicode_New --U _PyPyUnicode_ReadChar --U _PyPyUnicode_Replace --U _PyPyUnicode_Resize --U _PyPyUnicode_Split --U _PyPyUnicode_Splitlines --U _PyPyUnicode_Substring --U _PyPyUnicode_Tailmatch --U _PyPyUnicode_TransformDecimalToASCII --U _PyPyUnicode_Type --U _PyPyUnicode_WriteChar --U _PyPyVectorcall_Call --U _PyPyWeakref_Check --U _PyPyWeakref_CheckProxy --U _PyPyWeakref_CheckRef --U _PyPyWeakref_CheckRefExact --U _PyPyWeakref_GET_OBJECT --U _PyPyWeakref_GetObject --U _PyPyWeakref_LockObject --U _PyPyWeakref_NewProxy --U _PyPyWeakref_NewRef --U _PyPyWrapperDescr_Type --U _PyPy_AddPendingCall --U _PyPy_AtExit --U _PyPy_BuildValue --U _PyPy_BytesWarningFlag --U _PyPy_CompileStringFlags --U _PyPy_DebugFlag --U _PyPy_DecRef --U _PyPy_DontWriteBytecodeFlag --U _PyPy_EnterRecursiveCall --U _PyPy_FatalError --U _PyPy_FindMethod --U _PyPy_FrozenFlag --U _PyPy_GenericAlias --U _PyPy_GetProgramName --U _PyPy_GetRecursionLimit --U _PyPy_GetVersion --U _PyPy_HashRandomizationFlag --U _PyPy_IgnoreEnvironmentFlag --U _PyPy_IncRef --U _PyPy_InspectFlag --U _PyPy_InteractiveFlag --U _PyPy_IsInitialized --U _PyPy_IsolatedFlag --U _PyPy_LeaveRecursiveCall --U _PyPy_MakePendingCalls --U _PyPy_NoSiteFlag --U _PyPy_NoUserSiteDirectory --U _PyPy_OptimizeFlag --U _PyPy_QuietFlag --U _PyPy_ReprEnter --U _PyPy_ReprLeave --U _PyPy_SetRecursionLimit --U _PyPy_UNICODE_COPY --U _PyPy_UNICODE_ISALNUM --U _PyPy_UNICODE_ISALPHA --U _PyPy_UNICODE_ISDECIMAL --U _PyPy_UNICODE_ISDIGIT --U _PyPy_UNICODE_ISLINEBREAK --U _PyPy_UNICODE_ISLOWER --U _PyPy_UNICODE_ISNUMERIC --U _PyPy_UNICODE_ISSPACE --U _PyPy_UNICODE_ISTITLE --U _PyPy_UNICODE_ISUPPER --U _PyPy_UNICODE_TODECIMAL --U _PyPy_UNICODE_TODIGIT --U _PyPy_UNICODE_TOLOWER --U _PyPy_UNICODE_TONUMERIC --U _PyPy_UNICODE_TOTITLE --U _PyPy_UNICODE_TOUPPER --U _PyPy_UnbufferedStdioFlag --U _PyPy_VaBuildValue --U _PyPy_VerboseFlag --U _PySlice_AdjustIndices --U _PyState_FindModule --U _PyThread_tss_alloc --U _PyThread_tss_create --U _PyThread_tss_delete --U _PyThread_tss_free --U _PyThread_tss_get --U _PyThread_tss_is_created --U _PyThread_tss_set --U _PyType_GetFlags --U _Py_FileSystemDefaultEncoding --U __PyArg_BadArgument --U __PyArg_CheckPositional --U __PyArg_NoKeywords --U __PyArg_NoKwnames --U __PyArg_NoPositional --U __PyArg_ParseStack --U __PyArg_ParseStackAndKeywords --U __PyArg_ParseStackAndKeywords_SizeT --U __PyArg_ParseStack_SizeT --U __PyArg_ParseTupleAndKeywordsFast --U __PyArg_ParseTupleAndKeywordsFast_SizeT --U __PyArg_UnpackKeywords --U __PyArg_UnpackStack --U __PyArg_VaParseTupleAndKeywordsFast --U __PyArg_VaParseTupleAndKeywordsFast_SizeT --U __PyExc_ArithmeticError --U __PyExc_AssertionError --U __PyExc_AttributeError --U __PyExc_BaseException --U __PyExc_BlockingIOError --U __PyExc_BrokenPipeError --U __PyExc_BufferError --U __PyExc_BytesWarning --U __PyExc_ChildProcessError --U __PyExc_ConnectionAbortedError --U __PyExc_ConnectionError --U __PyExc_ConnectionRefusedError --U __PyExc_ConnectionResetError --U __PyExc_DeprecationWarning --U __PyExc_EOFError --U __PyExc_Exception --U __PyExc_FileExistsError --U __PyExc_FileNotFoundError --U __PyExc_FloatingPointError --U __PyExc_FutureWarning --U __PyExc_GeneratorExit --U __PyExc_ImportError --U __PyExc_ImportWarning --U __PyExc_IndentationError --U __PyExc_IndexError --U __PyExc_InterruptedError --U __PyExc_IsADirectoryError --U __PyExc_KeyError --U __PyExc_KeyboardInterrupt --U __PyExc_LookupError --U __PyExc_MemoryError --U __PyExc_ModuleNotFoundError --U __PyExc_NameError --U __PyExc_NotADirectoryError --U __PyExc_NotImplementedError --U __PyExc_OSError --U __PyExc_OverflowError --U __PyExc_PendingDeprecationWarning --U __PyExc_PermissionError --U __PyExc_ProcessLookupError --U __PyExc_RecursionError --U __PyExc_ReferenceError --U __PyExc_ResourceWarning --U __PyExc_RuntimeError --U __PyExc_RuntimeWarning --U __PyExc_StopAsyncIteration --U __PyExc_StopIteration --U __PyExc_SyntaxError --U __PyExc_SyntaxWarning --U __PyExc_SystemError --U __PyExc_SystemExit --U __PyExc_TabError --U __PyExc_TimeoutError --U __PyExc_TypeError --U __PyExc_UnboundLocalError --U __PyExc_UnicodeDecodeError --U __PyExc_UnicodeEncodeError --U __PyExc_UnicodeError --U __PyExc_UnicodeTranslateError --U __PyExc_UnicodeWarning --U __PyExc_UserWarning --U __PyExc_ValueError --U __PyExc_Warning --U __PyExc_ZeroDivisionError --U __PyLong_AsTime_t --U __PyLong_FromTime_t --U __PyPyArg_ParseTupleAndKeywords_SizeT --U __PyPyArg_ParseTuple_SizeT --U __PyPyArg_Parse_SizeT --U __PyPyArg_VaParseTupleAndKeywords_SizeT --U __PyPyArg_VaParse_SizeT --U __PyPyBytes_Eq --U __PyPyBytes_Join --U __PyPyBytes_Resize --U __PyPyComplex_AsCComplex --U __PyPyComplex_FromCComplex --U __PyPyDateTime_FromDateAndTime --U __PyPyDateTime_FromDateAndTimeAndFold --U __PyPyDateTime_FromTimestamp --U __PyPyDateTime_Import --U __PyPyDate_FromDate --U __PyPyDate_FromTimestamp --U __PyPyDelta_FromDelta --U __PyPyDict_GetItemStringWithError --U __PyPyDict_HasOnlyStringKeys --U __PyPyErr_FormatFromCause --U __PyPyErr_WriteUnraisableMsg --U __PyPyEval_SliceIndex --U __PyPyFloat_Unpack4 --U __PyPyFloat_Unpack8 --U __PyPyImport_AcquireLock --U __PyPyImport_ReleaseLock --U __PyPyList_Extend --U __PyPyLong_AsByteArrayO --U __PyPyLong_FromByteArray --U __PyPyLong_NumBits --U __PyPyLong_Sign --U __PyPyNamespace_New --U __PyPyNone_Type --U __PyPyNotImplemented_Type --U __PyPyObject_CallFunction_SizeT --U __PyPyObject_CallMethod_SizeT --U __PyPyObject_FastCall --U __PyPyObject_GC_Malloc --U __PyPyObject_GC_New --U __PyPyObject_GC_NewVar --U __PyPyObject_GetDictPtr --U __PyPyObject_New --U __PyPyObject_NewVar --U __PyPyObject_Vectorcall --U __PyPyPyGC_AddMemoryPressure --U __PyPyPy_Free --U __PyPyPy_Malloc --U __PyPySet_Next --U __PyPySet_NextEntry --U __PyPyThreadState_UncheckedGet --U __PyPyTimeZone_FromTimeZone --U __PyPyTime_FromTime --U __PyPyTime_FromTimeAndFold --U __PyPyTuple_Resize --U __PyPyType_Lookup --U __PyPyUnicode_EQ --U __PyPyUnicode_EqualToASCIIString --U __PyPyUnicode_Ready --U __PyPy_BuildValue_SizeT --U __PyPy_Dealloc --U __PyPy_EllipsisObject --U __PyPy_FalseStruct --U __PyPy_HashDouble --U __PyPy_HashPointer --U __PyPy_IsFinalizing --U __PyPy_NoneStruct --U __PyPy_NotImplementedStruct --U __PyPy_PackageContext --U __PyPy_RestoreSignals --U __PyPy_TrueStruct --U __PyPy_VaBuildValue_SizeT --U __PyPy_get_PyOS_InputHook --U __PyPy_get_capsule_type --U __PyPy_object_dealloc --U __PyPy_setfilesystemdefaultencoding --U __PyPy_strhex --U __PyPy_strhex_bytes --U __PyPy_subtype_dealloc --U __PyPy_tuple_dealloc --U __PyPy_tuple_new --U __PyTime_AsMicroseconds --U __PyTime_AsMilliseconds --U __PyTime_AsNanosecondsObject --U __PyTime_AsSecondsDouble --U __PyTime_AsTimeval --U __PyTime_AsTimevalTime_t --U __PyTime_AsTimeval_noraise --U __PyTime_FromMillisecondsObject --U __PyTime_FromNanoseconds --U __PyTime_FromNanosecondsObject --U __PyTime_FromSeconds --U __PyTime_FromSecondsObject --U __PyTime_GetMonotonicClock --U __PyTime_GetMonotonicClockWithInfo --U __PyTime_GetSystemClock --U __PyTime_GetSystemClockWithInfo --U __PyTime_Init --U __PyTime_ObjectToTime_t --U __PyTime_ObjectToTimespec --U __PyTime_ObjectToTimeval --U __PyTime_gmtime --U __PyTime_localtime --U __PyType_Name diff --git a/src/nanobind/cmake/darwin-python-path.py b/src/nanobind/cmake/darwin-python-path.py deleted file mode 100644 index c9d216f..0000000 --- a/src/nanobind/cmake/darwin-python-path.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env python3 - -# On macOS, system python binaries like /usr/bin/python and $(xcrun -f python3) -# are shims. They do some light validation work and then spawn the "real" python -# binary. Find the "real" python by asking dyld -- sys.executable reports the -# wrong thing more often than not. This is also useful when we're running under -# a Homebrew python3 binary, which also appears to be some kind of shim. -# (Taken from https://reviews.llvm.org/D79607) - -import ctypes -dyld = ctypes.cdll.LoadLibrary('/usr/lib/system/libdyld.dylib') -namelen = ctypes.c_ulong(1024) -name = ctypes.create_string_buffer(b'\000', namelen.value) -dyld._NSGetExecutablePath(ctypes.byref(name), ctypes.byref(namelen)) -print(name.value.decode('utf8').strip()) diff --git a/src/nanobind/cmake/nanobind-config.cmake b/src/nanobind/cmake/nanobind-config.cmake deleted file mode 100644 index 3053285..0000000 --- a/src/nanobind/cmake/nanobind-config.cmake +++ /dev/null @@ -1,681 +0,0 @@ -include_guard(GLOBAL) - -if (NOT TARGET Python::Module) - message(FATAL_ERROR "You must invoke 'find_package(Python COMPONENTS Interpreter Development REQUIRED)' prior to including nanobind.") -endif() - -# Determine the right suffix for ordinary and stable ABI extensions. - -# We always need to know the extension -if(WIN32) - set(NB_SUFFIX_EXT ".pyd") -else() - set(NB_SUFFIX_EXT "${CMAKE_SHARED_MODULE_SUFFIX}") -endif() - -# Check if FindPython/scikit-build-core defined a SOABI/SOSABI variable -if(DEFINED SKBUILD_SOABI) - set(NB_SOABI "${SKBUILD_SOABI}") -elseif(DEFINED Python_SOABI) - set(NB_SOABI "${Python_SOABI}") -endif() - -if(DEFINED SKBUILD_SOSABI) - set(NB_SOSABI "${SKBUILD_SOSABI}") -elseif(DEFINED Python_SOSABI) - set(NB_SOSABI "${Python_SOSABI}") -endif() - -# Error if scikit-build-core is trying to build Stable ABI < 3.12 wheels -if(DEFINED SKBUILD_SABI_VERSION AND SKBUILD_ABI_VERSION AND SKBUILD_SABI_VERSION VERSION_LESS "3.12") - message(FATAL_ERROR "You must set tool.scikit-build.wheel.py-api to 'cp312' or later when " - "using scikit-build-core with nanobind, '${SKBUILD_SABI_VERSION}' is too old.") -endif() - -# PyPy sets an invalid SOABI (platform missing), causing older FindPythons to -# report an incorrect value. Only use it if it looks correct (X-X-X form). -if(DEFINED NB_SOABI AND "${NB_SOABI}" MATCHES ".+-.+-.+") - set(NB_SUFFIX ".${NB_SOABI}${NB_SUFFIX_EXT}") -endif() - -if(DEFINED NB_SOSABI) - if(NB_SOSABI STREQUAL "") - set(NB_SUFFIX_S "${NB_SUFFIX_EXT}") - else() - set(NB_SUFFIX_S ".${NB_SOSABI}${NB_SUFFIX_EXT}") - endif() -endif() - -# Extract Python version and extensions (e.g. free-threaded build) -string(REGEX REPLACE "[^-]*-([^-]*)-.*" "\\1" NB_ABI "${NB_SOABI}") - -# 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. - execute_process( - COMMAND "${Python_EXECUTABLE}" "-c" - "import sysconfig; print(sysconfig.get_config_var('EXT_SUFFIX'))" - RESULT_VARIABLE NB_SUFFIX_RET - OUTPUT_VARIABLE EXT_SUFFIX - OUTPUT_STRIP_TRAILING_WHITESPACE) - - if(NB_SUFFIX_RET AND NOT NB_SUFFIX_RET EQUAL 0) - message(FATAL_ERROR "nanobind: Python sysconfig query to " - "find 'EXT_SUFFIX' property failed!") - endif() - - if(NOT DEFINED NB_SUFFIX) - set(NB_SUFFIX "${EXT_SUFFIX}") - endif() - - if(NOT DEFINED NB_SUFFIX_S) - get_filename_component(NB_SUFFIX_EXT "${EXT_SUFFIX}" LAST_EXT) - if(WIN32) - set(NB_SUFFIX_S "${NB_SUFFIX_EXT}") - else() - set(NB_SUFFIX_S ".abi3${NB_SUFFIX_EXT}") - endif() - endif() -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 "") - -get_filename_component(NB_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) -get_filename_component(NB_DIR "${NB_DIR}" PATH) - -set(NB_DIR ${NB_DIR} CACHE INTERNAL "") -set(NB_OPT $,$> CACHE INTERNAL "") -set(NB_OPT_SIZE $,$,$> CACHE INTERNAL "") - -# --------------------------------------------------------------------------- -# Helper function to handle undefined CPython API symbols on macOS -# --------------------------------------------------------------------------- - -function (nanobind_link_options name) - if (APPLE) - if (Python_INTERPRETER_ID STREQUAL "PyPy") - set(NB_LINKER_RESPONSE_FILE darwin-ld-pypy.sym) - else() - set(NB_LINKER_RESPONSE_FILE darwin-ld-cpython.sym) - endif() - target_link_options(${name} PRIVATE "-Wl,@${NB_DIR}/cmake/${NB_LINKER_RESPONSE_FILE}") - endif() -endfunction() - -# --------------------------------------------------------------------------- -# Create shared/static library targets for nanobind's non-templated core -# --------------------------------------------------------------------------- - -function (nanobind_build_library TARGET_NAME) - cmake_parse_arguments(PARSE_ARGV 1 ARG - "AS_SYSINCLUDE" "" "") - - if (TARGET ${TARGET_NAME}) - return() - endif() - - if (TARGET_NAME MATCHES "-static") - set (TARGET_TYPE STATIC) - else() - set (TARGET_TYPE SHARED) - endif() - - if (${ARG_AS_SYSINCLUDE}) - set (AS_SYSINCLUDE SYSTEM) - endif() - - add_library(${TARGET_NAME} ${TARGET_TYPE} - EXCLUDE_FROM_ALL - ${NB_DIR}/include/nanobind/make_iterator.h - ${NB_DIR}/include/nanobind/nanobind.h - ${NB_DIR}/include/nanobind/nb_accessor.h - ${NB_DIR}/include/nanobind/nb_attr.h - ${NB_DIR}/include/nanobind/nb_call.h - ${NB_DIR}/include/nanobind/nb_cast.h - ${NB_DIR}/include/nanobind/nb_class.h - ${NB_DIR}/include/nanobind/nb_defs.h - ${NB_DIR}/include/nanobind/nb_descr.h - ${NB_DIR}/include/nanobind/nb_enums.h - ${NB_DIR}/include/nanobind/nb_error.h - ${NB_DIR}/include/nanobind/nb_func.h - ${NB_DIR}/include/nanobind/nb_lib.h - ${NB_DIR}/include/nanobind/nb_misc.h - ${NB_DIR}/include/nanobind/nb_python.h - ${NB_DIR}/include/nanobind/nb_traits.h - ${NB_DIR}/include/nanobind/nb_tuple.h - ${NB_DIR}/include/nanobind/nb_types.h - ${NB_DIR}/include/nanobind/ndarray.h - ${NB_DIR}/include/nanobind/trampoline.h - ${NB_DIR}/include/nanobind/typing.h - ${NB_DIR}/include/nanobind/operators.h - ${NB_DIR}/include/nanobind/stl/array.h - ${NB_DIR}/include/nanobind/stl/bind_map.h - ${NB_DIR}/include/nanobind/stl/bind_vector.h - ${NB_DIR}/include/nanobind/stl/detail - ${NB_DIR}/include/nanobind/stl/detail/nb_array.h - ${NB_DIR}/include/nanobind/stl/detail/nb_dict.h - ${NB_DIR}/include/nanobind/stl/detail/nb_list.h - ${NB_DIR}/include/nanobind/stl/detail/nb_set.h - ${NB_DIR}/include/nanobind/stl/detail/traits.h - ${NB_DIR}/include/nanobind/stl/filesystem.h - ${NB_DIR}/include/nanobind/stl/function.h - ${NB_DIR}/include/nanobind/stl/list.h - ${NB_DIR}/include/nanobind/stl/map.h - ${NB_DIR}/include/nanobind/stl/optional.h - ${NB_DIR}/include/nanobind/stl/pair.h - ${NB_DIR}/include/nanobind/stl/set.h - ${NB_DIR}/include/nanobind/stl/shared_ptr.h - ${NB_DIR}/include/nanobind/stl/string.h - ${NB_DIR}/include/nanobind/stl/string_view.h - ${NB_DIR}/include/nanobind/stl/tuple.h - ${NB_DIR}/include/nanobind/stl/unique_ptr.h - ${NB_DIR}/include/nanobind/stl/unordered_map.h - ${NB_DIR}/include/nanobind/stl/unordered_set.h - ${NB_DIR}/include/nanobind/stl/variant.h - ${NB_DIR}/include/nanobind/stl/vector.h - ${NB_DIR}/include/nanobind/eigen/dense.h - ${NB_DIR}/include/nanobind/eigen/sparse.h - - ${NB_DIR}/src/buffer.h - ${NB_DIR}/src/hash.h - ${NB_DIR}/src/nb_internals.h - ${NB_DIR}/src/nb_internals.cpp - ${NB_DIR}/src/nb_func.cpp - ${NB_DIR}/src/nb_type.cpp - ${NB_DIR}/src/nb_enum.cpp - ${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 (TARGET_TYPE STREQUAL "SHARED") - nanobind_link_options(${TARGET_NAME}) - target_compile_definitions(${TARGET_NAME} PRIVATE -DNB_BUILD) - target_compile_definitions(${TARGET_NAME} PUBLIC -DNB_SHARED) - nanobind_lto(${TARGET_NAME}) - - nanobind_strip(${TARGET_NAME}) - elseif(NOT WIN32 AND NOT APPLE) - target_compile_options(${TARGET_NAME} PUBLIC $<${NB_OPT_SIZE}:-ffunction-sections -fdata-sections>) - target_link_options(${TARGET_NAME} PUBLIC $<${NB_OPT_SIZE}:-Wl,--gc-sections>) - endif() - - set_target_properties(${TARGET_NAME} PROPERTIES - POSITION_INDEPENDENT_CODE ON) - - if (${ARG_AS_SYSINCLUDE}) - set_target_properties(${TARGET_NAME} PROPERTIES - CXX_CLANG_TIDY "") - endif() - - if (MSVC) - # Do not complain about vsnprintf - target_compile_definitions(${TARGET_NAME} PRIVATE -D_CRT_SECURE_NO_WARNINGS) - else() - # Generally needed to handle type punning in Python code - target_compile_options(${TARGET_NAME} PRIVATE -fno-strict-aliasing) - endif() - - if (WIN32) - if (${TARGET_NAME} MATCHES "-abi3") - target_link_libraries(${TARGET_NAME} PUBLIC Python::SABIModule) - else() - target_link_libraries(${TARGET_NAME} PUBLIC Python::Module) - endif() - endif() - - if (TARGET_NAME MATCHES "-ft") - target_compile_definitions(${TARGET_NAME} PUBLIC NB_FREE_THREADED) - endif() - - # Nanobind performs many assertion checks -- detailed error messages aren't - # included in Release/MinSizeRel modes - target_compile_definitions(${TARGET_NAME} PRIVATE - $<${NB_OPT_SIZE}:NB_COMPACT_ASSERTIONS>) - - # If nanobind was installed without submodule dependencies, then the - # dependencies directory won't exist and we need to find them. - # 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)) - 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) - endif() - - target_include_directories(${TARGET_NAME} ${AS_SYSINCLUDE} PUBLIC - ${Python_INCLUDE_DIRS} - ${NB_DIR}/include) - - target_compile_features(${TARGET_NAME} PUBLIC cxx_std_17) - nanobind_set_visibility(${TARGET_NAME}) - - if (MSVC) - # warning #1388-D: base class dllexport/dllimport specification differs from that of the derived class - target_compile_options(${TARGET_NAME} PUBLIC $<$:-Xcudafe --diag_suppress=1388>) - endif() -endfunction() - -# --------------------------------------------------------------------------- -# Define a convenience function for creating nanobind targets -# --------------------------------------------------------------------------- - -function(nanobind_opt_size name) - if (MSVC) - target_compile_options(${name} PRIVATE $<${NB_OPT_SIZE}:$<$:/Os>>) - else() - target_compile_options(${name} PRIVATE $<${NB_OPT_SIZE}:$<$:-Os>>) - endif() -endfunction() - -function(nanobind_disable_stack_protector name) - if (NOT MSVC) - # The stack protector affects binding size negatively (+8% on Linux in my - # benchmarks). Protecting from stack smashing in a Python VM seems in any - # case futile, so let's get rid of it by default in optimized modes. - target_compile_options(${name} PRIVATE $<${NB_OPT}:-fno-stack-protector>) - endif() -endfunction() - -function(nanobind_extension name) - set_target_properties(${name} PROPERTIES PREFIX "" SUFFIX "${NB_SUFFIX}") -endfunction() - -function(nanobind_extension_abi3 name) - set_target_properties(${name} PROPERTIES PREFIX "" SUFFIX "${NB_SUFFIX_S}") -endfunction() - -function (nanobind_lto name) - set_target_properties(${name} PROPERTIES - INTERPROCEDURAL_OPTIMIZATION_RELEASE ON - INTERPROCEDURAL_OPTIMIZATION_MINSIZEREL ON) -endfunction() - -function (nanobind_compile_options name) - if (MSVC) - target_compile_options(${name} PRIVATE $<$:/bigobj /MP>) - endif() -endfunction() - -function (nanobind_strip name) - if (APPLE) - target_link_options(${name} PRIVATE $<${NB_OPT}:-Wl,-dead_strip -Wl,-x -Wl,-S>) - elseif (NOT WIN32) - target_link_options(${name} PRIVATE $<${NB_OPT}:-Wl,-s>) - endif() -endfunction() - -function (nanobind_set_visibility name) - set_target_properties(${name} PROPERTIES CXX_VISIBILITY_PRESET hidden) -endfunction() - -function (nanobind_musl_static_libcpp name) - if ("$ENV{AUDITWHEEL_PLAT}" MATCHES "musllinux") - target_link_options(${name} PRIVATE -static-libstdc++ -static-libgcc) - endif() -endfunction() - -function(nanobind_add_module name) - cmake_parse_arguments(PARSE_ARGV 1 ARG - "STABLE_ABI;FREE_THREADED;NB_STATIC;NB_SHARED;PROTECT_STACK;LTO;NOMINSIZE;NOSTRIP;MUSL_DYNAMIC_LIBCPP;NB_SUPPRESS_WARNINGS" - "NB_DOMAIN" "") - - add_library(${name} MODULE ${ARG_UNPARSED_ARGUMENTS}) - - nanobind_compile_options(${name}) - nanobind_link_options(${name}) - set_target_properties(${name} PROPERTIES LINKER_LANGUAGE CXX) - - if (ARG_NB_SHARED AND ARG_NB_STATIC) - message(FATAL_ERROR "NB_SHARED and NB_STATIC cannot be specified at the same time!") - elseif (NOT ARG_NB_SHARED) - set(ARG_NB_STATIC TRUE) - endif() - - # Stable ABI builds require CPython >= 3.12 and Python::SABIModule - if ((Python_VERSION VERSION_LESS 3.12) OR - (NOT Python_INTERPRETER_ID STREQUAL "Python") OR - (NOT TARGET Python::SABIModule)) - set(ARG_STABLE_ABI FALSE) - endif() - - if (NB_ABI MATCHES "t") - # Free-threaded Python interpreters don't support building a nanobind - # module that uses the stable ABI. - set(ARG_STABLE_ABI FALSE) - else() - # A free-threaded Python interpreter is required to build a free-threaded - # nanobind module. - set(ARG_FREE_THREADED FALSE) - endif() - - set(libname "nanobind") - if (ARG_NB_STATIC) - set(libname "${libname}-static") - endif() - - if (ARG_STABLE_ABI) - set(libname "${libname}-abi3") - endif() - - if (ARG_FREE_THREADED) - set(libname "${libname}-ft") - endif() - - if (ARG_NB_DOMAIN AND ARG_NB_SHARED) - set(libname ${libname}-${ARG_NB_DOMAIN}) - endif() - - if (ARG_NB_SUPPRESS_WARNINGS) - set(EXTRA_LIBRARY_PARAMS AS_SYSINCLUDE) - endif() - - nanobind_build_library(${libname} ${EXTRA_LIBRARY_PARAMS}) - - if (ARG_NB_DOMAIN) - target_compile_definitions(${name} PRIVATE NB_DOMAIN=${ARG_NB_DOMAIN}) - endif() - - if (ARG_STABLE_ABI) - target_compile_definitions(${libname} PUBLIC -DPy_LIMITED_API=0x030C0000) - nanobind_extension_abi3(${name}) - else() - nanobind_extension(${name}) - endif() - - if (ARG_FREE_THREADED) - target_compile_definitions(${name} PRIVATE NB_FREE_THREADED) - endif() - - target_link_libraries(${name} PRIVATE ${libname}) - - if (NOT ARG_PROTECT_STACK) - nanobind_disable_stack_protector(${name}) - endif() - - if (NOT ARG_NOMINSIZE) - nanobind_opt_size(${name}) - endif() - - if (NOT ARG_NOSTRIP) - nanobind_strip(${name}) - endif() - - if (ARG_LTO) - nanobind_lto(${name}) - endif() - - if (ARG_NB_STATIC AND NOT ARG_MUSL_DYNAMIC_LIBCPP) - nanobind_musl_static_libcpp(${name}) - endif() - - nanobind_set_visibility(${name}) -endfunction() - -# --------------------------------------------------------------------------- -# Detect if a list of targets uses sanitizers (ASAN/UBSAN/TSAN). If so, compute -# a shared library preload directive so that these sanitizers can be safely -# together with a Python binary that will in general not import them. -# --------------------------------------------------------------------------- - -function(nanobind_sanitizer_preload_env env_var) - set(detected_san "") - - # Process each target - foreach(target ${ARGN}) - if (NOT TARGET ${target}) - continue() - endif() - - # Check for sanitizer flags in various compile and link options - set(san_flags "") - set(san_options_to_search - COMPILE_OPTIONS LINK_OPTIONS - INTERFACE_LINK_OPTIONS INTERFACE_COMPILE_OPTIONS - ) - if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.30") - set(san_options_to_search - ${san_options_to_search} - TRANSITIVE_LINK_PROPERTIES - TRANSITIVE_COMPILE_PROPERTIES - ) - endif() - - # create a list of all dependent targets and scan those for dependencies on sanitizers - set(all_deps "${target}") - get_target_property(deps ${target} LINK_LIBRARIES) - if(deps AND NOT deps STREQUAL "deps-NOTFOUND") - foreach(dep ${deps}) - if(NOT "${dep}" IN_LIST all_deps) - list(APPEND all_deps "${dep}") - endif() - endforeach() - endif() - - foreach(tgt ${all_deps}) - # Check target type - get_target_property(target_type ${tgt} TYPE) - - foreach(prop ${san_options_to_search}) - # Skip non-interface properties for INTERFACE_LIBRARY targets - if(target_type STREQUAL "INTERFACE_LIBRARY") - if(NOT prop MATCHES "^INTERFACE_") - continue() - endif() - endif() - - get_target_property(options ${tgt} ${prop}) - if(options) - foreach(opt ${options}) - if(opt MATCHES "-fsanitize=([^ ]+)") - list(APPEND san_flags "${CMAKE_MATCH_1}") - endif() - endforeach() - endif() - endforeach() - endforeach() - - # Parse sanitizer flags - foreach(flag ${san_flags}) - string(REPLACE "\"" "" flag "${flag}") - string(REPLACE "," ";" san_list "${flag}") - foreach(san ${san_list}) - if(san MATCHES "^(address|asan)$") - list(APPEND detected_san "asan") - elseif(san MATCHES "^(thread|tsan)$") - list(APPEND detected_san "tsan") - elseif(san MATCHES "^(undefined|ubsan)$") - list(APPEND detected_san "ubsan") - endif() - endforeach() - endforeach() - endforeach() - - if (detected_san) - list(REMOVE_DUPLICATES detected_san) - set(libs "") - - foreach(san ${detected_san}) - set(san_libname "") - - if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") - if(APPLE) - set(san_libname "libclang_rt.${san}_osx_dynamic.dylib") - else() - set(san_libname "libclang_rt.${san}.so") - endif() - else() - set(san_libname "lib${san}.so") - endif() - - # Get the full path using a file name query - execute_process( - COMMAND ${CMAKE_CXX_COMPILER} -print-file-name=${san_libname} - RESULT_VARIABLE san_success - OUTPUT_VARIABLE san_libpath - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - - if(NOT san_success EQUAL 0) - message(FATAL_ERROR "Error querying ${san_libname}: ${san_success}") - endif() - - # Check if a real path was returned (and not just echoing back the input) - if(NOT san_libpath OR (san_libpath STREQUAL san_libname)) - continue() - endif() - - # Read the file content and turn into a single-line string - file(READ "${san_libpath}" san_libdata LIMIT 1024) - string(REPLACE "\n" " " san_libdata "${san_libdata}") - - if(san_libdata MATCHES "INPUT[ \t]*\\([ \t]*([^ \t)]+)") - # If this is a linker script with INPUT directive, extract the path - list(APPEND libs "${CMAKE_MATCH_1}") - else() - # Use the original library path - list(APPEND libs "${san_libpath}") - endif() - endforeach() - - # Set platform-specific environment variable - string(REPLACE ";" ":" libs_str "${libs}") - if(APPLE) - set(${env_var} "DYLD_INSERT_LIBRARIES=${libs_str}" PARENT_SCOPE) - else() - set(${env_var} "LD_PRELOAD=${libs_str}" PARENT_SCOPE) - endif() - else() - set(${env_var} "" PARENT_SCOPE) - endif() -endfunction() - -# On macOS, it's quite tricky to get the actual path of the Python executable -# which is often hidden behind several layers of shims. We need this path to -# inject sanitizers. -function(nanobind_resolve_python_path) - if(NOT DEFINED NB_PY_PATH) - if (APPLE) - execute_process( - COMMAND ${Python_EXECUTABLE} "${NB_DIR}/cmake/darwin-python-path.py" - RESULT_VARIABLE rv - OUTPUT_VARIABLE NB_PY_PATH - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - if(NOT rv EQUAL 0) - message(FATAL_ERROR "Could not query Python binary path") - endif() - else() - set(NB_PY_PATH "${Python_EXECUTABLE}") - endif() - set(NB_PY_PATH ${NB_PY_PATH} CACHE STRING "" FORCE) - endif() -endfunction() - -# --------------------------------------------------------------------------- -# Convenient Cmake frontent for nanobind's stub generator -# --------------------------------------------------------------------------- - -function (nanobind_add_stub name) - cmake_parse_arguments(PARSE_ARGV 1 ARG "VERBOSE;INCLUDE_PRIVATE;EXCLUDE_DOCSTRINGS;INSTALL_TIME;EXCLUDE_FROM_ALL" "MODULE;OUTPUT;MARKER_FILE;COMPONENT;PATTERN_FILE" "PYTHON_PATH;DEPENDS") - - if (EXISTS ${NB_DIR}/src/stubgen.py) - set(NB_STUBGEN "${NB_DIR}/src/stubgen.py") - elseif (EXISTS ${NB_DIR}/stubgen.py) - set(NB_STUBGEN "${NB_DIR}/stubgen.py") - else() - message(FATAL_ERROR "nanobind_add_stub(): could not locate 'stubgen.py'!") - endif() - - if (NOT ARG_VERBOSE) - list(APPEND NB_STUBGEN_ARGS -q) - else() - set(NB_STUBGEN_EXTRA USES_TERMINAL) - endif() - - if (ARG_INCLUDE_PRIVATE) - list(APPEND NB_STUBGEN_ARGS -P) - endif() - - if (ARG_EXCLUDE_DOCSTRINGS) - list(APPEND NB_STUBGEN_ARGS -D) - endif() - - foreach (TMP IN LISTS ARG_PYTHON_PATH) - list(APPEND NB_STUBGEN_ARGS -i "${TMP}") - endforeach() - - if (ARG_PATTERN_FILE) - list(APPEND NB_STUBGEN_ARGS -p "${ARG_PATTERN_FILE}") - endif() - - if (ARG_MARKER_FILE) - list(APPEND NB_STUBGEN_ARGS -M "${ARG_MARKER_FILE}") - list(APPEND NB_STUBGEN_OUTPUTS "${ARG_MARKER_FILE}") - endif() - - if (NOT ARG_MODULE) - message(FATAL_ERROR "nanobind_add_stub(): a 'MODULE' argument must be specified!") - else() - list(APPEND NB_STUBGEN_ARGS -m "${ARG_MODULE}") - endif() - - if (NOT ARG_OUTPUT) - message(FATAL_ERROR "nanobind_add_stub(): an 'OUTPUT' argument must be specified!") - else() - list(APPEND NB_STUBGEN_ARGS -o "${ARG_OUTPUT}") - list(APPEND NB_STUBGEN_OUTPUTS "${ARG_OUTPUT}") - endif() - - file(TO_CMAKE_PATH ${Python_EXECUTABLE} NB_Python_EXECUTABLE) - - set(NB_STUBGEN_CMD "${NB_Python_EXECUTABLE}" "${NB_STUBGEN}" ${NB_STUBGEN_ARGS}) - - if (NOT WIN32) - # Pass sanitizer flags to nanobind if needed - nanobind_sanitizer_preload_env(NB_STUBGEN_ENV ${ARG_DEPENDS}) - if (NB_STUBGEN_ENV) - nanobind_resolve_python_path() - if (NB_STUBGEN_ENV MATCHES asan) - list(APPEND NB_STUBGEN_ENV "ASAN_OPTIONS=detect_leaks=0") - endif() - set(NB_STUBGEN_CMD ${CMAKE_COMMAND} -E env "${NB_STUBGEN_ENV}" "${NB_PY_PATH}" "${NB_STUBGEN}" ${NB_STUBGEN_ARGS}) - endif() - endif() - - if (NOT ARG_INSTALL_TIME) - add_custom_command( - OUTPUT ${NB_STUBGEN_OUTPUTS} - COMMAND ${NB_STUBGEN_CMD} - WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" - DEPENDS ${ARG_DEPENDS} "${NB_STUBGEN}" "${ARG_PATTERN_FILE}" - ${NB_STUBGEN_EXTRA} - ) - add_custom_target(${name} ALL DEPENDS ${NB_STUBGEN_OUTPUTS}) - else() - set(NB_STUBGEN_EXTRA "") - if (ARG_COMPONENT) - list(APPEND NB_STUBGEN_EXTRA COMPONENT ${ARG_COMPONENT}) - endif() - if (ARG_EXCLUDE_FROM_ALL) - list(APPEND NB_STUBGEN_EXTRA EXCLUDE_FROM_ALL) - endif() - # \${CMAKE_INSTALL_PREFIX} has same effect as $ - # This is for compatibility with CMake < 3.27. - # For more info: https://github.com/wjakob/nanobind/issues/420#issuecomment-1971353531 - install(CODE "set(CMD \"${NB_STUBGEN_CMD}\")\nexecute_process(\n COMMAND \$\{CMD\}\n WORKING_DIRECTORY \"\${CMAKE_INSTALL_PREFIX}\"\n)" ${NB_STUBGEN_EXTRA}) - endif() -endfunction() diff --git a/src/nanobind/docs/api_bazel.rst b/src/nanobind/docs/api_bazel.rst deleted file mode 100644 index 6aca46e..0000000 --- a/src/nanobind/docs/api_bazel.rst +++ /dev/null @@ -1,185 +0,0 @@ -.. _api_bazel: - -Bazel API Reference (3rd party) -=============================== - -This page contains a reference of the basic APIs of -`nanobind-bazel `__. - -.. _rules-bazel: - -Rules ------ - -nanobind-bazel's rules can be used to declare different types of targets in -your Bazel project. Each of these rules is a thin wrapper around a -corresponding builtin Bazel rule producing the equivalent C++ target. - -The main tool to build nanobind extensions is the ``nanobind_extension`` rule. - -.. py:function:: nanobind_extension - - Declares a Bazel target representing a nanobind extension, which contains - the Python bindings of your C++ code. - - .. code-block:: python - - def nanobind_extension( - name, - domain = "", - srcs = [], - copts = [], - deps = [], - local_defines = [], - **kwargs): - - It corresponds directly to the builtin - `cc_binary `__ rule, - with all keyword arguments being directly forwarded to a ``cc_binary`` - target. - - The ``domain`` argument can be used to build the target extension under a - different ABI domain, as described in the :ref:`FAQ ` - section. - -To generate typing stubs for an extension, you can use the ``nanobind_stubgen`` -rule. - -.. py:function:: nanobind_stubgen - - Declares a Bazel target for generating a stub file from a previously - built nanobind bindings extension. - - .. code-block:: python - - def nanobind_stubgen( - name, - module, - output_file = None, - output_directory = None, - imports = [], - pattern_file = None, - marker_file = None, - include_private_members = False, - 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, - a typing marker file into ``output_directory`` (defaults to - the build output directory, commonly called "bindir" in Bazel terms). - - All arguments (except the name, which is used only to refer to the target - in Bazel) correspond directly to nanobind's stubgen command line interface, - which is described in more detail in the :ref:`typing documentation `. - - *New in nanobind-bazel version 2.1.0.* - - *New in nanobind-bazel v2.5.0: Added the "output_directory" and "recursive" - keyword arguments.* - -To build a C++ library with nanobind as a dependency, use the -``nanobind_library`` rule. - -.. py:function:: nanobind_library - - Declares a Bazel target representing a C++ library depending on nanobind. - - .. code-block:: python - - def nanobind_library( - name, - copts = [], - deps = [], - **kwargs): - - It corresponds directly to the builtin - `cc_library `__ rule, - with all keyword arguments being directly forwarded to a ``cc_library`` - target. - -To build a C++ shared library with nanobind as a dependency, use the -``nanobind_shared_library`` rule. - -.. py:function:: nanobind_shared_library - - Declares a Bazel target representing a C++ shared library depending on - nanobind. - - .. code-block:: python - - def nanobind_shared_library( - name, - deps = [], - **kwargs): - - It corresponds directly to the builtin - `cc_shared_library `__ - rule, with all keyword arguments being directly forwarded to a - ``cc_shared_library`` target. - - *New in nanobind-bazel version 2.1.0.* - -To build a C++ static library containing nanobind, use the -``nanobind_static_library`` rule. - -.. py:function:: nanobind_static_library - - Declares a Bazel target representing a static library (or archive) containing - nanobind. - - .. code-block:: python - - def nanobind_static_library(name, deps, **kwargs): - - It corresponds directly to the builtin - `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 - ``cc_static_library`` rule is considered experimental. - - *New in nanobind-bazel version 2.7.0.* - -To build a C++ test target requiring nanobind, use the ``nanobind_test`` rule. - -.. py:function:: nanobind_test - - Declares a Bazel target representing a C++ test depending on nanobind. - - .. code-block:: python - - def nanobind_test( - name, - copts = [], - deps = [], - **kwargs): - - It corresponds directly to the builtin - `cc_test `__ rule, with all - keyword arguments being directly forwarded to a ``cc_test`` target. - -.. _flags-bazel: - -Flags ------ - -To customize some of nanobind's build options, nanobind-bazel exposes the -following flag settings. - -.. py:function:: @nanobind_bazel//:minsize (boolean) - - Apply nanobind's size optimizations to the built extensions. Size - optimizations are turned on by default, similarly to the CMake build. - To turn off size optimizations, you can use the shorthand notation - ``--no@nanobind_bazel//:minsize``. - -.. py:function:: @nanobind_bazel//:py-limited-api (string) - - 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. - By default, all extensions are built without any ABI limitations. diff --git a/src/nanobind/docs/api_cmake.rst b/src/nanobind/docs/api_cmake.rst deleted file mode 100644 index 1547535..0000000 --- a/src/nanobind/docs/api_cmake.rst +++ /dev/null @@ -1,491 +0,0 @@ -.. _api_cmake: - -CMake API Reference -=================== - -nanobind's CMake API simplifies the process of building python extension -modules. This is needed because quite a few steps are involved: nanobind must -build the module, a library component, link the two together, and add a -different set of compilation and linker flags depending on the target platform. - -If you prefer another build system, then you have the following options: - -- `Nicholas Junge `__ has created a `Bazel - interface `__ to nanobind. - Please report Bazel-specific issues there. - -- `Will Ayd `__ has created a `Meson WrapDB - package `__ for nanobind. Please - report Meson-specific issues on the `Meson WrapDB - `__ repository. - -- You could create a new build system from scratch that takes care of these - steps. See `this file - `__ for - inspiration on how to do this on Linux. Note that you will be on your own if - you choose to go this route---I unfortunately do not have the time to respond - to GitHub tickets related to custom build systems. - -The section on :ref:`building extensions ` provided an introductory -example of how to set up a basic build system via the -:cmake:command:`nanobind_add_module` command, which is the :ref:`high level -` build interface. The defaults chosen by this function are -somewhat opinionated, however. For this reason, nanobind also provides an -alternative :ref:`low level ` interface that decomposes it into -smaller steps. - -A later part of this section explains how a Git submodule dependency can be -:ref:`avoided ` in exchange for a system-provided package. - -Finally, the section ends with an explanation of the CMake convenience -interface for :ref:`stub generation `. - -.. _highlevel-cmake: - -High-level interface --------------------- - -The high-level interface consists of just one CMake command: - -.. cmake:command:: nanobind_add_module - - Compile a nanobind extension module using the specified target name, - optional flags, and source code files. Use it as follows: - - .. code-block:: cmake - - nanobind_add_module( - my_ext # Target name - NB_STATIC STABLE_ABI LTO # Optional flags (see below) - my_ext.h # Source code files below - my_ext.cpp) - - It supports the following optional parameters: - - .. list-table:: - - * - ``STABLE_ABI`` - - Perform a `stable ABI - `__ build, making it - possible to use a compiled extension across Python minor versions. - The flag is ignored on Python versions older than < 3.12. - * - ``FREE_THREADED`` - - Compile an Python extension that opts into free-threaded (i.e., - GIL-less) Python behavior, which requires a special free-threaded - build of Python 3.13 or newer. The flag is ignored on unsupported - Python versions. - * - ``NB_STATIC`` - - Compile the core nanobind library as a static library. This - simplifies redistribution but can increase the combined binary - storage footprint when a project contains many Python extensions - (this is the default). - * - ``NB_SHARED`` - - The opposite of ``NB_STATIC``: compile the core nanobind library - as a shared library for use in projects that consist of multiple - extensions. - * - ``NB_SUPPRESS_WARNINGS`` - - Mark the include directories of nanobind and Python as - `SYSTEM `__ - include directories, which suppresses any potential warning messages - originating there. This is mainly of relevance if your project artificially - raises the warning level via flags like ``-pedantic``, ``-Wcast-qual``, - ``-Wsign-conversion``. - * - ``PROTECT_STACK`` - - Don't remove stack smashing-related protections. - * - ``LTO`` - - Perform link time optimization. - * - ``NOMINSIZE`` - - Don't perform optimizations to minimize binary size. - * - ``NOSTRIP`` - - Don't strip unneded symbols and debug information from the compiled - extension when performing release builds. - * - ``NB_DOMAIN `` - - Restrict the inter-extension type visibility to a named subdomain. - See the associated :ref:`FAQ entry ` for details. - * - ``MUSL_DYNAMIC_LIBCPP`` - - When `cibuildwheel - `__ is used to - produce `musllinux `__ wheels, - don't statically link against ``libstdc++`` and ``libgcc`` (which is - an optimization that nanobind does by default in this specific case). - If this explanation sounds confusing, then you can ignore it. See the - detailed description below for more information on this step. - - :cmake:command:`nanobind_add_module` performs the following - steps to produce bindings. - - - It creates a CMake library via ``add_library(target_name MODULE ...)`` and - enables the use of C++17 features during compilation. - - - It creates a CMake target for an internal library component required by - nanobind (named ``nanobind-..`` where ``..`` depends on the compilation - flags). This is only done once when compiling multiple extensions. - - This library component can either be a static or shared library depending - on whether the optional ``NB_STATIC`` or ``NB_SHARED`` parameter was - provided to ``nanobind_add_module()``. The default is a static build, - which simplifies redistribution (only one shared library must be deployed). - - When a project contains many Python extensions, a shared build is - preferable to avoid unnecessary binary size overheads that arise from - redundant copies of the ``nanobind-...`` component. - - - It links the newly created library against the ``nanobind-..`` target. - - - It appends the library suffix (e.g., ``.cpython-39-darwin.so``) based - on information provided by CMake’s ``FindPython`` module. - - - When requested via the optional ``STABLE_ABI`` parameter, the build system - will create a `stable ABI `_ - extension module with a different suffix (e.g., ``.abi3.so``). - - Once compiled, a stable ABI extension can be reused across Python minor - versions. In contrast, ordinary builds are only compatible across patch - versions. This feature requires Python >= 3.12 and is ignored on older - versions. Note that use of the stable ABI come at a small performance cost - since nanobind can no longer access the internals of various data - structures directly. If in doubt, benchmark your code to see if the cost - is acceptable. - - - In non-debug modes, it compiles with *size optimizations* (i.e., - ``-Os``). This is generally the mode that you will want to use for - C++/Python bindings. Switching to ``-O3`` would enable further - optimizations like vectorization, loop unrolling, etc., but these all - increase compilation time and binary size with no real benefit for - bindings. - - If your project contains portions that benefit from ``-O3``-level - optimizations, then it’s better to run two separate compilation - steps. An example is shown below: - - .. code:: cmake - - # Compile project code with current optimization mode configured in CMake - add_library(example_lib STATIC source_1.cpp source_2.cpp) - # Need position independent code (-fPIC) to link into 'example_ext' below - set_target_properties(example_lib PROPERTIES POSITION_INDEPENDENT_CODE ON) - - # Compile extension module with size optimization and add 'example_lib' - nanobind_add_module(example_ext common.h source_1.cpp source_2.cpp) - target_link_libraries(example_ext PRIVATE example_lib) - - Size optimizations can be disabled by specifying the optional - ``NOMINSIZE`` argument, though doing so is not recommended. - - - ``nanobind_add_module()`` also disables stack-smashing protections - (i.e., it specifies ``-fno-stack-protector`` to Clang/GCC). - Protecting against such vulnerabilities in a Python VM seems futile, - and it adds non-negligible extra cost (+8% binary size in - benchmarks). This behavior can be disabled by specifying the optional - ``PROTECT_STACK`` flag. Either way, is not recommended that you use - nanobind in a setting where it presents an attack surface. - - - It sets the default symbol visibility to ``hidden`` so that only functions - and types specifically marked for export generate symbols in the resulting - binary. This substantially reduces the size of the generated binary. - - - In release builds, it strips unreferenced functions and debug information - names from the resulting binary. This can substantially reduce the size of - the generated binary and can be disabled using the optional ``NOSTRIP`` - argument. - - - Link-time optimization (LTO) is *not active* by default; benefits compared - to pybind11 are relatively low, and this can make linking a build - bottleneck. That said, the optional ``LTO`` argument can be specified to - enable LTO in release builds. - - - nanobind's CMake build system is often combined with `cibuildwheel - `__ to automate the - generation of wheels for many different platforms. One such platform - called `musllinux `__ exists to create - tiny self-contained binaries that are cheap to install in a container - environment (Docker, etc.). An issue of the combination with nanobind is - that ``musllinux`` doesn't include the ``libstdc++`` and ``libgcc`` - libraries which nanobind depends on. ``cibuildwheel`` then has to ship - those along in each wheel, which actually increases their size rather - dramatically (by a factor of >5x for small projects). To avoid this, - nanobind prefers to link against these libraries *statically* when it - detects a ``cibuildwheel`` build targeting ``musllinux``. Pass the - ``MUSL_DYNAMIC_LIBCPP`` parameter to avoid this behavior. - - - If desired (via the optional ``NB_DOMAIN`` parameter), nanobind will - restrict the visibility of symbols to a named subdomain to avoid conflicts - between bindings. See the associated :ref:`FAQ entry ` - for details. - -.. _lowlevel-cmake: - -Low-level interface -------------------- - -Instead of :cmake:command:`nanobind_add_module` nanobind also exposes a more -fine-grained interface to the underlying operations. -The following - -.. code-block:: cmake - - nanobind_add_module(my_ext NB_SHARED LTO my_ext.cpp) - -is equivalent to - -.. code-block:: cmake - - # Build the core parts of nanobind once - nanobind_build_library(nanobind SHARED) - - # Compile an extension library - add_library(my_ext MODULE my_ext.cpp) - - # .. and link it against the nanobind parts - target_link_libraries(my_ext PRIVATE nanobind) - - # .. enable size optimizations - nanobind_opt_size(my_ext) - - # .. enable link time optimization - nanobind_lto(my_ext) - - # .. set the default symbol visibility to 'hidden' - nanobind_set_visibility(my_ext) - - # .. strip unneeded symbols and debug info from the binary (only active in release builds) - nanobind_strip(my_ext) - - # .. disable the stack protector - nanobind_disable_stack_protector(my_ext) - - # .. set the Python extension suffix - nanobind_extension(my_ext) - - # .. set important compilation flags - nanobind_compile_options(my_ext) - - # .. set important linker flags - nanobind_link_options(my_ext) - - # Statically link against libstdc++/libgcc when targeting musllinux - nanobind_musl_static_libcpp(my_ext) - -The various commands are described below: - -.. cmake:command:: nanobind_build_library - - Compile the core nanobind library. The function expects only the target - name and uses a slightly unusual parameter passing policy: its behavior - changes based on whether or not one the following substrings is detected - in the target name: - - .. list-table:: - :widths: 10 50 - - * - ``-static`` - - Perform a static library build (without this suffix, a shared build is used) - * - ``-abi3`` - - Perform a stable ABI build targeting Python v3.12+. - * - ``-ft`` - - Perform a build that opts into the Python 3.13+ free-threaded behavior. - - .. code-block:: cmake - - # Normal shared library build - nanobind_build_library(nanobind) - - # Static ABI3 build - nanobind_build_library(nanobind-static-abi3) - -.. cmake:command:: nanobind_opt_size - - This function enable size optimizations in ``Release``, ``MinSizeRel``, - ``RelWithDebInfo`` builds. It expects a single target as argument, as in - - .. code-block:: cmake - - nanobind_opt_size(my_target) - -.. cmake:command:: nanobind_set_visibility - - - This function sets the default symbol visibility to ``hidden`` so that only - functions and types specifically marked for export generate symbols in the - resulting binary. It expects a single target as argument, as in - - .. code-block:: cmake - - nanobind_trim(my_target) - - This substantially reduces the size of the generated binary. - -.. cmake:command:: nanobind_strip - - This function strips unused and debug symbols in ``Release`` and - ``MinSizeRel`` builds on Linux and macOS. It expects a single target as - argument, as in - - .. code-block:: cmake - - nanobind_strip(my_target) - -.. cmake:command:: nanobind_disable_stack_protector - - The stack protector affects the binary size of bindings negatively (+8% - on Linux in benchmarks). Protecting from stack smashing in a Python VM - seems in any case futile, so this function disables it for the specified - target when performing a build with optimizations. Use it as follows: - - .. code-block:: cmake - - nanobind_disable_stack_protector(my_target) - -.. cmake:command:: nanobind_extension - - This function assigns an extension name to the compiled binding, e.g., - ``.cpython-311-darwin.so``. Use it as follows: - - .. code-block:: cmake - - nanobind_extension(my_target) - -.. cmake:command:: nanobind_extension_abi3 - - This function assigns a stable ABI extension name to the compiled binding, - e.g., ``.abi3.so``. Use it as follows: - - .. code-block:: cmake - - nanobind_extension_abi3(my_target) - - -.. cmake:command:: nanobind_compile_options - - This function sets recommended compilation flags. Currently, it specifies - ``/bigobj`` and ``/MP`` on MSVC builds, and it does nothing other platforms - or compilers. Use it as follows: - - .. code-block:: cmake - - nanobind_compile_options(my_target) - -.. cmake:command:: nanobind_link_options - - This function sets recommended linker flags. Currently, it controls link - time handling of undefined symbols on Apple platforms related to Python C - API calls, and it does nothing other platforms. Use it as follows: - - .. code-block:: cmake - - nanobind_link_options(my_target) - -.. cmake:command:: nanobind_musl_static_libcpp - - This function passes the linker flags ``-static-libstdc++`` and - ``-static-libgcc`` to ``gcc`` when the environment variable - ``AUDITWHEEL_PLAT`` contains the string ``musllinux``, which indicates a - cibuildwheel build targeting that platform. - - The function expects a single target as argument, as in - - .. code-block:: cmake - - nanobind_musl_static_libcpp(my_target) - -.. _submodule_deps: - -Submodule dependencies ----------------------- - -nanobind includes a dependency (a fast hash map named ``tsl::robin_map``) as a -Git submodule. If you prefer to use another (e.g., system-provided) version of -this dependency, set the ``NB_USE_SUBMODULE_DEPS`` variable before importing -nanobind into CMake. In this case, nanobind's CMake scripts will internally -invoke ``find_dependency(tsl-robin-map)`` to locate the associated header -files. - -.. _stub_generation_cmake: - -Stub generation ---------------- - -Nanobind's CMake tooling includes a convenience command to interface with the -``stubgen`` program explained in the section on :ref:`stub generation `. - -.. cmake:command:: nanobind_add_stub - - Import the specified module (``MODULE`` parameter), generate a stub, and - write it to the specified file (``OUTPUT`` parameter). Here is an example - use: - - .. code-block:: cmake - - nanobind_add_stub( - my_ext_stub - MODULE my_ext - OUTPUT my_ext.pyi - PYTHON_PATH $ - DEPENDS my_ext - ) - - The target name (``my_ext_stub`` in this example) must be unique but has no - other significance. - - ``stubgen`` will add all paths specified as part of the ``PYTHON_PATH`` - block and then execute ``import my_ext`` in a Python session. If the - extension is not importable, this will cause stub generation to fail. - - This command supports the following parameters: - - .. list-table:: - - * - ``INSTALL_TIME`` - - By default, stub generation takes place at build time following - generation of all dependencies (see ``DEPENDS``). When this parameter - is specified, stub generation is instead postponed to the - installation phase. - * - ``MODULE`` - - Specifies the name of the module that should be imported. Mandatory. - * - ``OUTPUT`` - - Specifies the name of the stub file that should be written. The path - is relative to ``CMAKE_CURRENT_BINARY_DIR`` for build-time stub - generation and relative to ``CMAKE_INSTALL_PREFIX`` for install-time - stub generation. Mandatory. - * - ``PYTHON_PATH`` - - List of search paths that should be considered when importing the - module. The paths are relative to ``CMAKE_CURRENT_BINARY_DIR`` for - build-time stub generation and relative to ``CMAKE_INSTALL_PREFIX`` - for install-time stub generation. The current directory (``"."``) is - always included and does not need to be specified. The parameter may - contain CMake `generator expressions - `__ - when :cmake:command:`nanobind_add_stub` is used for build-time stub - generation. Otherwise, generator expressions should not be used. - Optional. - * - ``DEPENDS`` - - Any targets listed here will be marked as a dependencies. This should - generally be used to list the target names of one or more prior - :cmake:command:`nanobind_add_module` declarations. Note that this - parameter tracks *build-time* dependencies and does not need to be - specified when stub generation occurs at install time (see - ``INSTALL_TIME``). Optional. - * - ``VERBOSE`` - - Show status messages generated by ``stubgen``. - * - ``EXCLUDE_DOCSTRINGS`` - - Generate a stub containing only typed signatures without docstrings. - * - ``INCLUDE_PRIVATE`` - - Also include private members, whose names begin or end with a single - underscore. - * - ``MARKER_FILE`` - - Typed extensions normally identify themselves via the presence of an - empty file named ``py.typed`` in each module directory. When this - parameter is specified, :cmake:command:`nanobind_add_stub` will - automatically generate such an empty file as well. - * - ``PATTERN_FILE`` - - Specify a pattern file used to replace declarations in the stub. The - syntax is described in the section on :ref:`stub generation `. - * - ``COMPONENT`` - - Specify a component when ``INSTALL_TIME`` stub generation is used. - This is analogous to ``install(..., COMPONENT [name])`` in other - install targets. - * - ``EXCLUDE_FROM_ALL`` - - If specified, the file is only installed as part of a - component-specific installation when ``INSTALL_TIME`` stub generation - is used. This is analogous to ``install(..., EXCLUDE_FROM_ALL)`` in - other install targets. diff --git a/src/nanobind/docs/api_core.rst b/src/nanobind/docs/api_core.rst deleted file mode 100644 index 216d991..0000000 --- a/src/nanobind/docs/api_core.rst +++ /dev/null @@ -1,3253 +0,0 @@ -.. _api: - -.. cpp:namespace:: nanobind - -C++ API Reference (Core) -======================== - -Macros ------- - -.. c:macro:: NB_MODULE(name, variable) - - This macro creates the entry point that will be invoked when the Python - interpreter imports an extension module. The module name is given as the - first argument and it should not be in quotes. It **must** match the module - name given to the :cmake:command:`nanobind_add_module()` function in the - CMake build system. - - The second macro argument defines a variable of type :cpp:class:`module_`. - The body of the declaration typically contains a sequence of operations - that populate the module variable with contents. - - .. code-block:: cpp - - NB_MODULE(example, m) { - m.doc() = "Example module"; - - // Add bindings here - m.def("add", []() { - return "Hello, World!"; - }); - } - -.. c:macro:: NB_MAKE_OPAQUE(T) - - The macro registers a partial template specialization pattern for the type - `T` that marks it as *opaque*, meaning that nanobind won't try to run its - type casting template machinery on it. - - This is useful when trying to register a binding for `T` that is simultaneously - also covered by an existing type caster. - - This macro should be used at the top level (outside of namespaces and - program code). - -Python object API ------------------ - -Nanobind ships with a wide range of Python wrapper classes like -:cpp:class:`object`, :cpp:class:`list`, etc. Besides class-specific operations -(e.g., :cpp:func:`list::append`), these classes also implement core operations -that can be performed on *any* Python object. Since it would be tedious to -implement this functionality over and over again, it is realized by the -following mixin class that lives in the ``nanobind::detail`` namespace. - -.. cpp:namespace:: nanobind::detail - -.. cpp:class:: template api - - This mixin class adds common functionality to various nanobind types using - the `curiously recurring template pattern - `_ - (CRTP). The only requirement for the `Derived` template parameter is that it - implements the member function ``PyObject *ptr() const`` that gives access - to the underlying Python object pointer. - - .. cpp:function:: Derived &derived() - - Obtain a mutable reference to the derived class. - - .. cpp:function:: const Derived &derived() const - - Obtain a const reference to the derived class. - - .. cpp:function:: handle inc_ref() const - - Increases the reference count and returns a reference to the Python object. - - .. cpp:function:: handle dec_ref() const - - Decreases the reference count and returns a reference to the Python object. - - .. cpp:function:: iterator begin() const - - Return a forward iterator analogous to ``iter()`` in Python. The object - must be a collection that supports the iteration protocol. This interface - provides a generic iterator that works any type of Python object. The - :cpp:class:`tuple`, :cpp:class:`list`, and :cpp:class:`dict` wrappers - provide more efficient specialized alternatives. - - .. cpp:function:: iterator end() const - - Return a sentinel that ends the iteration. - - .. cpp:function:: handle type() const - - Return a :cpp:class:`handle` to the underlying Python type object. - - .. cpp:function:: operator handle() const - - Return a :cpp:class:`handle` wrapping the underlying ``PyObject*`` pointer. - - .. cpp:function:: detail::accessor attr(handle key) const - - Analogous to ``self.key`` in Python, where ``key`` is a Python object. - The result is wrapped in an :cpp:class:`accessor ` so - that it can be read and written. - - .. cpp:function:: detail::accessor attr(const char * key) const - - Analogous to ``self.key`` in Python, where ``key`` is a C-style string. - The result is wrapped in an :cpp:class:`accessor ` so - that it can be read and written. - - .. cpp:function:: detail::accessor doc() const - - Analogous to ``self.__doc__``. The result is wrapped in an - :cpp:class:`accessor ` so that it can be read and - written. - - .. cpp:function:: detail::accessor operator[](handle key) const - - Analogous to ``self[key]`` in Python, where ``key`` is a Python object. - The result is wrapped in an :cpp:class:`accessor ` so that it can be read and - written. - - .. cpp:function:: detail::accessor operator[](const char * key) const - - Analogous to ``self[key]`` in Python, where ``key`` is a C-style string. - The result is wrapped in an :cpp:class:`accessor ` so that it can be read and - written. - - .. cpp:function:: template > = 1> detail::accessor operator[](T key) const - - Analogous to ``self[key]`` in Python, where ``key`` is an arithmetic - type (e.g., an integer). The result is wrapped in an :cpp:class:`accessor ` so - that it can be read and written. - - .. cpp:function:: template object operator()(Args &&...args) const - - Assuming the Python object is a function or implements the ``__call__`` - protocol, `operator()` invokes the underlying function, passing an - arbitrary set of parameters, while expanding any detected variable length - argument and keyword argument packs. The result is returned as an - :cpp:class:`object` and may need to be converted back into a Python - object using :cpp:func:`cast()`. - - Type conversion is performed using the return value policy `policy` - - When type conversion of arguments or return value fails, the function - raises a :cpp:type:`cast_error`. When the Python function call fails, it - instead raises a :cpp:class:`python_error`. - - .. cpp:function:: args_proxy operator*() const - - Given a a tuple or list, this helper function performs variable argument - list unpacking in function calls resembling the ``*`` operator in Python. - Applying `operator*()` twice yields ``**`` keyword argument - unpacking for dictionaries. - - .. cpp:function:: bool is(handle value) const - - Analogous to ``self is value`` in Python. - - .. cpp:function:: bool is_none() const - - Analogous to ``self is None`` in Python. - - .. cpp:function:: bool is_type() const - - Analogous to ``isinstance(self, type)`` in Python. - - .. cpp:function:: bool is_valid() const - - Checks if this wrapper contains a valid Python object (in the sense that - the ``PyObject *`` pointer is non-null). - - .. cpp:function:: template bool equal(const api &other) - - Equivalent to ``self == other`` in Python. - - .. cpp:function:: template bool not_equal(const api &other) - - Equivalent to ``self != other`` in Python. - - .. cpp:function:: template bool operator<(const api &other) - - Equivalent to ``self < other`` in Python. - - .. cpp:function:: template bool operator<=(const api &other) - - Equivalent to ``self <= other`` in Python. - - .. cpp:function:: template bool operator>(const api &other) - - Equivalent to ``self > other`` in Python. - - .. cpp:function:: template bool operator>=(const api &other) - - Equivalent to ``self >= other`` in Python. - - .. cpp:function:: object operator-() - - Equivalent to ``-self`` in Python. - - .. cpp:function:: object operator~() - - Equivalent to ``~self`` in Python. - - .. cpp:function:: template object operator+(const api &other) - - Equivalent to ``self + other`` in Python. - - .. cpp:function:: template object operator-(const api &other) - - Equivalent to ``self - other`` in Python. - - .. cpp:function:: template object operator*(const api &other) - - Equivalent to ``self * other`` in Python. - - .. cpp:function:: template object operator/(const api &other) - - Equivalent to ``self / other`` in Python. - - .. cpp:function:: template object floor_div(const api &other) - - Equivalent to ``self // other`` in Python. - - .. cpp:function:: template object operator|(const api &other) - - Equivalent to ``self | other`` in Python. - - .. cpp:function:: template object operator&(const api &other) - - Equivalent to ``self & other`` in Python. - - .. cpp:function:: template object operator^(const api &other) - - Equivalent to ``self ^ other`` in Python. - - .. cpp:function:: template object operator<<(const api &other) - - Equivalent to ``self << other`` in Python. - - .. cpp:function:: template object operator>>(const api &other) - - Equivalent to ``self >> other`` in Python. - - .. cpp:function:: template object operator+=(const api &other) - - Equivalent to ``self += other`` in Python. Note that the `api` version - of the in-place operator does not update the ``self`` reference, which - may lead to unexpected results when working with immutable types that - return their result instead of updating ``self``. - - The :cpp:class:`object` class and subclasses override the in-place - operators to achieve more intuitive behavior. - - .. cpp:function:: template object operator-=(const api &other) - - Equivalent to ``self -= other`` in Python. See :cpp:func:`operator+=` for limitations. - - .. cpp:function:: template object operator*=(const api &other) - - Equivalent to ``self *= other`` in Python. See :cpp:func:`operator+=` for limitations. - - .. cpp:function:: template object operator/=(const api &other) - - Equivalent to ``self /= other`` in Python. See :cpp:func:`operator+=` for limitations. - - .. cpp:function:: template object operator|=(const api &other) - - Equivalent to ``self |= other`` in Python. See :cpp:func:`operator+=` for limitations. - - .. cpp:function:: template object operator&=(const api &other) - - Equivalent to ``self &= other`` in Python. See :cpp:func:`operator+=` for limitations. - - .. cpp:function:: template object operator^=(const api &other) - - Equivalent to ``self ^= other`` in Python. See :cpp:func:`operator+=` for limitations. - - .. cpp:function:: template object operator<<=(const api &other) - - Equivalent to ``self <<= other`` in Python. See :cpp:func:`operator+=` for limitations. - - .. cpp:function:: template object operator>>=(const api &other) - - Equivalent to ``self >>= other`` in Python. See :cpp:func:`operator+=` for limitations. - -.. cpp:class:: template accessor - - This helper class facilitates attribute and item access. Casting an - :cpp:class:`accessor` to a :cpp:class:`handle` or :cpp:class:`object` - subclass causes a corresponding call to ``__getitem__`` or ``__getattr__`` - depending on the template argument `Impl`. Assigning a - :cpp:class:`handle` or :cpp:class:`object` subclass causes a call to - ``__setitem__`` or ``__setattr__``. - -.. cpp:namespace:: nanobind - -Handles and objects -------------------- - -nanobind provides two styles of Python object wrappers: classes without -reference counting deriving from :cpp:class:`handle`, and reference-counted -wrappers deriving from :cpp:class:`object`. Reference counting bugs can be -really tricky to track down, hence it is recommended that you always prefer -:cpp:class:`object`-style wrappers unless there are specific reasons that -warrant the use of raw handles. - -Without reference counting -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. cpp:class:: handle: public detail::api - - This class provides a thin wrapper around a raw ``PyObject *`` pointer. Its - main purpose is to intercept various C++ operations and convert them into - Python C API calls. It does *not* do any reference counting and can be - somewhat unsafe to use. - - .. cpp:function:: handle() = default - - Default constructor. Creates an invalid handle wrapping a null pointer. - (:cpp:func:`detail::api::is_valid()` is ``false``) - - .. cpp:function:: handle(const handle &) = default - - Default copy constructor. - - .. cpp:function:: handle(handle &&) = default - - Default move constructor. - - .. cpp:function:: handle(const PyObject * o) - - Initialize a handle from a Python object pointer. Does not change the reference count of `o`. - - .. cpp:function:: handle(const PyTypeObject * o) - - Initialize a handle from a Python type object pointer. Does not change the reference count of `o`. - - .. cpp:function:: handle &operator=(const handle &) = default - - Default copy assignment operator. - - .. cpp:function:: handle &operator=(handle &&) = default - - Default move assignment operator. - - .. cpp:function:: explicit operator bool() const - - Check if the handle refers to a valid Python object. Equivalent to - :cpp:func:`detail::api::is_valid()` - - .. cpp:function:: handle inc_ref() const noexcept - - Increases the reference count and returns a reference to the Python object. - Never raises an exception. - - .. cpp:function:: handle dec_ref() const noexcept - - Decreases the reference count and returns a reference to the Python object. - Never raises an exception. - - .. cpp:function:: PyObject * ptr() const - - Return the underlying ``PyObject*`` pointer. - -With reference counting -^^^^^^^^^^^^^^^^^^^^^^^ - -.. cpp:class:: object: public handle - - This class provides a convenient `RAII - `_ - wrapper around a ``PyObject*`` pointer. Like :cpp:class:`handle`, it - intercepts various C++ operations and converts them into Python C API calls. - - The main difference to :cpp:class:`handle` is that it uses reference - counting to keep the underlying Python object alive. - - Use the :cpp:func:`borrow()` and :cpp:func:`steal()` functions to create an - :cpp:class:`object` from a :cpp:class:`handle` or ``PyObject*`` pointer. - - .. cpp:function:: object() = default - - Default constructor. Creates an invalid object wrapping a null pointer. - (:cpp:func:`detail::api::is_valid()` is ``false``) - - .. cpp:function:: object(object &&o) - - Move constructor. Steals the object from `o` without - changing its reference count. - - .. cpp:function:: object(const object &o) - - Copy constructor. Acquires a new reference to `o` (if valid). - - .. cpp:function:: ~object() - - Decrease the reference count of the referenced Python object (if valid). - - .. cpp:function:: object& operator=(object &&o) - - Move assignment operator. Decreases the reference count of the currently - held object (if valid) and steals the object from `o` without - changing its reference count. - - .. cpp:function:: object& operator=(const object &o) - - Copy assignment operator. Decreases the reference count of the currently - held object (if valid) and acquires a new reference to the object - `o` (if valid). - - .. cpp:function:: void reset() - - Decreases the reference count of the currently held object (if valid) and - resets the internal pointer to ``nullptr``. - - .. cpp:function:: handle release() - - Resets the internal pointer to ``nullptr`` and returns its previous - contents as a :cpp:class:`handle`. This operation does not change - the object's reference count and should be used carefully. - - .. cpp:function:: template object& operator+=(const api &other) - - Equivalent to ``self += other`` in Python. - - .. cpp:function:: template object& operator-=(const api &other) - - Equivalent to ``self -= other`` in Python. - - .. cpp:function:: template object& operator*=(const api &other) - - Equivalent to ``self *= other`` in Python. - - .. cpp:function:: template object& operator/=(const api &other) - - Equivalent to ``self /= other`` in Python. - - .. cpp:function:: template object& operator|=(const api &other) - - Equivalent to ``self |= other`` in Python. - - .. cpp:function:: template object& operator&=(const api &other) - - Equivalent to ``self &= other`` in Python. - - .. cpp:function:: template object& operator^=(const api &other) - - Equivalent to ``self ^= other`` in Python. - - .. cpp:function:: template object& operator<<=(const api &other) - - Equivalent to ``self <<= other`` in Python. - - .. cpp:function:: template object& operator>>=(const api &other) - - Equivalent to ``self >>= other`` in Python. - - -.. cpp:function:: template T borrow(handle h) - - Create a reference-counted Python object wrapper of type `T` from a raw - handle or ``PyObject *`` pointer. The target type `T` must be - :cpp:class:`object` (the default) or one of its derived classes. The - function does not perform any conversions or checks---it is up to the user - to make sure that the target type is correct. - - The function *borrows* a reference, which means that it will increase the - reference count while constructing ``T``. - - For example, consider the Python C API function `PyList_GetItem() - `_, whose - documentation states that it returns a borrowed reference. An interface - between this API and nanobind could look as follows: - - .. code-block:: cpp - - - PyObject* list = ...; - Py_ssize_t index = ...; - nb::object o = nb::borrow(PyList_GetItem(list, index)); - - Using :cpp:func:`steal()` in this setting is incorrect and would lead to a - reference underflow. - -.. cpp:function:: template T steal(handle h) - - Create a reference-counted Python object wrapper of type `T` from a raw - handle or ``PyObject *`` pointer. The target type `T` must be - :cpp:class:`object` (the default) or one of its derived classes. The - function does not perform any conversions or checks---it is up to the user - to make sure that the target type is correct. - - The function *steals* a reference, which means that constructing ``T`` - leaves the object's reference count unchanged. - - For example, consider the Python C API function `PyObject_Str() - `_, whose - documentation states that it returns a *new reference*. An interface - between this API and nanobind could look as follows: - - .. code-block:: cpp - - PyObject* value = ...; - nb::object o = nb::steal(PyObject_Str(value)); - - Using :cpp:func:`borrow()` in this setting is incorrect and would lead to a - reference leak. - - -Attribute access ----------------- - -.. cpp:function:: bool hasattr(handle h, const char * key) noexcept - - Check if the given object has an attribute string ``key``. The function never - raises an exception and returns ``false`` in case of an internal error. - - Equivalent to ``hasattr(h, key)`` in Python. - -.. cpp:function:: bool hasattr(handle h, handle key) noexcept - - Check if the given object has a attribute represented by the Python object - ``key``. The function never raises an exception and returns ``false`` in - case of an internal error. - - Equivalent to ``hasattr(h, key)`` in Python. - -.. cpp:function:: object getattr(handle h, const char * key) - - Equivalent to ``h.key`` and ``getattr(h, key)`` in Python. - Raises :cpp:class:`python_error` if the operation fails. - -.. cpp:function:: object getattr(handle h, handle key) - - Equivalent to ``h.key`` and ``getattr(h, key)`` in Python. - Raises :cpp:class:`python_error` if the operation fails. - -.. cpp:function:: object getattr(handle h, const char * key, handle def) noexcept - - Equivalent to ``getattr(h, key, def)`` in Python. Never raises an - exception and returns ``def`` when the operation fails, or when the desired - attribute could not be found. - -.. cpp:function:: object getattr(handle h, handle key, handle def) noexcept - - Equivalent to ``getattr(h, key, def)`` in Python. Never raises an - exception and returns ``def`` when the operation fails, or when the desired - attribute could not be found. - -.. cpp:function:: void setattr(handle h, const char * key, handle value) - - Equivalent to ``h.key = value`` and ``setattr(h, key, value)`` in Python. - Raises :cpp:class:`python_error` if the operation fails. - -.. cpp:function:: void setattr(handle h, handle key, handle value) - - Equivalent to ``h.key = value`` and ``setattr(h, key, value)`` in Python. - Raises :cpp:class:`python_error` if the operation fails. - -.. cpp:function:: void delattr(handle h, const char * key) - - Equivalent to ``del h.key`` and ``delattr(h, key)`` in Python. - Raises :cpp:class:`python_error` if the operation fails. - -.. cpp:function:: void delattr(handle h, handle key) - - Equivalent to ``del h.key`` and ``delattr(h, key)`` in Python. - Raises :cpp:class:`python_error` if the operation fails. - -.. cpp:function:: template void del(detail::accessor &) - - Remove an element from a sequence or mapping. The C++ statement - - .. code-block:: cpp - - nb::del(o[key]); - - is equivalent to ``del o[key]`` in Python. - - When the element cannot be removed, the function will raise - :cpp:class:`python_error` wrapping either a Python ``IndexError`` (for - sequence types) or a ``KeyError`` (for mapping types). - -.. cpp:function:: template void del(detail::accessor &&) - - Rvalue equivalent of the above expression. - -Size queries ------------- - -.. cpp:function:: size_t len(handle h) - - Equivalent to ``len(h)`` in Python. Raises :cpp:class:`python_error` if the - operation fails. - -.. cpp:function:: size_t len(const tuple &t) - - Equivalent to ``len(t)`` in Python. Optimized variant for tuples. - -.. cpp:function:: size_t len(const list &l) - - Equivalent to ``len(l)`` in Python. Optimized variant for lists. - -.. cpp:function:: size_t len(const dict &d) - - Equivalent to ``len(d)`` in Python. Optimized variant for dictionaries. - -.. cpp:function:: size_t len(const set &d) - - Equivalent to ``len(d)`` in Python. Optimized variant for sets. - -.. cpp:function:: size_t len_hint(handle h) - - Equivalent to ``operator.length_hint(h)`` in Python. Raises - :cpp:class:`python_error` if the operation fails. - -Type queries ------------- - -.. cpp:function:: template isinstance(handle h) - - Checks if the Python object `h` represents a valid instance of the C++ type - `T`. This works for bound C++ classes, basic types (``int``, ``bool``, - etc.), and Python type wrappers ( :cpp:class:`list`, :cpp:class:`dict`, - :cpp:class:`module_`, etc.). - - *Note*: the check even works when `T` involves a type caster (e.g., an STL - types like ``std::vector``). However, this involve a wasteful attempt - to convert the object to C++. It may be more efficient to just perform the - conversion using :cpp:func:`cast` and catch potential raised exceptions. - -.. cpp:function:: isinstance(handle inst, handle cls) - - Checks if the Python object `inst` is an instance of the Python type `cls`. - -.. cpp:function:: template handle type() noexcept - - Returns the Python type object associated with the C++ type `T`. When the - type not been bound via nanobind, the function returns an invalid handle - (:cpp:func:`detail::api::is_valid()` is ``false``). - - *Note*: in contrast to the :cpp:func:`isinstance()` function above, builtin - types, type wrappers, and types handled using type casters, are *not* - supported. - -Wrapper classes ---------------- - -.. cpp:class:: tuple: public object - - Wrapper class representing Python ``tuple`` instances. - - Use the standard ``operator[]`` C++ operator with an integer argument to - read tuple elements (the bindings for this operator are provided by the - parent class and not listed here). Once created, the set is immutable and - its elements cannot be replaced. - - Use the :py:func:`make_tuple` function to create new tuples. - - .. cpp:function:: tuple() - - Create an empty tuple - - .. cpp:function:: tuple(handle h) - - Attempt to convert a given Python object into a tuple. Analogous to the - expression ``tuple(h)`` in Python. - - .. cpp:function:: size_t size() const - - Return the number of tuple elements. - - .. cpp:function:: bool empty() const - - Check whether the tuple is empty. - - .. cpp:function:: detail::fast_iterator begin() const - - Return a forward iterator analogous to ``iter()`` in Python. The function - overrides a generic version in :cpp:class:`detail::api` and is more - efficient for tuples. - - .. cpp:function:: detail::fast_iterator end() const - - Return a sentinel that ends the iteration. - - .. cpp:function:: template > = 1> detail::accessor operator[](T key) const - - Analogous to ``self[key]`` in Python, where ``key`` is an arithmetic - type (e.g., an integer). The result is wrapped in an :cpp:class:`accessor ` so - that it can be read and converted. Write access is not possible. - - The function overrides the generic version in :cpp:class:`detail::api` - and is more efficient for tuples. - - -.. cpp:class:: list : public object - - Wrapper class representing Python ``list`` instances. - - Use the standard ``operator[]`` C++ operator with an integer argument to - read and write list elements (the bindings for this operator are provided by - the parent class and not listed here). - - Use the :cpp:func:`nb::del ` function to remove elements. - - .. cpp:function:: list() - - Create an empty list - - .. cpp:function:: list(handle h) - - Attempt to convert a given Python object into a list. Analogous to the - expression ``list(h)`` in Python. - - .. cpp:function:: size_t size() const - - Return the number of list elements. - - .. cpp:function:: bool empty() const - - Check whether the list is empty. - - .. cpp:function:: template void append(T&& value) - - Append an element to the list. When `T` does not already represent a - wrapped Python object, the function performs a cast. - - .. cpp:function:: template void insert(Py_ssize_t index, T&& value) - - Insert an element to the list (at index ``index``, which may also be - negative). When `T` does not already represent a wrapped Python object, - the function performs a cast. - - .. cpp:function:: void clear() - - Clear the list entries. - - .. cpp:function:: void extend(handle h) - - Analogous to the ``.extend(h)`` method of ``list`` in Python. - - .. cpp:function:: void sort() - - Analogous to the ``.sort()`` method of ``list`` in Python. - - .. cpp:function:: void reverse() - - Analogous to the ``.reverse()`` method of ``list`` in Python. - - .. cpp:function:: template > = 1> detail::accessor operator[](T key) const - - Analogous to ``self[key]`` in Python, where ``key`` is an arithmetic - type (e.g., an integer). The result is wrapped in an :cpp:class:`accessor ` so - that it can be read and written. - - The function overrides the generic version in :cpp:class:`detail::api` - and is more efficient for lists. - - .. cpp:function:: detail::fast_iterator begin() const - - Return a forward iterator analogous to ``iter()`` in Python. The operator - provided here overrides the generic version in :cpp:class:`detail::api` - and is more efficient for lists. - - .. cpp:function:: detail::fast_iterator end() const - - Return a sentinel that ends the iteration. - - -.. cpp:class:: dict: public object - - Wrapper class representing Python ``dict`` instances. - - Use the standard ``operator[]`` C++ operator to read and write dictionary - elements (the bindings for this operator are provided by the parent class - and not listed here). - - Use the :cpp:func:`nb::del ` function to remove elements. - - .. cpp:function:: dict() - - Create an empty dictionary - - .. cpp:function:: size_t size() const - - Return the number of dictionary elements. - - .. cpp:function:: bool empty() const - - Check whether the dictionary is empty. - - .. cpp:function:: template bool contains(T&& key) const - - Check whether the dictionary contains a particular key. When `T` does not - already represent a wrapped Python object, the function performs a cast. - - .. cpp:function:: detail::dict_iterator begin() const - - Return an item iterator that returns ``std::pair`` - key-value pairs analogous to ``iter(dict.items())`` in Python. - - In free-threaded Python, the :cpp:class:``detail::dict_iterator`` class - acquires a lock to the underlying dictionary to enable the use of the - efficient but thread-unsafe ``PyDict_Next()`` Python C traversal routine. - - .. cpp:function:: detail::dict_iterator end() const - - Return a sentinel that ends the iteration. - - .. cpp:function:: list keys() const - - Return a list containing all dictionary keys. - - .. cpp:function:: list values() const - - Return a list containing all dictionary values. - - .. cpp:function:: list items() const - - Return a list containing all dictionary items as ``(key, value)`` pairs. - - .. cpp:function:: void clear() - - Clear the contents of the dictionary. - - .. cpp:function:: void update(handle h) - - Analogous to the ``.update(h)`` method of ``dict`` in Python. - - .. cpp:function:: object get(handle key, handle def) - - Analogous to ``.get(key, def)`` method of ``dict`` in Python with a - fallback value ``def``. This is more efficient than checking the presence - of ``.contains(key)`` first, or using ``operator[]`` and catching a - potential exceptions. - - .. cpp:function:: object get(const char * key, handle def) - - Overload of the above method that takes a C-string as key. - -.. cpp:class:: set: public object - - Wrapper class representing Python ``set`` instances. - - .. cpp:function:: set() - - Create an empty set - - .. cpp:function:: set(handle h) - - Attempt to convert a given Python object into a set. Analogous to the - expression ``set(h)`` in Python. - - .. cpp:function:: size_t size() const - - Return the number of set elements. - - .. cpp:function:: bool empty() const - - Check whether the set is empty. - - .. cpp:function:: template void add(T&& key) - - Add a key to the set. When `T` does not already represent a wrapped - Python object, the function performs a cast. - - .. cpp:function:: template bool contains(T&& key) const - - Check whether the set contains a particular key. When `T` does not - already represent a wrapped Python object, the function performs a cast. - - .. cpp:function:: void clear() - - Clear the contents of the set. - - .. cpp:function:: template bool discard(T&& key) - - Analogous to the ``.discard(h)`` method of the ``set`` type in Python. - Returns ``true`` if the item was deleted successfully, and ``false`` if - the value was not present. When `T` does not already represent a wrapped - Python object, the function performs a cast. - -.. cpp:class:: frozenset: public object - - Wrapper class representing Python ``frozenset`` instances. - - .. cpp:function:: frozenset() - - Create an empty frozenset. - - .. cpp:function:: frozenset(handle h) - - Attempt to convert a given Python object into a ``frozenset``. Analogous - to the expression ``frozenset(h)`` in Python. - - .. cpp:function:: size_t size() const - - Return the number of set elements. - - .. cpp:function:: bool empty() const - - Check whether the set is empty. - - .. cpp:function:: template bool contains(T&& key) const - - Check whether the set contains a particular key. When `T` does not - already represent a wrapped Python object, the function performs a cast. - -.. cpp:class:: module_: public object - - Wrapper class representing Python ``module`` instances. The underscore at - the end disambiguates the class name from the C++20 ``module`` declaration. - - .. cpp:function:: template module_ &def(const char * name, Func &&f, const Extra &...extra) - - Bind the function `f` to the identifier `name` within the module. Returns - a reference to ``*this`` so that longer sequences of binding declarations - can be chained, as in ``m.def(...).def(...);``. The variable length - `extra` parameter can be used to pass docstrings and other :ref:`function - binding annotations `. - - Example syntax: - - .. code-block:: cpp - - void test() { printf("Hello world!"); } - - NB_MODULE(example, m) { - // here, "m" is variable of type 'module_'. - m.def("test", &test, "A test function") - .def(...); // more binding declarations - } - - - .. cpp:function:: module_ import_(const char * name) - - Import the Python module with the specified name and return a reference - to it. The underscore at the end disambiguates the function name from the - C++20 ``import`` statement. - - Example usage: - - .. code-block:: cpp - - nb::module_ np = nb::module_::import_("numpy"); - nb::object np_array = np.attr("array"); - - .. cpp:function:: module_ import_(handle name) - - Import the Python module with the specified name and return a reference - to it. In contrast to the version above, this function expects a Python - object as key. - - .. cpp:function:: module_ def_submodule(const char * name, const char * doc = nullptr) - - Create a Python submodule within an existing module and return a - reference to it. Can be chained recursively. - - Example usage: - - .. code-block:: cpp - - NB_MODULE(example, m) { - nb::module_ m2 = m.def_submodule("sub", "A submodule of 'example'"); - nb::module_ m3 = m2.def_submodule("subsub", "A submodule of 'example.sub'"); - } - -.. cpp:class:: capsule: public object - - Capsules are small opaque Python objects that wrap a C or C++ pointer and a cleanup routine. - - .. cpp:function:: capsule(const void * ptr, void (* cleanup)(void*) noexcept = nullptr) - - Construct an *unnamed* capsule wrapping the pointer `p`. When the - capsule is garbage collected, Python will call the destructor `cleanup` - (if provided) with the value of `p`. - - .. cpp:function:: capsule(const void * ptr, const char * name, void (* cleanup)(void*) noexcept = nullptr) - - Construct a *named* capsule with name `name` wrapping the pointer `p`. - When the capsule is garbage collected, Python will call the destructor - `cleanup` (if provided) with the value of `p`. - - .. cpp:function:: const char * name() const - - Return the capsule name (or ``nullptr`` when the capsule is unnamed) - - .. cpp:function:: void * data() const - - Return the pointer wrapped by the capsule. - - .. cpp:function:: void * data(const char *name) const - - Return the pointer wrapped by the capsule. Check that the - capsule name matches the specified value, or raise an exception. - - -.. cpp:class:: bool_: public object - - This wrapper class represents Python ``bool`` instances. - - .. cpp:function:: bool_(handle h) - - Performs a boolean cast within Python. This is equivalent to the Python - expression ``bool(h)``. - - .. cpp:function:: explicit bool_(bool value) - - Convert an C++ boolean instance into a Python ``bool``. - - .. cpp:function:: explicit operator bool() const - - Extract the boolean value underlying this object. - - -.. cpp:class:: int_: public object - - This wrapper class represents Python ``int`` instances. It can handle large - numbers requiring more than 64 bits of storage. - - .. cpp:function:: int_(handle h) - - Performs an integer cast within Python. This is equivalent to the Python - expression ``int(h)``. - - .. cpp:function:: template > = 0> explicit int_(T value) - - Convert an C++ arithmetic type into a Python integer. - - .. cpp:function:: template > = 0> explicit operator T() const - - Convert a Python integer into a C++ arithmetic type. - - -.. cpp:class:: float_: public object - - This wrapper class represents Python ``float`` instances. - - .. cpp:function:: float_(handle h) - - Performs an floating point cast within Python. This is equivalent to the - Python expression ``float(h)``. - - .. cpp:function:: explicit float_(double value) - - Convert an C++ double value into a Python float objecct - - .. cpp:function:: explicit operator double() const - - Convert a Python float object into a C++ double value - - -.. cpp:class:: str: public object - - This wrapper class represents Python unicode ``str`` instances. - - .. cpp:function:: str(handle h) - - Performs a string cast within Python. This is equivalent equivalent to - the Python expression ``str(h)``. - - .. cpp:function:: str(const char * s) - - Convert a null-terminated C-style string in UTF-8 encoding into a Python string. - - .. cpp:function:: str(const char * s, size_t n) - - Convert a C-style string in UTF-8 encoding of length ``n`` bytes into a Python string. - - There is a user-defined string literal ``_s`` accessible in the - ``nanobind::literals`` namespace:: - - using namespace nanobind::literals; - auto s = "Hello world!"_s; // equivalent to str("Hello world!") - - .. cpp:function:: const char * c_str() const - - Convert a Python string into a null-terminated C-style string with UTF-8 - encoding. - - *Note*: The C string will be deleted when the `str` instance is garbage - collected. - - .. cpp:function:: template str format(Args&&... args) - - C++ analog of the Python routine ``str.format``. Can be called with - positional and keyword arguments. - - -.. cpp:class:: bytes: public object - - This wrapper class represents Python unicode ``bytes`` instances. - - .. cpp:function:: bytes(handle h) - - Performs a cast within Python. This is equivalent to - the Python expression ``bytes(h)``. - - .. cpp:function:: bytes(const char * s) - - Convert a null-terminated C-style string encoding into a Python ``bytes`` object. - - .. cpp:function:: bytes(const void * buf, size_t n) - - Convert a byte buffer ``buf`` of length ``n`` bytes into a Python ``bytes`` object. The buffer can contain embedded null bytes. - - .. cpp:function:: const char * c_str() const - - Convert a Python bytes object into a null-terminated C-style string. - - .. cpp:function:: size_t size() const - - Return the size in bytes. - - .. cpp:function:: const void * data() const - - Convert a Python ``bytes`` object into a byte buffer of length :cpp:func:`bytes::size()` bytes. - - -.. cpp:class:: bytearray: public object - - This wrapper class represents Python ``bytearray`` instances. - - .. cpp:function:: bytearray() - - Create an empty ``bytearray``. - - .. cpp:function:: bytearray(handle h) - - Performs a cast within Python. This is equivalent to - the Python expression ``bytearray(h)``. - - .. cpp:function:: bytearray(const void * buf, size_t n) - - Convert a byte buffer ``buf`` of length ``n`` bytes into a Python ``bytearray`` object. The buffer can contain embedded null bytes. - - .. cpp:function:: const char * c_str() const - - Convert a Python ``bytearray`` object into a null-terminated C-style string. - - .. cpp:function:: size_t size() const - - Return the size in bytes. - - .. cpp:function:: void * data() - - Convert a Python ``bytearray`` object into a byte buffer of length :cpp:func:`bytearray::size()` bytes. - - .. cpp:function:: const void * data() const - - Convert a Python ``bytearray`` object into a byte buffer of length :cpp:func:`bytearray::size()` bytes. - - .. cpp:function:: void resize(size_t n) - - Resize the internal buffer of a Python ``bytearray`` object to ``n``. Any - space added by this method, which calls `PyByteArray_Resize`, will not be - initialized and may contain random data. - - -.. cpp:class:: type_object: public object - - Wrapper class representing Python ``type`` instances. - -.. cpp:class:: sequence: public object - - Wrapper class representing arbitrary Python sequence types. - -.. cpp:class:: mapping : public object - - Wrapper class representing arbitrary Python mapping types. - - .. cpp:function:: template bool contains(T&& key) const - - Check whether the map contains a particular key. When `T` does not - already represent a wrapped Python object, the function performs a cast. - - .. cpp:function:: list keys() const - - Return a list containing all of the map's keys. - - .. cpp:function:: list values() const - - Return a list containing all of the map's values. - - .. cpp:function:: list items() const - - Return a list containing all of the map's items as ``(key, value)`` pairs. - -.. cpp:class:: iterator : public object - - Wrapper class representing a Python iterator. - - .. cpp:function:: iterator& operator++() - - Advance to the next element (pre-increment form). - - .. cpp:function:: iterator& operator++(int) - - Advance to the next element (post-increment form). - - .. cpp:function:: handle operator*() const - - Return the item at the current position. - - .. cpp:function:: handle operator->() const - - Convenience routine for pointer-style access. - - .. static iterator sentinel(); - - Return a sentinel that ends the iteration. - - .. cpp:function:: friend bool operator==(const iterator &a, const iterator &b); - - Iterator equality comparison operator. - - .. cpp:function:: friend bool operator!=(const iterator &a, const iterator &b); - - Iterator inequality comparison operator. - -.. cpp:class:: iterable : public object - - Wrapper class representing an object that can be iterated upon (in the sense - that calling :cpp:func:`iter()` is valid). - -.. cpp:class:: slice : public object - - Wrapper class representing a Python slice object. - - .. cpp:function:: slice(handle start, handle stop, handle step) - - Create the slice object given by ``slice(start, stop, step)`` in Python. - - .. cpp:function:: template > = 0> slice(T stop) - - Create the slice object ``slice(stop)``, where `stop` is represented by a - C++ integer type. - - .. cpp:function:: template > = 0> slice(T start, T stop) - - Create the slice object ``slice(start, stop)``, where `start` and `stop` - are represented by a C++ integer type. - - .. cpp:function:: template > = 0> slice(T start, T stop, T step) - - Create the slice object ``slice(start, stop, step)``, where `start`, - `stop`, and `step` are represented by a C++ integer type. - - .. cpp:function:: detail::tuple compute(size_t size) const - - Adjust the slice to the `size` value of a given container. Returns a tuple containing - ``(start, stop, step, slice_length)``. - -.. cpp:class:: ellipsis: public object - - Wrapper class representing a Python ellipsis (``...``) object. - - .. cpp:function:: ellipsis() - - Create a wrapper referencing the unique Python ``Ellipsis`` object. - -.. cpp:class:: not_implemented: public object - - Wrapper class representing a Python ``NotImplemented`` object. - - .. cpp:function:: not_implemented() - - Create a wrapper referencing the unique Python ``NotImplemented`` object. - -.. cpp:class:: callable: public object - - Wrapper class representing a callable Python object. - -.. cpp:class:: weakref: public object - - Wrapper class representing a Python weak reference object. - - .. cpp:function:: explicit weakref(handle obj, handle callback = { }) - - Construct a new weak reference that points to `obj`. If provided, - Python will invoke the callable `callback` when `obj` expires. - -.. cpp:class:: args : public tuple - - Variable argument keyword list for use in function argument declarations. - -.. cpp:class:: kwargs : public dict - - Variable keyword argument keyword list for use in function argument declarations. - -.. cpp:class:: any : public object - - This wrapper class represents Python ``typing.Any``-typed values. On the C++ - end, this type is interchangeable with :py:class:`object`. The only - difference is the type signature when used in function arguments and return - values. - -.. cpp:class:: fallback : public object - - Instances of this wrapper class behave just like :cpp:class:`handle`. When - used to pass arguments in function bindings, the associated arguments - *require* implicit conversion (which, however, adds no runtime cost). - - The type is convenient in overload chains where a generic fallback should - accept any Python object, e.g., - - .. code-block:: cpp - - m.def("func", [](MyClass &arg)) { ... }); - m.def("func", [](nb::fallback arg)) { ... }); - - If :cpp:class:`handle` or :cpp:class:`object` were used instead on the - second line, they would always match and prevent potential implicit - conversion of arguments to `MyClass`. - -Parameterized wrapper classes ------------------------------ - -.. cpp:class:: template handle_t : public handle - - Wrapper class representing a handle to a subclass of the C++ type `T`. It - can be used to bind functions that take the associated Python object in its - wrapped form, while rejecting objects with a different type (i.e., it is - more discerning than :cpp:class:`handle`, which accepts *any* Python object). - - .. code-block:: cpp - - // Bind the class A - class A { int value; }; - nb::class_(m, "A"); - - // Bind a function that takes a Python object representing a 'A' instance - m.def("process_a", [](nb::handle_t h) { - PyObject * a_py = h.ptr(); // PyObject* pointer to wrapper - A &a_cpp = nb::cast(h); // Reference to C++ instance - }); - -.. cpp:class:: template type_object_t : public type_object - - Wrapper class representing a Python type object that is a subtype of the C++ - type `T`. It can be used to bind functions that only accept type objects - satisfying this criterion (i.e., it is more discerning than - :cpp:class:`type_object`, which accepts *any* Python type object). - -Error management ----------------- - -nanobind provides a range of functionality to convert C++ exceptions into -equivalent Python exceptions and raise captured Python error state in C++. The -:cpp:class:`exception` class is also relevant in this context, but is listed in -the reference section on :ref:`class binding `. - -.. cpp:struct:: error_scope - - RAII helper class that temporarily stashes any existing Python error status. - This is important when running Python code in the context of an existing - failure that must be processed (e.g., to generate an error message). - - .. cpp:function:: error_scope() - - Stash the current error status (if any) - - .. cpp:function:: ~error_scope() - - Restore the stashed error status (if any) - -.. cpp:struct:: python_error : public std::exception - - Exception that represents a detected Python error status. - - .. cpp:function:: python_error() - - This constructor may only be called when a Python error has occurred - (``PyErr_Occurred()`` must be ``true``). It creates a C++ exception - object that represents this error and clears the Python error status. - - .. cpp:function:: python_error(const python_error &) - - Copy constructor - - .. cpp:function:: python_error(python_error &&) noexcept - - Move constructor - - .. cpp:function:: const char * what() noexcept - - Return a stringified version of the exception. nanobind internally - normalizes the exception and generates a traceback that is included - as part of this string. This can be a relatively costly operation - and should only be used if all of this detail is actually needed. - - .. cpp:function:: bool matches(handle exc) noexcept - - Checks whether the exception has the same type as `exc`. - - The argument to this function is usually one of the `Standard Exceptions - `_. - - .. cpp:function:: void restore() noexcept - - Restore the error status in Python and clear the `python_error` - contents. This may only be called once, and you should not - reraise the `python_error` in C++ afterward. - - .. cpp:function:: void discard_as_unraisable(handle context) noexcept - - Pass the error to Python's :py:func:`sys.unraisablehook`, which - prints a traceback to :py:data:`sys.stderr` by default but may - be overridden. Like :cpp:func:`restore`, this consumes the - error and you should not reraise the exception in C++ afterward. - - The *context* argument should be some object whose ``repr()`` - helps identify the location of the error. The default - :py:func:`sys.unraisablehook` prints a traceback that begins - with the text ``Exception ignored in:`` followed by - the result of ``repr(context)``. - - Example use case: handling a Python error that occurs in a C++ - destructor where you cannot raise a C++ exception. - - .. cpp:function:: void discard_as_unraisable(const char * context) noexcept - - Convenience wrapper around the above function, which takes a C-style - string for the ``context`` argument. - - .. cpp:function:: handle type() const - - Returns a handle to the exception type - - .. cpp:function:: handle value() const - - Returns a handle to the exception value - - .. cpp:function:: object traceback() const - - Returns a handle to the exception's traceback object - -.. cpp:class:: cast_error - - The function :cpp:func:`cast` raises this exception to indicate that a cast - was unsuccessful. - - .. cpp:function:: cast_error() - - Constructor - -.. cpp:class:: next_overload - - Raising this special exception from a bound function informs nanobind that - the function overload detected incompatible inputs. nanobind will then try - other overloads before reporting a ``TypeError``. - - This feature is useful when a multiple overloads of a function accept - overlapping or identical input types (e.g. :cpp:class:`object`) and must run - code at runtime to select the right overload. - - You should probably write a thorough docstring that explicitly mentions the - expected inputs in this case, since the behavior won't be obvious from the - auto-generated function signature. It can be frustrating when a function - call fails with an error message stating that the provided arguments aren't - compatible with any overload, when the associated error message suggests - otherwise. - - .. cpp:function:: next_overload() - - Constructor - -.. cpp:class:: builtin_exception : public std::runtime_error - - General-purpose class to propagate builtin Python exceptions from C++. A - number of convenience functions (see below) instantiate it. - -.. cpp:function:: builtin_exception stop_iteration(const char * what = nullptr) - - Convenience wrapper to create a :cpp:class:`builtin_exception` C++ exception - instance that nanobind will re-raise as a Python ``StopIteration`` exception - when it crosses the C++ ↔ Python interface. - -.. cpp:function:: builtin_exception index_error(const char * what = nullptr) - - Convenience wrapper to create a :cpp:class:`builtin_exception` C++ exception - instance that nanobind will re-raise as a Python ``IndexError`` exception - when it crosses the C++ ↔ Python interface. - -.. cpp:function:: builtin_exception key_error(const char * what = nullptr) - - Convenience wrapper to create a :cpp:class:`builtin_exception` C++ exception - instance that nanobind will re-raise as a Python ``KeyError`` exception - when it crosses the C++ ↔ Python interface. - -.. cpp:function:: builtin_exception value_error(const char * what = nullptr) - - Convenience wrapper to create a :cpp:class:`builtin_exception` C++ exception - instance that nanobind will re-raise as a Python ``ValueError`` exception - when it crosses the C++ ↔ Python interface. - -.. cpp:function:: builtin_exception type_error(const char * what = nullptr) - - Convenience wrapper to create a :cpp:class:`builtin_exception` C++ exception - instance that nanobind will re-raise as a Python ``TypeError`` exception - when it crosses the C++ ↔ Python interface. - -.. cpp:function:: builtin_exception buffer_error(const char * what = nullptr) - - Convenience wrapper to create a :cpp:class:`builtin_exception` C++ exception - instance that nanobind will re-raise as a Python ``BufferError`` exception - when it crosses the C++ ↔ Python interface. - -.. cpp:function:: builtin_exception import_error(const char * what = nullptr) - - Convenience wrapper to create a :cpp:class:`builtin_exception` C++ exception - instance that nanobind will re-raise as a Python ``ImportError`` exception - when it crosses the C++ ↔ Python interface. - -.. cpp:function:: builtin_exception attribute_error(const char * what = nullptr) - - Convenience wrapper to create a :cpp:class:`builtin_exception` C++ exception - instance that nanobind will re-raise as a Python ``AttributeError`` exception - when it crosses the C++ ↔ Python interface. - -.. cpp:function:: void register_exception_translator(void (* exception_translator)(const std::exception_ptr &, void*), void * payload = nullptr) - - Install an exception translator callback that will be invoked whenever - nanobind's function call dispatcher catches a previously unknown C++ - exception. This exception translator should follow a standard structure of - re-throwing an exception, catching a specific type, and converting this into - a Python error status upon "success". - - Here is an example for a hypothetical ``ZeroDivisionException``. - - .. code-block:: cpp - - register_exception_translator( - [](const std::exception_ptr &p, void * /*payload*/) { - try { - std::rethrow_exception(p); - } catch (const ZeroDivisionException &e) { - PyErr_SetString(PyExc_ZeroDivisionError, e.what()); - } - }, nullptr /*payload*/); - - Generally, you will want to use the more convenient exception binding - interface provided by :cpp:class:`exception` class. This function provides - an escape hatch for more specialized use cases. - -.. cpp:function:: void chain_error(handle type, const char * fmt, ...) noexcept - - Raise a Python error of type ``type`` using the format string ``fmt`` - interpreted by ``PyErr_FormatV``. - - If a Python error state was already set prior to calling this method, then - the new error is *chained* on top of the existing one. Otherwise, the - function creates a new error without initializing its ``__cause__`` field. - -.. cpp:function:: void raise_from(python_error &e, handle type, const char * fmt, ...) - - Convenience wrapper around :cpp:func:`chain_error `. It takes - an existing Python error (e.g. caught in a ``catch`` block) and creates an - additional Python exception with the current error as cause. It then - re-raises :cpp:class:`python_error`. The argument ``fmt`` is a - ``printf``-style format string interpreted by ``PyErr_FormatV``. - - Usage of this function is explained in the documentation section on - :ref:`exception chaining `. - -.. cpp:function:: void raise(const char * fmt, ...) - - This function takes a ``printf``-style format string with arguments and then - raises a ``std::runtime_error`` with the formatted string. The function has - no dependence on Python, and nanobind merely includes it for convenience. - -.. cpp:function:: void raise_type_error(const char * fmt, ...) - - This function is analogous to :cpp:func:`raise`, except that it raises a - :cpp:class:`builtin_exception` that will convert into a Python ``TypeError`` - when crossing the language interface. - -.. cpp:function:: void raise_python_error() - - This function should only be called if a Python error status was set by a - prior operation, which should now be raised as a C++ exception. The function - is analogous to the statement ``throw python_error();`` but compiles into - more compact code. - -Casting -------- - -.. cpp:function:: template T cast(const detail::api &value, bool convert = true) - - Convert the Python object `value` (typically a :cpp:class:`handle` or a - :cpp:class:`object` subclass) into a C++ object of type `T`. - - When the `convert` argument is set to ``true`` (the default), the - implementation may also attempt *implicit conversions* to perform the cast. - - The function raises a :cpp:type:`cast_error` when the conversion fails. - See :cpp:func:`try_cast()` for an alternative that never raises. - -.. cpp:function:: template bool try_cast(const detail::api &value, T &out, bool convert = true) noexcept - - Convert the Python object `value` (typically a :cpp:class:`handle` or a - :cpp:class:`object` subclass) into a C++ object of type `T`, and store it - in the output parameter `out`. - - When the `convert` argument is set to ``true`` (the default), the - implementation may also attempt *implicit conversions* to perform the cast. - - The function returns ``false`` when the conversion fails. In this case, the - `out` parameter is left untouched. See :cpp:func:`cast()` for an alternative - that instead raises an exception in this case. - -.. cpp:function:: template object cast(T &&value, rv_policy policy = rv_policy::automatic_reference) - - Convert the C++ object ``value`` into a Python object. The return value - policy `policy` is used to handle ownership-related questions when a new - Python object must be created. - - The function raises a :cpp:type:`cast_error` when the conversion fails. - -.. cpp:function:: template object cast(T &&value, rv_policy policy, handle parent) - - Convert the C++ object ``value`` into a Python object. The return value - policy `policy` is used to handle ownership-related questions when a new - Python object must be created. A valid `parent` object is required when - specifying a `reference_internal` return value policy. - - The function raises a :cpp:type:`cast_error` when the conversion fails. - -.. cpp:function:: template object find(const T &value) noexcept - - Return the Python object associated with the C++ instance `value`. When no - such object can be found, the function it returns an invalid object - (:cpp:func:`detail::api::is_valid()` is ``false``). - -.. cpp:function:: template tuple make_tuple(Args&&... args) - - Create a Python tuple from a sequence of C++ objects ``args...``. The return - value policy `policy` is used to handle ownership-related questions when a - new Python objects must be created. - - The function raises a :cpp:type:`cast_error` when the conversion fails. - -Common binding annotations --------------------------- - -The following annotations can be specified in both function and class bindings. - -.. cpp:struct:: scope - - .. cpp:function:: scope(handle value) - - Captures the Python scope (e.g., a :cpp:class:`module_` or - :cpp:class:`type_object`) in which the function or class should be - registered. - -.. _function_binding_annotations: - -Function binding annotations ----------------------------- - -The following annotations can be specified using the variable-length ``Extra`` -parameter of :cpp:func:`module_::def`, :cpp:func:`class_::def`, -:cpp:func:`cpp_function`, etc. - -.. cpp:struct:: name - - .. cpp:function:: name(const char * value) - - Specify this annotation to override the name of the function. - - nanobind will internally copy the string when creating a function - binding, hence dynamically generated arguments with a limited lifetime - are legal. - -.. cpp:struct:: arg - - Function argument annotation to enable keyword-based calling, default - arguments, passing ``None``, and implicit conversion hints. Note that when a - function argument should be annotated, you *must* specify annotations for all - arguments of that function. - - Example use: - - .. code-block:: cpp - - m.def("add", [](int a, int b) { return a + b; }, nb::arg("a"), nb::arg("b")); - - It is usually convenient to add the following ``using`` declaration to your binding code. - - .. code-block:: cpp - - using namespace nb::literals; - - In this case, the argument annotations can be shortened: - - .. code-block:: cpp - - m.def("add", [](int a, int b) { return a + b; }, "a"_a, "b"_a); - - .. cpp:function:: explicit arg(const char * name = nullptr) - - Create a function argument annotation. The name is optional. - - .. cpp:function:: template arg_v operator=(T &&value) const - - Return an argument annotation that is like this one but also assigns a - default value to the argument. The default will be converted into a Python - object immediately, so its bindings must have already been defined. - - .. cpp:function:: arg &none(bool value = true) - - Set a flag noting that the function argument accepts ``None``. Can only - be used for python wrapper types (e.g. :cpp:class:`handle`, - :cpp:class:`int_`) and types that have been bound using - :cpp:class:`class_`. You cannot use this to implement functions that - accept null pointers to builtin C++ types like ``int *i = nullptr``. - - .. cpp:function:: arg &noconvert(bool value = true) - - Set a flag noting that implicit conversion should never be performed for - this function argument. - - .. cpp:function:: arg &sig(const char * sig) - - Override the signature of the default argument value. This is useful when - the argument value is unusually complex so that the default method to - explain it in docstrings and stubs (``str(value)``) does not produce - acceptable output. - - .. cpp:function:: arg_locked lock() - - Return an argument annotation that is like this one but also requests that - this argument be locked when dispatching a function call in free-threaded - Python extensions. It does nothing in regular GIL-protected extensions. - -.. cpp:struct:: is_method - - Indicate that the bound function is a method. - -.. cpp:struct:: is_operator - - Indicate that the bound operator represents a special double underscore - method (``__add__``, ``__radd__``, etc.) that implements an arithmetic - operation. - - When a bound functions with this annotation is called with incompatible - arguments, it will return ``NotImplemented`` rather than raising a - ``TypeError``. - -.. cpp:struct:: is_implicit - - Indicate that the bound constructor can be used to perform implicit conversions. - -.. cpp:struct:: lock_self - - Indicate that the implicit ``self`` argument of a method should be locked - when dispatching a call in a free-threaded extension. This annotation does - nothing in regular GIL-protected extensions. - -.. cpp:struct:: template call_guard - - Invoke the call guard(s) `Ts` when the bound function executes. The RAII - helper :cpp:struct:`gil_scoped_release` is often combined with this feature. - -.. cpp:struct:: template keep_alive - - Following evaluation of the bound function, keep the object referenced by - index ``Patient`` alive *as long as* the object with index ``Nurse`` exists. - This uses the following indexing convention: - - - Index ``0`` refers to the return value of methods. It should not be used - in constructors or functions that do not return a result. - - - Index ``1`` refers to the first argument. In methods and constructors, - index ``1`` refers to the implicit ``this`` pointer, while regular - arguments begin at index ``2``. - - The annotation has the following runtime characteristics: - - - It does nothing when the nurse or patient object are ``None``. - - - It raises an exception when the nurse object is neither - weak-referenceable nor an instance of a binding created via - :cpp:class:`nb::class_\<..\> `. - - Two additional caveats regarding :cpp:class:`keep_alive ` are - noteworthy: - - - It *usually* doesn't make sense to specify a ``Nurse`` or ``Patient`` for an - argument or return value handled by a :ref:`type caster ` - (e.g., a STL vector handled via the include directive ``#include - ``). That's because type casters copy-convert the - Python object into an equivalent C++ object, whose lifetime is decoupled - from the original Python object. However, the :cpp:class:`keep_alive - ` annotation *only* affects the lifetime of Python objects - *and not their C++ copy*. - - - Dispatching a Python → C++ function call may require the :ref:`implicit - conversion ` of function arguments. In this case, the objects - passed to the C++ function differ from the originally specified arguments. - The ``Nurse`` and ``Patient`` annotation always refer to the *final* object - following implicit conversion. - -.. cpp:struct:: sig - - .. cpp:function:: sig(const char * value) - - This is *both* a class and a function binding annotation. - - 1. When used in functions bindings, it provides complete control over - the function's type signature by replacing the automatically generated - version with ``value``. You can use it to add or change arguments and - return values, tweak how default values are rendered, and add custom - decorators. - - Here is an example: - - .. code-block:: cpp - - nb::def("function_name", &function_name, - nb::sig( - "@decorator(decorator_args..)\n - "def function_name(arg_1: type_1 = def_1, ...) -> ret" - )); - - - 2. When used in class bindings, the annotation enables complete control - over how the class is rendered by nanobind's ``stubgen`` program. You - can use it add decorators, specify ``typing.TypeVar``-parameterized - base classes, metaclasses, etc. - - Here is an example: - - .. code-block:: cpp - - nb::class_(m, "Class", - nb::sig( - "@decorator(decorator_args..)\n" - "class Class(Base1[T], Base2, meta=Meta)" - )); - - Deviating significantly from the nanobind-generated signature likely - means that the class or function declaration is a *lie*, but such lies - can be useful to type-check complex binding projects. - - Specifying decorators isn't required---the above are just examples to - show that this is possible. - - nanobind will internally copy the signature during function/type - creation, hence dynamically generated strings with a limited lifetime are - legal. - - The provided string should be valid Python signature, but *without* a - trailing colon (``":"``) or trailing newline. Furthermore, nanobind - analyzes the string and expects to find the name of the function or class - on the *last line* between the ``"def"`` / ``"class"`` prefix and the - opening parenthesis. - - For function bindings, this name must match the specified function name - in ``.def("name", ..)``-style binding declarations, and for class - bindings, the specified name must match the ``name`` argument of - :cpp:class:`nb::class_ `. - -.. cpp:enum-class:: rv_policy - - A return value policy determines the question of *ownership* when a bound - function returns a previously unknown C++ instance that must now be - converted into a Python object. - - Return value policies apply to functions that return values handled using - :ref:`class bindings `, which means that their Python equivalent - was registered using :cpp:class:`class_\<...\> `. They are ignored - in most other cases. One exception are STL types handled using :ref:`type - casters ` (e.g. ``std::vector``), which contain a nested - type ``T`` handled using class bindings. In this case, the return value - policy also applies recursively. - - A return value policy is unnecessary when the type itself clarifies - ownership (e.g., ``std::unique_ptr``, ``std::shared_ptr``, a type with - :ref:`intrusive reference counting `). - - The following policies are available (where `automatic` is the default). - Please refer to the :ref:`return value policy section ` of the main - documentation, which clarifies the list below using concrete examples. - - .. cpp:enumerator:: take_ownership - - Create a Python object that wraps the existing C++ instance and takes - full ownership of it. No copies are made. Python will call the C++ - destructor and ``delete`` operator when the Python wrapper is garbage - collected at some later point. The C++ side *must* relinquish ownership - and is not allowed to destruct the instance, or undefined behavior will - ensue. - - .. cpp:enumerator:: copy - - Copy-construct a new Python object from the C++ instance. The new copy - will be owned by Python, while C++ retains ownership of the original. - - .. cpp:enumerator:: move - - Move-construct a new Python object from the C++ instance. The new object - will be owned by Python, while C++ retains ownership of the original - (whose contents were likely invalidated by the move operation). - - .. cpp:enumerator:: reference - - Create a Python object that wraps the existing C++ instance *without - taking ownership* of it. No copies are made. Python will never call the - destructor or ``delete`` operator, even when the Python wrapper is - garbage collected. - - .. cpp:enumerator:: reference_internal - - A safe extension of the `reference` policy for methods that implement - some form of attribute access. It creates a Python object that wraps the - existing C++ instance *without taking ownership* of it. Additionally, it - adjusts reference counts to keeps the method's implicit ``self`` argument - alive until the newly created object has been garbage collected. - - .. cpp:enumerator:: none - - This is the most conservative policy: it simply refuses the cast unless - the C++ instance already has a corresponding Python object, in which case - the question of ownership becomes moot. - - .. cpp:enumerator:: automatic - - This is the default return value policy, which falls back to - `take_ownership` when the return value is a pointer, `move` when it is a - rvalue reference, and `copy` when it is a lvalue reference. - - .. cpp:enumerator:: automatic_reference - - This policy matches `automatic` but falls back to `reference` when the - return value is a pointer. - -.. cpp:struct:: kw_only - - Indicate that all following function parameters are keyword-only. This - may only be used if you supply an :cpp:struct:`arg` annotation for each - parameters, because keyword-only parameters are useless if they don't have - names. For example, if you write - - .. code-block:: cpp - - int some_func(int one, const char* two); - - m.def("some_func", &some_func, - nb::arg("one"), nb::kw_only(), nb::arg("two")); - - then in Python you can write ``some_func(42, two="hi")``, or - ``some_func(one=42, two="hi")``, but not ``some_func(42, "hi")``. - - Just like in Python, any parameters appearing after variadic - :cpp:class:`*args ` are implicitly keyword-only. You don't - need to include the :cpp:struct:`kw_only` annotation in this case, - but if you do include it, it must be in the correct position: - immediately after the :cpp:struct:`arg` annotation for the variadic - :cpp:class:`*args ` parameter. - -.. cpp:struct:: template for_getter - - When defining a property with a getter and a setter, you can use this to - only pass a function binding attribute to the getter part. An example is - shown below. - - .. code-block:: cpp - - nb::class_(m, "MyClass") - .def_prop_rw("value", &MyClass::value, - nb::for_getter(nb::sig("def value(self, /) -> int")), - nb::for_setter(nb::sig("def value(self, value: int, /) -> None")), - nb::for_getter("docstring for getter"), - nb::for_setter("docstring for setter")); - -.. cpp:struct:: template for_setter - - Analogous to :cpp:struct:`for_getter`, but for setters. - -.. cpp:struct:: template call_policy - - Request that custom logic be inserted around each call to the - bound function, by calling ``Policy::precall(args, nargs, cleanup)`` before - Python-to-C++ argument conversion, and ``Policy::postcall(args, nargs, ret)`` - after C++-to-Python return value conversion. - - If multiple call policy annotations are provided for the same function, then - their precall and postcall hooks will both execute left-to-right according - to the order in which the annotations were specified when binding the - function. - - The :cpp:struct:`nb::call_guard\() ` annotation - should be preferred over ``call_policy`` unless the wrapper logic - depends on the function arguments or return value. - If both annotations are combined, then - :cpp:struct:`nb::call_guard\() ` always executes on - the "inside" (closest to the bound function, after argument - conversions and before return value conversion) regardless of its - position in the function annotations list. - - Your ``Policy`` class must define two static member functions: - - .. cpp:function:: static void precall(PyObject **args, size_t nargs, detail::cleanup_list *cleanup); - - A hook that will be invoked before calling the bound function. More - precisely, it is called after any :ref:`argument locks ` - have been obtained, but before the Python arguments are converted to C++ - objects for the function call. - - This hook may access or modify the function arguments using the - *args* array, which holds borrowed references in one-to-one - correspondence with the C++ arguments of the bound function. If - the bound function is a method, then ``args[0]`` is its *self* - argument. *nargs* is the number of function arguments. It is actually - passed as ``std::integral_constant()``, so you can - match on that type if you want to do compile-time checks with it. - - The *cleanup* list may be used as it is used in type casters, - to cause some Python object references to be released at some point - after the bound function completes. (If the bound function is part - of an overload set, the cleanup list isn't released until all overloads - have been tried.) - - ``precall()`` may choose to throw a C++ exception. If it does, - it will preempt execution of the bound function, and the - exception will be treated as if the bound function had thrown it. - - .. cpp:function:: static void postcall(PyObject **args, size_t nargs, handle ret); - - A hook that will be invoked after calling the bound function and - converting its return value to a Python object, but only if the - bound function returned normally. - - *args* stores the Python object arguments, with the same semantics - as in ``precall()``, except that arguments that participated in - implicit conversions will have had their ``args[i]`` pointer updated - to reflect the new Python object that the implicit conversion produced. - *nargs* is the number of arguments, passed as a ``std::integral_constant`` - in the same way as for ``precall()``. - - *ret* is the bound function's return value. If the bound function returned - normally but its C++ return value could not be converted to a Python - object, then ``postcall()`` will execute with *ret* set to null, - and the Python error indicator might or might not be set to explain why. - - If the bound function did not return normally -- either because its - Python object arguments couldn't be converted to the appropriate C++ - types, or because the C++ function threw an exception -- then - ``postcall()`` **will not execute**. If you need some cleanup logic to - run even in such cases, your ``precall()`` can add a capsule object to the - cleanup list; its destructor will run eventually, but with no promises - as to when. A :cpp:struct:`nb::call_guard ` might be a - better choice. - - ``postcall()`` may choose to throw a C++ exception. If it does, - the result of the wrapped function will be destroyed, - and the exception will be raised in its place, as if the bound function - had thrown it just before returning. - - Here is an example policy to demonstrate. - ``nb::call_policy>()`` behaves like - :cpp:class:`nb::keep_alive\<0, I\>() `, except that the - return value is a treated as a list of objects rather than a single one. - - .. code-block:: cpp - - template - struct returns_references_to { - static void precall(PyObject **, size_t, nb::detail::cleanup_list *) {} - - template - static void postcall(PyObject **args, - std::integral_constant, - nb::handle ret) { - static_assert(I > 0 && I <= N, - "I in returns_references_to must be in the " - "range [1, number of C++ function arguments]"); - if (!nb::isinstance(ret)) { - throw std::runtime_error("return value should be a sequence"); - } - for (nb::handle nurse : ret) { - nb::detail::keep_alive(nurse.ptr(), args[I - 1]); - } - } - }; - - For a more complex example (binding an object that uses trivially-copyable - callbacks), see ``tests/test_callbacks.cpp`` in the nanobind source - distribution. - -.. _class_binding_annotations: - -Class binding annotations -------------------------- - -The following annotations can be specified using the variable-length ``Extra`` -parameter of the constructor :cpp:func:`class_::class_`. - -Besides the below options, also refer to the :cpp:class:`sig` which is -usable in both function and class bindings. It can be used to override class -declarations in generated :ref:`stubs `, - -.. cpp:struct:: is_final - - Indicate that a type cannot be subclassed. - -.. cpp:struct:: dynamic_attr - - Indicate that instances of a type require a Python dictionary to support the dynamic addition of attributes. - -.. cpp:struct:: is_weak_referenceable - - Indicate that instances of a type require a weak reference list so that they - can be referenced by the Python ``weakref.*`` types. - -.. cpp:struct:: is_generic - - If present, nanobind will add a ``__class_getitem__`` function to the newly - created type that permits constructing *parameterized* versions (e.g., - ``MyType[int]``). The implementation of this function is equivalent to - - .. code-block:: python - - def __class_getitem__(cls, value): - import types - return types.GenericAlias(cls, value) - - 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 - store supplemental data in the type object. See :ref:`Supplemental - type data ` for more information. - -.. cpp:struct:: type_slots - - .. cpp:function:: type_slots(PyType_Slot * value) - - nanobind uses the ``PyType_FromSpec`` Python C API interface to construct - types. In certain advanced use cases, it may be helpful to append additional - type slots during type construction. This class binding annotation can be - used to accomplish this. The provided list should be followed by a - zero-initialized ``PyType_Slot`` element. See :ref:`Customizing type creation - ` for more information about this feature. - -.. cpp:struct:: template intrusive_ptr - - nanobind provides a custom interface for intrusive reference-counted C++ - types that nicely integrate with Python reference counting. See the - :ref:`separate section ` on this topic. This annotation - marks a type as compatible with this interface. - - .. cpp:function:: intrusive_ptr(void (* set_self_py)(T*, PyObject*) noexcept) - - Declares a callback that will be invoked when a C++ instance is first - cast into a Python object. - - -.. _enum_binding_annotations: - -Enum binding annotations ------------------------- - -The following annotations can be specified using the variable-length -``Extra`` parameter of the constructor :cpp:func:`enum_::enum_`. - -.. cpp:struct:: is_arithmetic - - Indicate that the enumeration supports arithmetic operations. This enables - both unary (``-``, ``~``, ``abs()``) and binary (``+``, ``-``, ``*``, - ``//``, ``&``, ``|``, ``^``, ``<<``, ``>>``) operations with operands of - either enumeration or numeric types. - - The result will be as if the operands were first converted to integers. (So - ``Shape(2) + Shape(1) == 3`` and ``Shape(2) * 1.5 == 3.0``.) It is - unspecified whether operations on mixed enum types (such as ``Shape.Circle + - Color.Red``) are permissible. - - Passing this annotation changes the Python enumeration parent class to - either :py:class:`enum.IntEnum` or :py:class:`enum.IntFlag`, depending on - whether or not the flag enumeration attribute is also specified (see - :cpp:class:`is_flag`). - -.. cpp:struct:: is_flag - - Indicate that the enumeration supports bit-wise operations. This enables the - operators (``|``, ``&``, ``^``, and ``~``) with two enumerators as operands. - - The result has the same type as the operands, i.e., ``Shape(2) | Shape(1)`` - will be equivalent to ``Shape(3)``. - - Passing this annotation changes the Python enumeration parent class to - either :py:class:`enum.IntFlag` or :py:class:`enum.Flag`, depending on - whether or not the enumeration is also marked to support arithmetic - operations (see :cpp:class:`is_arithmetic`). - -Function binding ----------------- - -.. cpp:function:: object cpp_function(Func &&f, const Extra&... extra) - - Convert the function `f` into a Python callable. This function has - a few overloads (not shown here) to separately deal with function/method - pointers and lambda functions. - - The variable length `extra` parameter can be used to pass a docstring and - other :ref:`function binding annotations `. - -.. _class_binding: - -Class binding -------------- - -.. cpp:class:: template class_ : public object - - Binding helper class to expose a custom C++ type `T` (declared using either - the ``class`` or ``struct`` keyword) in Python. - - The variable length parameter `Ts` is optional and can be used to specify - the base class of `T` and/or an alias needed to realize :ref:`trampoline - classes `. - - When the type ``T`` was previously already registered (either within the - same extension or another extension), the ``class_<..>`` declaration is - redundant. nanobind will print a warning message in this case: - - .. code-block:: text - - RuntimeWarning: nanobind: type 'MyType' was already registered! - - The ``class_<..>`` instance will subsequently wrap the original type object - instead of creating a new one. - - .. cpp:function:: template class_(handle scope, const char * name, const Extra &... extra) - - Bind the type `T` to the identifier `name` within the scope `scope`. The - variable length `extra` parameter can be used to pass a docstring and - other :ref:`class binding annotations `. - - .. cpp:function:: template class_ &def(const char * name, Func &&f, const Extra &... extra) - - Bind the function `f` and assign it to the class member `name`. - The variable length `extra` parameter can be used to pass a docstring and - other :ref:`function binding annotations `. - - This function has two overloads (listed just below) to handle constructor - binding declarations. - - **Example**: - - .. code-block:: cpp - - struct A { - void f() { /*...*/ } - }; - - nb::class_(m, "A") - .def(nb::init<>()) // Bind the default constructor - .def("f", &A::f); // Bind the method A::f - - .. cpp:function:: template class_ &def(init arg, const Extra &... extra) - - Bind a constructor. The variable length `extra` parameter can be used to - pass a docstring and other :ref:`function binding annotations - `. - - .. cpp:function:: template class_ &def(init_implicit arg, const Extra &... extra) - - Bind a constructor that may be used for implicit type conversions. The - constructor must take a single argument of an unspecified type `Arg`. - - When nanobind later tries to dispatch a function call requiring an - argument of type `T` while `Arg` was actually provided, it will run this - constructor to perform the necessary conversion. - - The variable length `extra` parameter can be used to pass a docstring and - other :ref:`function binding annotations `. - - This constructor generates more compact code than a separate call to - :cpp:func:`implicitly_convertible`, but is otherwise equivalent. - - .. cpp:function:: template class_ &def(new_ arg, const Extra &... extra) - - Bind a C++ factory function as a Python object constructor (``__new__``). - This is an advanced feature; prefer :cpp:struct:`nb::init\<..\> ` - where possible. See the discussion of :ref:`customizing object creation - ` for more details. - - .. cpp:function:: template class_ &def(def_visitor arg, const Extra &... extra) - - Dispatch to custom user-provided binding logic implemented by the type - ``Visitor``, passing it the binding annotations ``extra...``. - See the documentation of :cpp:struct:`nb::def_visitor\<..\> ` - for details. - - .. cpp:function:: template class_ &def_rw(const char * name, D C::* p, const Extra &...extra) - - Bind the field `p` and assign it to the class member `name`. nanobind - constructs a ``property`` object with *read-write* access (hence the - ``rw`` suffix) to do so. - - Every access from Python will read from or write to the C++ field while - performing a suitable conversion (using :ref:`type casters - `, :ref:`bindings `, or :ref:`wrappers - `) as determined by its type. - - The variable length `extra` parameter can be used to pass a docstring and - other :ref:`function binding annotations ` - that are forwarded to the anonymous functions used to construct the - property. - Use the :cpp:struct:`nb::for_getter ` and - :cpp:struct:`nb::for_setter ` to pass annotations - specifically to the setter or getter part. - - **Example**: - - .. code-block:: cpp - - struct A { int value; }; - - nb::class_(m, "A") - .def_rw("value", &A::value); // Enable mutable access to the field A::value - - .. cpp:function:: template class_ &def_ro(const char * name, D C::* p, const Extra &...extra) - - Bind the field `p` and assign it to the class member `name`. nanobind - constructs a ``property`` object with *read only* access (hence the - ``ro`` suffix) to do so. - - Every access from Python will read the C++ field while performing a - suitable conversion (using :ref:`type casters `, - :ref:`bindings `, or :ref:`wrappers `) as determined - by its type. - - The variable length `extra` parameter can be used to pass a docstring and - other :ref:`function binding annotations ` - that are forwarded to the anonymous functions used to construct the - property. - - **Example**: - - .. code-block:: cpp - - struct A { int value; }; - - nb::class_(m, "A") - .def_ro("value", &A::value); // Enable read-only access to the field A::value - - .. cpp:function:: template class_ &def_prop_rw(const char * name, Getter &&getter, Setter &&setter, const Extra &...extra) - - Construct a *mutable* (hence the ``rw`` suffix) Python ``property`` and - assign it to the class member `name`. Every read access will call the - function ``getter`` with the `T` instance, and every write access will - call the ``setter`` with the `T` instance and value to be assigned. - - The variable length `extra` parameter can be used to pass a docstring and - other :ref:`function binding annotations `. - Use the :cpp:struct:`nb::for_getter ` and - :cpp:struct:`nb::for_setter ` to pass annotations - specifically to the setter or getter part. - - Note that this function implicitly assigns the - :cpp:enumerator:`rv_policy::reference_internal` return value policy to - `getter` (as opposed to the usual - :cpp:enumerator:`rv_policy::automatic`). Provide an explicit return value - policy as part of the `extra` argument to override this. - - **Example**: the example below uses `def_prop_rw` to expose a C++ - setter/getter pair as a more "Pythonic" property: - - .. code-block:: cpp - - class A { - public: - A(int value) : m_value(value) { } - void set_value(int value) { m_value = value; } - int value() const { return m_value; } - private: - int m_value; - }; - - nb::class_(m, "A") - .def(nb::init()) - .def_prop_rw("value", - [](A &t) { return t.value() ; }, - [](A &t, int value) { t.set_value(value); }); - - .. cpp:function:: template class_ &def_prop_ro(const char * name, Getter &&getter, const Extra &...extra) - - Construct a *read-only* (hence the ``ro`` suffix) Python ``property`` and - assign it to the class member `name`. Every read access will call the - function ``getter`` with the `T` instance. - - The variable length `extra` parameter can be used to pass a docstring and - other :ref:`function binding annotations `. - - Note that this function implicitly assigns the - :cpp:enumerator:`rv_policy::reference_internal` return value policy to - `getter` (as opposed to the usual - :cpp:enumerator:`rv_policy::automatic`). Provide an explicit return value - policy as part of the `extra` argument to override this. - - **Example**: the example below uses `def_prop_ro` to expose a C++ getter - as a more "Pythonic" property: - - .. code-block:: cpp - - class A { - public: - A(int value) : m_value(value) { } - int value() const { return m_value; } - private: - int m_value; - }; - - nb::class_(m, "A") - .def(nb::init()) - .def_prop_ro("value", - [](A &t) { return t.value() ; }); - - .. cpp:function:: template class_ &def_static(const char * name, Func &&f, const Extra &... extra) - - Bind the *static* function `f` and assign it to the class member `name`. - The variable length `extra` parameter can be used to pass a docstring and - other :ref:`function binding annotations `. - - **Example**: - - .. code-block:: cpp - - struct A { - static void f() { /*...*/ } - }; - - nb::class_(m, "A") - .def_static("f", &A::f); // Bind the static method A::f - - .. cpp:function:: template class_ &def_rw_static(const char * name, D* p, const Extra &...extra) - - Bind the *static* field `p` and assign it to the class member `name`. nanobind - constructs a class ``property`` object with *read-write* access (hence the - ``rw`` suffix) to do so. - - Every access from Python will read from or write to the static C++ field - while performing a suitable conversion (using :ref:`type casters - `, :ref:`bindings `, or :ref:`wrappers - `) as determined by its type. - - The variable length `extra` parameter can be used to pass a docstring and - other :ref:`function binding annotations ` - that are forwarded to the anonymous functions used to construct the - property - Use the :cpp:struct:`nb::for_getter ` and - :cpp:struct:`nb::for_setter ` to pass annotations - specifically to the setter or getter part. - - **Example**: - - .. code-block:: cpp - - struct A { inline static int value = 5; }; - - nb::class_(m, "A") - // Enable mutable access to the static field A::value - .def_rw_static("value", &A::value); - - .. cpp:function:: template class_ &def_ro_static(const char * name, D* p, const Extra &...extra) - - Bind the *static* field `p` and assign it to the class member `name`. - nanobind constructs a class ``property`` object with *read-only* access - (hence the ``ro`` suffix) to do so. - - Every access from Python will read the static C++ field while performing - a suitable conversion (using :ref:`type casters `, - :ref:`bindings `, or :ref:`wrappers `) as determined - by its type. - - The variable length `extra` parameter can be used to pass a docstring and - other :ref:`function binding annotations ` - that are forwarded to the anonymous functions used to construct the - property - - **Example**: - - .. code-block:: cpp - - struct A { inline static int value = 5; }; - - nb::class_(m, "A") - // Enable read-only access to the static field A::value - .def_ro_static("value", &A::value); - - .. cpp:function:: template class_ &def_prop_rw_static(const char * name, Getter &&getter, Setter &&setter, const Extra &...extra) - - Construct a *mutable* (hence the ``rw`` suffix) Python ``property`` and - assign it to the class member `name`. Every read access will call the - function ``getter`` with `T`'s Python type object, and every write access will - call the ``setter`` with `T`'s Python type object and value to be assigned. - - The variable length `extra` parameter can be used to pass a docstring and - other :ref:`function binding annotations `. - Use the :cpp:struct:`nb::for_getter ` and - :cpp:struct:`nb::for_setter ` to pass annotations - specifically to the setter or getter part. - - Note that this function implicitly assigns the - :cpp:enumerator:`rv_policy::reference` return value policy to - `getter` (as opposed to the usual - :cpp:enumerator:`rv_policy::automatic`). Provide an explicit return value - policy as part of the `extra` argument to override this. - - **Example**: the example below uses `def_prop_rw_static` to expose a - static C++ setter/getter pair as a more "Pythonic" property: - - .. code-block:: cpp - - class A { - public: - static void set_value(int value) { s_value = value; } - static int value() { return s_value; } - private: - inline static int s_value = 5; - }; - - nb::class_(m, "A") - .def_prop_rw_static("value", - [](nb::handle /*unused*/) { return A::value() ; }, - [](nb::handle /*unused*/, int value) { A::set_value(value); }); - - .. cpp:function:: template class_ &def_prop_ro_static(const char * name, Getter &&getter, const Extra &...extra) - - Construct a *read-only* (hence the ``ro`` suffix) Python ``property`` and - assign it to the class member `name`. Every read access will call the - function ``getter`` with `T`'s Python type object. - - The variable length `extra` parameter can be used to pass a docstring and - other :ref:`function binding annotations `. - - Note that this function implicitly assigns the - :cpp:enumerator:`rv_policy::reference` return value policy to - `getter` (as opposed to the usual - :cpp:enumerator:`rv_policy::automatic`). Provide an explicit return value - policy as part of the `extra` argument to override this. - - **Example**: the example below uses `def_prop_ro_static` to expose a - static C++ getter as a more "Pythonic" property: - - .. code-block:: cpp - - class A { - public: - static int value() { return s_value; } - private: - inline static int s_value = 5; - }; - - nb::class_(m, "A") - .def_prop_ro_static("value", - [](nb::handle /*unused*/) { return A::value() ; }); - - .. cpp:function:: template class_ &def(const detail::op_ &op, const Extra&... extra) - - This interface provides convenient syntax sugar to replace relatively - lengthy method bindings with shorter operator bindings. To use it, you - will need an extra include directive: - - .. code-block:: cpp - - #include - - Below is an example type with three arithmetic operators in C++ (unary - negation and 2 binary subtraction overloads) along with corresponding - bindings. - - **Example**: - - .. code-block:: cpp - - struct A { - float value; - - A operator-() const { return { -value }; } - A operator-(const A &o) const { return { value - o.value }; } - A operator-(float o) const { return { value - o }; } - }; - - nb::class_(m, "A") - .def(nb::init()) - .def(-nb::self) - .def(nb::self - nb::self) - .def(nb::self - float()); - - - Bind an arithmetic or comparison operator expressed in short-hand form (e.g., ``.def(nb::self + nb::self)``). - - .. cpp:function:: template class_ &def_cast(const detail::op_ &op, const Extra&... extra) - - Like the above ``.def()`` variant, but furthermore cast the result of the operation back to `T`. - - -.. cpp:class:: template enum_ : public class_ - - Class binding helper for scoped and unscoped C++ enumerations. - - .. cpp:function:: template NB_INLINE enum_(handle scope, const char * name, const Extra &...extra) - - Bind the enumeration of type `T` to the identifier `name` within the - scope `scope`. The variable length `extra` parameter can be used to pass - a docstring and other :ref:`enum binding annotations - ` (currently, only :cpp:class:`is_arithmetic` is supported). - - .. cpp:function:: enum_ &value(const char * name, T value, const char * doc = nullptr) - - Add the entry `value` to the enumeration using the identifier `name`, - potentially with a docstring provided via `doc` (optional). - - .. cpp:function:: enum_ &export_values() - - Export all entries of the enumeration into the parent scope. - -.. cpp:class:: template exception : public object - - Class binding helper for declaring new Python exception types - - .. cpp:function:: exception(handle scope, const char * name, handle base = PyExc_Exception) - - Create a new exception type identified by `name` that derives from - `base`, and install it in `scope`. The constructor also calls - :cpp:func:`register_exception_translator()` to register a new exception - translator that converts caught C++ exceptions of type `T` into the - newly created Python equivalent. - -.. cpp:struct:: template init - - nanobind uses this simple helper class to capture the signature of a - constructor. It is only meant to be used in binding declarations done via - :cpp:func:`class_::def()`. - - Sometimes, it is necessary to bind constructors that don't exist in the - underlying C++ type (meaning that they are specific to the Python bindings). - Because `init` only works for existing C++ constructors, this requires - a manual workaround noting that - - .. code-block:: cpp - - nb::class_(m, "MyType") - .def(nb::init()); - - is syntax sugar for the following lower-level implementation using - "`placement new `_": - - .. code-block:: cpp - - nb::class_(m, "MyType") - .def("__init__", - [](MyType* t, const char* arg0, int arg1) { - new (t) MyType(arg0, arg1); - }); - - The provided lambda function will be called with a pointer to uninitialized - memory that has already been allocated (this memory region is co-located - with the Python object for reasons of efficiency). The lambda function can - then either run an in-place constructor and return normally (in which case - the instance is assumed to be correctly constructed) or fail by raising an - exception. - -.. cpp:struct:: template init_implicit - - See :cpp:class:`init` for detail on binding constructors. The main - difference between :cpp:class:`init` and `init_implicit` is that the latter - only supports constructors taking a single argument `Arg`, and that it marks - the constructor as usable for implicit conversions from `Arg`. - - Sometimes, it is necessary to bind implicit conversion-capable constructors - that don't exist in the underlying C++ type (meaning that they are specific - to the Python bindings). This can be done manually noting that - - .. code-block:: cpp - - nb::class_(m, "MyType") - .def(nb::init_implicit()); - - can be replaced by the lower-level code - - .. code-block:: cpp - - nb::class_(m, "MyType") - .def("__init__", - [](MyType* t, const char* arg0) { - new (t) MyType(arg0); - }); - - nb::implicitly_convertible(); - -.. cpp:struct:: template new_ - - This is a small helper class that indicates to :cpp:func:`class_::def()` - that a particular lambda or static method provides a Python object - constructor (``__new__``) for the class being bound. Normally, you would - use :cpp:class:`init` instead if possible, in order to cooperate with - nanobind's usual object creation process. Using :cpp:class:`new_` - replaces that process entirely. This is principally useful when some - C++ type of interest can only provide pointers to its instances, - rather than allowing them to be constructed directly. - - Like :cpp:class:`init`, the only use of a :cpp:class:`new_` object is - as an argument to :cpp:func:`class_::def()`. - - Example use: - - .. code-block:: cpp - - class MyType { - private: - MyType(); - public: - static std::shared_ptr create(); - int value = 0; - }; - - nb::class_(m, "MyType") - .def(nb::new_(&MyType::create)); - - Given this example code, writing ``MyType()`` in Python would - produce a Python object wrapping the result of ``MyType::create()`` - in C++. If multiple calls to ``create()`` return pointers to the - same C++ object, these will turn into references to the same Python - object as well. - - See the discussion of :ref:`customizing Python object creation ` - for more information. - -.. cpp:struct:: template def_visitor - - An empty base object which serves as a tag to allow :cpp:func:`class_::def()` - to dispatch to custom logic implemented by the type ``Visitor``. This is the - same mechanism used by :cpp:class:`init`, :cpp:class:`init_implicit`, and - :cpp:class:`new_`; it's exposed publicly so that you can create your own - reusable abstractions for binding logic. - - To define a ``def_visitor``, you would write something like: - - .. code-block:: cpp - - struct my_ops : nb::def_visitor { - template - void execute(Class &cl, const Extra&... extra) { - /* series of def() statements on `cl`, which is a nb::class_ */ - } - }; - - Then use it like: - - .. code-block:: cpp - - nb::class_(m, "MyType") - .def("some_method", &MyType::some_method) - .def(my_ops()) - ... ; - - Any arguments to :cpp:func:`class_::def()` after the ``def_visitor`` object - get passed through as the ``Extra...`` parameters to ``execute()``. - As with any other C++ object, data needed by the ``def_visitor`` can be passed - through template arguments or ordinary constructor arguments. - The ``execute()`` method may be static if it doesn't need to access anything - in ``*this``. - - -GIL Management --------------- - -These two `RAII -`_ helper -classes acquire and release the *Global Interpreter Lock* (GIL) in a given -scope. The :cpp:struct:`gil_scoped_release` helper is often combined with the -:cpp:struct:`call_guard`, as in - -.. code-block:: cpp - - m.def("expensive", &expensive, nb::call_guard()); - -This releases the interpreter lock while `expensive` is running, which permits -running it in parallel from multiple Python threads. - -.. cpp:struct:: gil_scoped_acquire - - .. cpp:function:: gil_scoped_acquire() - - Acquire the GIL - - .. cpp:function:: ~gil_scoped_acquire() - - Release the GIL - -.. cpp:struct:: gil_scoped_release - - .. cpp:function:: gil_scoped_release() - - Release the GIL (**must** be currently held) - - In :ref:`free-threaded extensions `, this operation also - temporarily releases all :ref:`argument locks ` held by - the current thread. - - .. cpp:function:: ~gil_scoped_release() - - Reacquire the GIL - -Free-threading --------------- - -Nanobind provides abstractions to implement *additional* locking that is -needed to ensure the correctness of free-threaded Python extensions. - -.. cpp:struct:: ft_mutex - - Object-oriented wrapper representing a `PyMutex - `__. It can be - slightly more efficient than OS/language-provided primitives (e.g., - ``std::thread``, ``pthread_mutex_t``) and should generally be preferred when - adding critical sections to Python bindings. - - In Python builds *without* free-threading, this class does nothing. It has - no attributes and the :cpp:func:`lock` and :cpp:func:`unlock` functions - return immediately. - - .. cpp:function:: ft_mutex() - - Create a new (unlocked) mutex. - - .. cpp:function:: void lock() - - Acquire the mutex. - - .. cpp:function:: void unlock() - - Release the mutex. - -.. cpp:struct:: ft_lock_guard - - This class provides a RAII lock guard analogous to ``std::lock_guard`` and - ``std::unique_lock``. - - .. cpp:function:: ft_lock_guard(ft_mutex &mutex) - - Call :cpp:func:`mutex.lock() ` (no-op in non-free-threaded builds). - - .. cpp:function:: ~ft_lock_guard() - - Call :cpp:func:`mutex.unlock() ` (no-op in non-free-threaded builds). - -.. cpp:struct:: ft_object_guard - - This class provides a RAII guard that locks a single Python object within a - local scope (in contrast to :cpp:class:`ft_lock_guard`, which locks a - mutex). - - It is a thin wrapper around the Python `critical section API - `__. - Please refer to the Python documentation for details on the semantics of - this relaxed form of critical section (in particular, Python critical sections - may release previously held locks). - - In Python builds *without* free-threading, this class does nothing---the - constructor and destructor return immediately. - - .. cpp:function:: ft_object_guard(handle h) - - Lock the object ``h`` (no-op in non-free-threaded builds) - - .. cpp:function:: ~ft_object_guard() - - Unlock the object ``h`` (no-op in non-free-threaded builds) - -.. cpp:struct:: ft_object2_guard - - This class provides a RAII guard that locks *two* Python object within a - local scope (in contrast to :cpp:class:`ft_lock_guard`, which locks a - mutex). - - It is a thin wrapper around the Python `critical section API - `__. - Please refer to the Python documentation for details on the semantics of - this relaxed form of critical section (in particular, Python critical sections - may release previously held locks). - - In Python builds *without* free-threading, this class does nothing---the - constructor and destructor return immediately. - - .. cpp:function:: ft_object2_guard(handle h1, handle h2) - - Lock the objects ``h1`` and ``h2`` (no-op in non-free-threaded builds) - - .. cpp:function:: ~ft_object2_guard() - - Unlock the objects ``h1`` and ``h2`` (no-op in non-free-threaded builds) - -Low-level type and instance access ----------------------------------- - -nanobind exposes a low-level interface to provide fine-grained control over -the sequence of steps that instantiates a Python object wrapping a C++ -instance. A thorough explanation of these features is provided in a -:ref:`separate section `. - -Type objects -^^^^^^^^^^^^ - -.. cpp:function:: bool type_check(handle h) - - Returns ``true`` if ``h`` is a type that was previously bound via - :cpp:class:`class_`. - -.. cpp:function:: size_t type_size(handle h) - - Assuming that `h` represents a bound type (see :cpp:func:`type_check`), - return its size in bytes. - -.. cpp:function:: size_t type_align(handle h) - - Assuming that `h` represents a bound type (see :cpp:func:`type_check`), - return its alignment in bytes. - -.. cpp:function:: const std::type_info& type_info(handle h) - - Assuming that `h` represents a bound type (see :cpp:func:`type_check`), - return its C++ RTTI record. - -.. cpp:function:: template T &type_supplement(handle h) - - Return a reference to supplemental data stashed in a type object. - The type ``T`` must exactly match the type specified in the - :cpp:class:`nb::supplement\ ` annotation used when - creating the type; no type check is performed, and invalid supplement - accesses may crash the interpreter. Also refer to - :cpp:class:`nb::supplement\ `. - -.. cpp:function:: str type_name(handle h) - - Return the full (module-qualified) name of a type object as a Python string. - -.. cpp:function:: void * type_get_slot(handle h, int slot_id) - - On Python 3.10+, this function is a simple wrapper around the Python C API - function ``PyType_GetSlot`` that provides stable API-compatible access to - type object members. On Python 3.9 and earlier, the official function did - not work on non-heap types. The nanobind version consistently works on heap - and non-heap types across Python versions. - -Instances -^^^^^^^^^ - -The documentation below refers to two per-instance flags with the following meaning: - -- *ready*: is the instance fully constructed? nanobind will not permit passing - the instance to a bound C++ function when this flag is unset. - -- *destruct*: should nanobind call the C++ destructor when the instance is - garbage-collected? - -.. cpp:function:: bool inst_check(handle h) - - Returns ``true`` if `h` represents an instance of a type that was - previously bound via :cpp:class:`class_`. - -.. cpp:function:: template T * inst_ptr(handle h) - - Assuming that `h` represents an instance of a type that was previously bound - via :cpp:class:`class_`, return a pointer to the underlying C++ instance. - - The function *does not check* that `h` actually contains an instance with - C++ type `T`. - -.. cpp:function:: object inst_alloc(handle h) - - Assuming that `h` represents a type object that was previously created via - :cpp:class:`class_` (see :cpp:func:`type_check`), allocate an unitialized - object of type `h` and return it. The *ready* and *destruct* flags of the - returned instance are both set to ``false``. - -.. cpp:function:: object inst_alloc_zero(handle h) - - Assuming that `h` represents a type object that was previously created via - :cpp:class:`class_` (see :cpp:func:`type_check`), allocate a zero-initialized - object of type `h` and return it. The *ready* and *destruct* flags of the - returned instance are both set to ``true``. - - This operation is equivalent to calling :cpp:func:`inst_alloc` followed by - :cpp:func:`inst_zero`. - -.. cpp:function:: object inst_reference(handle h, void * p, handle parent = handle()) - - Assuming that `h` represents a type object that was previously created via - :cpp:class:`class_` (see :cpp:func:`type_check`) create an object of type - `h` that wraps an existing C++ instance `p`. - - The *ready* and *destruct* flags of the returned instance are respectively - set to ``true`` and ``false``. - - This is analogous to casting a C++ object with return value policy - :cpp:enumerator:`rv_policy::reference`. - - If a `parent` object is specified, the instance keeps this parent alive - while the newly created object exists. This is analogous to casting a C++ - object with return value policy - :cpp:enumerator:`rv_policy::reference_internal`. - -.. cpp:function:: object inst_take_ownership(handle h, void * p) - - Assuming that `h` represents a type object that was previously created via - :cpp:class:`class_` (see :cpp:func:`type_check`) create an object of type - `h` that wraps an existing C++ instance `p`. - - The *ready* and *destruct* flags of the returned instance are both set to - ``true``. - - This is analogous to casting a C++ object with return value policy - :cpp:enumerator:`rv_policy::take_ownership`. - -.. cpp:function:: void inst_zero(handle h) - - Zero-initialize the contents of `h`. Sets the *ready* and *destruct* flags - to ``true``. - -.. cpp:function:: bool inst_ready(handle h) - - Query the *ready* flag of the instance `h`. - -.. cpp:function:: std::pair inst_state(handle h) - - Separately query the *ready* and *destruct* flags of the instance `h`. - -.. cpp:function:: void inst_mark_ready(handle h) - - Simultaneously set the *ready* and *destruct* flags of the instance `h` to ``true``. - -.. cpp:function:: void inst_set_state(handle h, bool ready, bool destruct) - - Separately set the *ready* and *destruct* flags of the instance `h`. - -.. cpp:function:: void inst_destruct(handle h) - - Destruct the instance `h`. This entails calling the C++ destructor if the - *destruct* flag is set and then setting the *ready* and *destruct* fields to - ``false``. - -.. cpp:function:: void inst_copy(handle dst, handle src) - - Copy-construct the contents of `src` into `dst` and set the *ready* and - *destruct* flags of `dst` to ``true``. - - `dst` should be an uninitialized instance of the same type. Note that - setting the *destruct* flag may be problematic if `dst` is an offset into an - existing object created using :cpp:func:`inst_reference` (the destructor - will be called multiple times in this case). If so, you must use - :cpp:func:`inst_set_state` to disable the flag following the call to - :cpp:func:`inst_copy`. - - *New in nanobind v2.0.0*: The function is a no-op when ``src`` and ``dst`` - refer to the same object. - -.. cpp:function:: void inst_move(handle dst, handle src) - - Analogous to :cpp:func:`inst_copy`, except that the move constructor - is used instead of the copy constructor. - -.. cpp:function:: void inst_replace_copy(handle dst, handle src) - - Destruct the contents of `dst` (even if the *destruct* flag is ``false``). - Next, copy-construct the contents of `src` into `dst` and set the *ready* - flag of ``dst``. The value of the *destruct* flag is subsequently set to its - value prior to the call. - - This operation is useful to replace the contents of one instance with that - of another regardless of whether `dst` has been created using - :cpp:func:`inst_alloc`, :cpp:func:`inst_reference`, or - :cpp:func:`inst_take_ownership`. - - *New in nanobind v2.0.0*: The function is a no-op when ``src`` and ``dst`` - refer to the same object. - -.. cpp:function:: void inst_replace_move(handle dst, handle src) - - Analogous to :cpp:func:`inst_replace_copy`, except that the move constructor - is used instead of the copy constructor. - -.. cpp:function:: str inst_name(handle h) - - Return the full (module-qualified) name of the instance's type object as a - Python string. - -Global flags ------------- - -.. cpp:function:: bool leak_warnings() noexcept - - Returns whether nanobind warns if any nanobind instances, types, or - functions are still alive when the Python interpreter shuts down. - -.. cpp:function:: bool implicit_cast_warnings() noexcept - - Returns whether nanobind warns if an implicit conversion was not successful. - -.. cpp:function:: void set_leak_warnings(bool value) noexcept - - By default, nanobind loudly complains when any nanobind instances, types, or - functions are still alive when the Python interpreter shuts down. Call this - function to disable or re-enable leak warnings. - -.. cpp:function:: void set_implicit_cast_warnings(bool value) noexcept - - By default, nanobind loudly complains when it attempts to perform an - implicit conversion, and when that conversion is not successful. Call this - function to disable or re-enable the warnings. - -.. cpp:function:: inline bool is_alive() noexcept - - The function returns ``true`` when nanobind is initialized and ready for - use. It returns ``false`` when the Python interpreter has shut down, causing - the destruction various nanobind-internal data structures. Having access to - this liveness status can be useful to avoid operations that are illegal in - the latter context. - -Miscellaneous -------------- - -.. cpp:function:: str repr(handle h) - - Return a stringified version of the provided Python object. - Equivalent to ``repr(h)`` in Python. - -.. cpp:function:: void print(handle value, handle end = handle(), handle file = handle()) - - Invoke the Python ``print()`` function to print the object `value`. If desired, - a line ending `end` and file handle `file` can be specified. - -.. cpp:function:: void print(const char * str, handle end = handle(), handle file = handle()) - - Invoke the Python ``print()`` function to print the null-terminated C-style - string `str` that is encoded using UTF-8 encoding. If desired, a line - ending `end` and file handle `file` can be specified. - -.. cpp:function:: iterator iter(handle h) - - Equivalent to ``iter(h)`` in Python. - -.. cpp:function:: object none() - - Return an object representing the value ``None``. - -.. cpp:function:: dict builtins() - - Return the ``__builtins__`` dictionary. - -.. cpp:function:: dict globals() - - Return the ``globals()`` dictionary. - -.. cpp:function:: Py_hash_t hash(handle h) - - Hash the given argument like ``hash()`` in pure Python. The type of the - return value (``Py_hash_t``) is an implementation-specific signed integer - type. - -.. cpp:function:: template void implicitly_convertible() - - Indicate that the type `Source` is implicitly convertible into `Target` - (which must refer to a type that was previously bound via - :cpp:class:`class_`). - - *Note*: the :cpp:struct:`init_implicit` interface generates more compact - code and should be preferred, i.e., use - - .. code-block:: cpp - - nb::class_(m, "Target") - .def(nb::init_implicit()); - - instead of - - .. code-block:: cpp - - nb::class_(m, "Target") - .def(nb::init()); - - nb::implicitly_convertible(); - - The function is provided for reasons of compatibility with pybind11, and as - an escape hatch to enable use cases where :cpp:struct:`init_implicit` - is not available (e.g., for custom binding-specific constructors that don't - exist in `Target` type). - -.. cpp:class:: template typed - - This helper class provides an interface to parameterize generic types to - improve generated Python function signatures (e.g., to turn ``list`` into - ``list[MyType]``). - - Consider the following binding that iterates over a Python list. - - .. code-block:: cpp - - m.def("f", [](nb::list l) { - for (handle h : l) { - // ... - } - }); - - Suppose that ``f`` expects a list of ``MyType`` objects, which is not clear - from the signature. To make this explicit, use the ``nb::typed`` - wrapper to pass additional type parameters. This has no effect besides - clarifying the signature---in particular, nanobind does *not* insert - additional runtime checks! At runtime, a ``nb::typed`` behaves - exactly like a ``T``. - - .. code-block:: cpp - - m.def("f", [](nb::typed l) { - for (nb::handle h : l) { - // ... - } - }); - - ``nb::typed`` and ``nb::typed`` are - treated specially: they generate a signature that refers just to ``T``, - rather than to the nonsensical ``object[T]`` that would otherwise - be produced. This can be useful if you want to replace the type of - a parameter instead of augmenting it. Note that at runtime these - perform no checks at all, since ``nb::object`` and ``nb::handle`` - can refer to any Python object. - - To support callable types, you can specify a C++ function signature in - ``nb::typed`` and nanobind will attempt to convert - it to a Python callable signature. - ``nb::typed`` becomes - ``Callable[[float, str], int]``, while - ``nb::typed`` becomes ``Callable[..., int]``. - Type checkers will verify that any callable passed for such an argument - has a compatible signature. (At runtime, any sort of callable object - will be accepted.) diff --git a/src/nanobind/docs/api_extra.rst b/src/nanobind/docs/api_extra.rst deleted file mode 100644 index 903aa58..0000000 --- a/src/nanobind/docs/api_extra.rst +++ /dev/null @@ -1,1562 +0,0 @@ -C++ API Reference (Extras) -========================== - -.. cpp:namespace:: nanobind - -Operator overloading --------------------- - -The following optional include directive imports the special value :cpp:var:`self`. - -.. code-block:: cpp - - #include - -The underlying type exposes various C++ operators that enable a shorthand -notation to bind operators to python. See the :ref:`operator overloading -` example in the main documentation for details. - - -.. cpp:class:: detail::self_t - - This is an internal class that should be accessed through the singleton - :cpp:var:`self` value. - - It supports the overloaded operators listed below. Depending on whether - :cpp:var:`self` is the left or right argument of a binary operation, - the binding will map to different Python methods as shown below. - - .. list-table:: - :header-rows: 1 - :widths: 50 50 - - * - C++ operator - - Python method (left or right) - * - ``operator-`` - - ``__sub__``, ``__rsub__`` - * - ``operator+`` - - ``__add__``, ``__radd__`` - * - ``operator*`` - - ``__mul__``, ``__rmul__`` - * - ``operator/`` - - ``__truediv__``, ``__rtruediv__`` - * - ``operator%`` - - ``__mod__``, ``__rmod__`` - * - ``operator<<`` - - ``__lshift__``, ``__rlshift__`` - * - ``operator>>`` - - ``__rshift__``, ``__rrshift__`` - * - ``operator&`` - - ``__and__``, ``__rand__`` - * - ``operator^`` - - ``__xor__``, ``__rxor__`` - * - ``operator|`` - - ``__or__``, ``__ror__`` - * - ``operator>`` - - ``__gt__``, ``__lt__`` - * - ``operator>=`` - - ``__ge__``, ``__le__`` - * - ``operator<`` - - ``__lt__``, ``__gt__`` - * - ``operator<=`` - - ``__le__``, ``__ge__`` - * - ``operator==`` - - ``__eq__`` - * - ``operator!=`` - - ``__ne__`` - * - ``operator+=`` - - ``__iadd__`` - * - ``operator-=`` - - ``__isub__`` - * - ``operator*=`` - - ``__mul__`` - * - ``operator/=`` - - ``__itruediv__`` - * - ``operator%=`` - - ``__imod__`` - * - ``operator<<=`` - - ``__ilrshift__`` - * - ``operator>>=`` - - ``__ilrshift__`` - * - ``operator&=`` - - ``__iand__`` - * - ``operator^=`` - - ``__ixor__`` - * - ``operator|=`` - - ``__ior__`` - * - ``operator-`` (unary) - - ``__neg__`` - * - ``operator+`` (unary) - - ``__pos__`` - * - ``operator~`` (unary) - - ``__invert__`` - * - ``operator!`` (unary) - - ``__bool__`` (with extra negation) - * - ``nb::abs(..)`` - - ``__abs__`` - * - ``nb::hash(..)`` - - ``__hash__`` - -.. cpp:var:: detail::self_t self - -Trampolines ------------ - -The following macros to implement trampolines that forward virtual function -calls to Python require an additional include directive: - -.. code-block:: cpp - - #include - -See the section on :ref:`trampolines ` for further detail. - -.. c:macro:: NB_TRAMPOLINE(base, size) - - Install a trampoline in an alias class to enable dispatching C++ virtual - function calls to a Python implementation. Refer to the documentation on - :ref:`trampolines ` to see how this macro can be used. - -.. c:macro:: NB_OVERRIDE(func, ...) - - Dispatch the call to a Python method named ``"func"`` if it is overloaded on - the Python side, and forward the function arguments specified in the - variable length argument ``...``. Otherwise, call the C++ implementation - `func` in the base class. - - Refer to the documentation on :ref:`trampolines ` to see how - this macro can be used. - -.. c:macro:: NB_OVERRIDE_PURE(func, ...) - - Dispatch the call to a Python method named ``"func"`` if it is overloaded on - the Python side, and forward the function arguments specified in the - variable length argument ``...``. Otherwise, raise an exception. This macro - should be used when the C++ function is pure virtual. - - Refer to the documentation on :ref:`trampolines ` to see how - this macro can be used. - -.. c:macro:: NB_OVERRIDE_NAME(name, func, ...) - - Dispatch the call to a Python method named ``name`` if it is overloaded on - the Python side, and forward the function arguments specified in the - variable length argument ``...``. Otherwise, call the C++ function `func` in - the base class. - - This function differs from :c:macro:`NB_OVERRIDE() ` in that - C++ and Python functions can be named differently (e.g., ``operator+`` and - ``__add__``). Refer to the documentation on :ref:`trampolines ` - to see how this macro can be used. - -.. c:macro:: NB_OVERRIDE_PURE_NAME(name, func, ...) - - Dispatch the call to a Python method named ``name`` if it is overloaded on - the Python side, and forward the function arguments specified in the - variable length argument ``...``. Otherwise, raise an exception. This macro - should be used when the C++ function is pure virtual. - - This function differs from :c:macro:`NB_OVERRIDE_PURE() ` - in that C++ and Python functions can be named differently (e.g., - ``operator+`` and ``__add__``). Although the C++ base implementation cannot - be called, its name is still important since nanobind uses it to infer the - return value type. Refer to the documentation on :ref:`trampolines - ` to see how this macro can be used. - -.. _vector_bindings: - -STL vector bindings -------------------- - -The following function can be used to expose ``std::vector<...>`` variants -in Python. It is not part of the core nanobind API and requires an additional -include directive: - -.. code-block:: cpp - - #include - -.. cpp:function:: template class_ bind_vector(handle scope, const char * name, Args &&...args) - - Bind the STL vector-derived type `Vector` to the identifier `name` and - place it in `scope` (e.g., a :cpp:class:`module_`). The variable argument - list can be used to pass a docstring and other :ref:`class binding - annotations `. - - The type includes the following methods resembling ``list``: - - .. list-table:: - :header-rows: 1 - :widths: 50 50 - - * - Signature - - Documentation - * - ``__init__(self)`` - - Default constructor - * - ``__init__(self, arg: Vector)`` - - Copy constructor - * - ``__init__(self, arg: typing.Sequence)`` - - Construct from another sequence type - * - ``__len__(self) -> int`` - - Return the number of elements - * - ``__repr__(self) -> str`` - - Generate a string representation - * - ``__contains__(self, arg: Value)`` - - Check if the vector contains ``arg`` - * - ``__eq__(self, arg: Vector)`` - - Check if the vector is equal to ``arg`` - * - ``__ne__(self, arg: Vector)`` - - Check if the vector is not equal to ``arg`` - * - ``__bool__(self) -> bool`` - - Check whether the vector is empty - * - ``__iter__(self) -> iterator`` - - Instantiate an iterator to traverse the elements - * - ``__getitem__(self, arg: int) -> Value`` - - Return an element from the list (supports negative indexing) - * - ``__setitem__(self, arg0: int, arg1: Value)`` - - Assign an element in the list (supports negative indexing) - * - ``__delitem__(self, arg: int)`` - - Delete an item from the list (supports negative indexing) - * - ``__getitem__(self, arg: slice) -> Vector`` - - Slice-based getter - * - ``__setitem__(self, arg0: slice, arg1: Value)`` - - Slice-based assignment - * - ``__delitem__(self, arg: slice)`` - - Slice-based deletion - * - ``clear(self)`` - - Remove all items from the list - * - ``append(self, arg: Value)`` - - Append a list item - * - ``insert(self, arg0: int, arg1: Value)`` - - Insert a list item (supports negative indexing) - * - ``pop(self, index: int = -1)`` - - Pop an element at position ``index`` (the end by default) - * - ``extend(self, arg: Vector)`` - - Extend ``self`` by appending elements from ``arg``. - * - ``count(self, arg: Value)`` - - Count the number of times that ``arg`` is contained in the vector - * - ``remove(self, arg: Value)`` - - Remove all occurrences of ``arg``. - - In contrast to ``std::vector<...>``, all bound functions perform range - checks to avoid undefined behavior. When the type underlying the vector is - not comparable or copy-assignable, some of these functions will not be - generated. - - The binding operation is a no-op if the vector type has already been - registered with nanobind. - - .. warning:: - - While this function creates a type resembling a Python ``list``, it has a - major caveat: the item accessor ``__getitem__`` copies the accessed - element by default (the bottom of this paragraph explains how this copy - can be avoided). - - Consequently, writes to elements may not propagate in the expected way. - Consider the following C++ bindings: - - .. code-block:: cpp - - struct A { - int value; - }; - - nb::class_(m, "A") - .def(nb::init()) - .def_rw("value", &A::value); - - nb::bind_vector>(m, "VecA"); - - On the Python end, they yield the following surprising behavior: - - .. code-block:: python - - from my_ext import A, VecA - - va = VecA() - va.append(A(123)) - va[0].value = 456 - assert va[0].value == 456 # <-- assertion fails! - - To actually modify ``va``, another write is needed. - - .. code-block:: python - - v = va[0] - v.value = 456 - va[0] = v - - This may seem like a strange design, so it is worth explaining why the - implementation works in this way. - - The key issue is that any particular value (e.g., ``va[0]``) lies within - a memory buffer managed by the ``std::vector``. It is not safe for - nanobind to refer to objects within this buffer using their absolute or - relative memory address. For example, inserting an element at position 0 - will rearrange the buffer's contents and shift all subsequent ``A`` - instances. If nanobind ``A`` objects could be "views" into the - ``std::vector``, then an insertion would cause the contents of unrelated - ``A`` Python objects to change unexpectedly. Insertion may also require - reallocation of the buffer, invalidating all current addresses, and this - could lead to undefined behavior (use-after-free) if nanobind did not - make a copy. - - There are three situations in which the surprising behavior is avoided: - - 1. If the modification of the array is performed using in-place - operations like - - .. code-block:: python - - v[i] += 5 - - In-place operators automatically perform an array assignment, causing - the issue to disappear. This means that if you work with a vector type - like ``std::vector`` or ``std::vector`` with an - immutable element type like ``int`` or ``str`` on the Python end, it - will behave completely naturally in Python. - - 2. If the array contains STL shared pointers (e.g., - ``std::vector>``), the added - indirection and ownership tracking removes the need for extra copies. - - 3. If the array contains pointers to reference-counted objects (e.g., - ``std::vector>`` via the :cpp:class:`ref` wrapper) and ``T`` - uses the intrusive reference counting approach explained :ref:`here - `, the added indirection and ownership tracking removes the - need for extra copies. - - (It is usually unsafe to use this class to bind pointer-valued - vectors ``std::vector`` when ``T`` does not use intrusive - reference counting, because then there is nothing to prevent the Python - objects returned by ``__getitem__`` from outliving the C++ ``T`` - objects that they point to. But if you are able to guarantee through - other means that the ``T`` objects will live long enough, the intrusive - reference counting is not strictly required.) - - .. note:: - - Previous versions of nanobind (before 2.0) and pybind11 return Python - objects from ``__getitem__`` that wrap *references* (i.e., views), - meaning that they are only safe to use until the next insertion or - deletion in the vector they were drawn from. As discussed above, any use - after that point could **corrupt memory or crash your program**, which is - why reference semantics are no longer the default. - - If you truly need the unsafe reference semantics, and if you - can guarantee that all use of your bindings will respect - the memory layout and reference-invalidation rules of the - underlying C++ container type, you can request the old behavior - by passing a second template argument of - :cpp:enumerator:`rv_policy::reference_internal` to - :cpp:func:`bind_vector`. This will override nanobind's usual - choice of :cpp:enumerator:`rv_policy::copy` for ``__getitem__``. - - .. code-block:: cpp - - nb::bind_vector, - nb::rv_policy::reference_internal>(m, "ExampleVec"); - - Again, please avoid this if at all possible. - It is *very* easy to cause problems if you're not careful, as the - following example demonstrates. - - .. code-block:: python - - def looks_fine_but_crashes(vec: ext.ExampleVec) -> None: - # Trying to remove all the elements too much older than the last: - last = vec[-1] - # Even being careful to iterate backwards so we visit each - # index only once... - for idx in range(len(vec) - 2, -1, -1): - if last.timestamp - vec[idx].timestamp > 5: - del vec[idx] - # Oops! After the first deletion, 'last' now refers to - # uninitialized memory. - - -.. _map_bindings: - -STL map bindings ----------------- - -The following function can be used to expose ``std::map<...>`` or -``std::unordered_map<...>`` variants in Python. It is not part of the core -nanobind API and requires an additional include directive: - -.. code-block:: cpp - - #include - -.. cpp:function:: template class_ bind_map(handle scope, const char * name, Args &&...args) - - Bind the STL map-derived type `Map` (ordered or unordered) to the identifier - `name` and place it in `scope` (e.g., a :cpp:class:`module_`). The variable - argument list can be used to pass a docstring and other :ref:`class binding - annotations `. - - The type includes the following methods resembling ``dict``: - - .. list-table:: - :header-rows: 1 - :widths: 50 50 - - * - Signature - - Documentation - * - ``__init__(self)`` - - Default constructor - * - ``__init__(self, arg: Map)`` - - Copy constructor - * - ``__init__(self, arg: dict)`` - - Construct from a Python dictionary - * - ``__len__(self) -> int`` - - Return the number of elements - * - ``__repr__(self) -> str`` - - Generate a string representation - * - ``__contains__(self, arg: Key)`` - - Check if the map contains ``arg`` - * - ``__eq__(self, arg: Map)`` - - Check if the map is equal to ``arg`` - * - ``__ne__(self, arg: Map)`` - - Check if the map is not equal to ``arg`` - * - ``__bool__(self) -> bool`` - - Check whether the map is empty - * - ``__iter__(self) -> iterator`` - - Instantiate an iterator to traverse the set of map keys - * - ``__getitem__(self, arg: Key) -> Value`` - - Return an element from the map - * - ``__setitem__(self, arg0: Key, arg1: Value)`` - - Assign an element in the map - * - ``__delitem__(self, arg: Key)`` - - Delete an item from the map - * - ``clear(self)`` - - Remove all items from the list - * - ``update(self, arg: Map)`` - - Update the map with elements from ``arg``. - * - ``keys(self, arg: Map) -> Map.KeyView`` - - Returns an iterable view of the map's keys - * - ``values(self, arg: Map) -> Map.ValueView`` - - Returns an iterable view of the map's values - * - ``items(self, arg: Map) -> Map.ItemView`` - - Returns an iterable view of the map's items - - The binding operation is a no-op if the map type has already been - registered with nanobind. - - The binding routine ideally expects the involved types to be: - - - copy-constructible - - copy-assignable - - equality-comparable - - If not all of these properties are available, then a subset of the above - methods will be omitted. Please refer to ``bind_map.h`` for details on the - logic. - - .. warning:: - - While this function creates a type resembling a Python ``dict``, it has a - major caveat: the item accessor ``__getitem__`` copies the accessed - element by default. - - Please refer to the :ref:`STL vector bindings ` for a - discussion of the problem and possible solutions. Everything applies - equally to the map case. - - .. note:: - - Unlike ``std::vector``, the ``std::map`` and ``std::unordered_map`` - containers are *node-based*, meaning their elements do have a - consistent address for as long as they're stored in the map. - (Note that this is generally *not* true of third-party containers - with similar interfaces, such as ``absl::flat_hash_map``.) - - If you are binding a node-based container type, and you want - ``__getitem__`` to return a reference to the accessed element - rather than copying it, it is *somewhat* safer than it would - be with :cpp:func:`bind_vector` to use the unsafe workaround - discussed there: - - .. code-block:: cpp - - nb::bind_map, - nb::rv_policy::reference_internal>(m, "ExampleMap"); - - With a node-based container, the only situation where a reference - returned from ``__getitem__`` would be invalidated is if the individual - element that it refers to were removed from the map. Unlike with - ``std::vector``, additions and removals of *other* elements would - not present a danger. - - It is still easy to cause problems if you're not careful, though: - - .. code-block:: python - - def unsafe_pop(map: ext.ExampleMap, key: str) -> ext.SomeValue: - value = map[key] - del map[key] - # Oops! `value` now points to a dangling element. Anything you - # do with it now is liable to crash the interpreter. - return value # uh-oh... - - -Unique pointer deleter ----------------------- - -The following *deleter* should be used to gain maximal flexibility in combination with -``std::unique_ptr<..>``. It requires the following additional include directive: - -.. code-block:: cpp - - #include - -See the two documentation sections on unique pointers for further detail -(:ref:`#1 `, :ref:`#2 `). - -.. cpp:struct:: template deleter - - .. cpp:function:: deleter() = default - - Create a deleter that destroys the object using a ``delete`` expression. - - .. cpp:function:: deleter(handle h) - - Create a deleter that destroys the object by reducing the Python reference count. - - .. cpp:function:: bool owned_by_python() const - - Check if the object is owned by Python. - - .. cpp:function:: bool owned_by_cpp() const - - Check if the object is owned by C++. - - .. cpp:function:: void operator()(void * p) noexcept - - Destroy the object at address `p`. - -.. _iterator_bindings: - -Iterator bindings ------------------ - -The following functions can be used to expose existing C++ iterators in -Python. They are not part of the core nanobind API and require an additional -include directive: - -.. code-block:: cpp - - #include - -.. cpp:function:: template auto make_iterator(handle scope, const char * name, Iterator first, Sentinel last, Extra &&...extra) - - Create a Python iterator wrapping the C++ iterator represented by the range - ``[first, last)``. The `Extra` parameter can be used to pass additional - function binding annotations. - - This function lazily creates a new Python iterator type identified by - `name`, which is stored in the given `scope`. Usually, some kind of - :cpp:class:`keep_alive` annotation is needed to tie the lifetime of the - parent container to that of the iterator. - - The return value is a typed iterator (:cpp:class:`iterator` wrapped using - :cpp:class:`typed`), whose template parameter is given by the type of - ``*first``. - - Here is an example of what this might look like for a STL vector: - - .. code-block:: cpp - - using IntVec = std::vector; - - nb::class_(m, "IntVec") - .def("__iter__", - [](const IntVec &v) { - return nb::make_iterator(nb::type(), "iterator", - v.begin(), v.end()); - }, nb::keep_alive<0, 1>()); - - .. note:: - - Pre-2.0 versions of nanobind and pybind11 return *references* (views) - into the underlying sequence. - - This is convenient when - - 1. Iterated elements are used to modify the underlying container. - - 2. Iterated elements should reflect separately made changes to - the underlying container. - - But this strategy is *unsafe* if the allocated memory region or layout - of the container could change (e.g., through insertion of removal of - elements). - - Because of this, iterators now copy by default. There are two - ways to still obtain references to the target elements: - - 1. If the iterator is over STL shared pointers, the added indirection and - ownership tracking removes the need for extra copies. - - 2. If the iterator is over reference-counted objects (e.g., ``ref`` - via the :cpp:class:`ref` wrapper) and ``T`` uses the intrusive - reference counting approach explained :ref:`here `, - the added indirection and ownership tracking removes the need - for extra copies. - - If you truly need the unsafe reference semantics, and if you can - guarantee that all use of your bindings will respect the memory layout - and reference-invalidation rules of the underlying C++ container type, - you can request the old behavior by passing - :cpp:enumerator:`rv_policy::reference_internal` to the ``Policy`` - template argument of this function. - - -.. cpp:function:: template auto make_iterator(handle scope, const char * name, Type &value, Extra &&...extra) - - This convenience wrapper calls the above :cpp:func:`make_iterator` variant with - ``first`` and ``last`` set to ``std::begin(value)`` and ``std::end(value)``, - respectively. - -.. cpp:function:: template iterator make_key_iterator(handle scope, const char * name, Iterator first, Sentinel last, Extra &&...extra) - - :cpp:func:`make_iterator` specialization for C++ iterators that return - key-value pairs. `make_key_iterator` returns the first pair element to - iterate over keys. - - The return value is a typed iterator (:cpp:class:`iterator` wrapped using - :cpp:class:`typed`), whose template parameter is given by the type of - ``(*first).first``. - - -.. cpp:function:: template iterator make_value_iterator(handle scope, const char * name, Iterator first, Sentinel last, Extra &&...extra) - - :cpp:func:`make_iterator` specialization for C++ iterators that return - key-value pairs. `make_value_iterator` returns the second pair element to - iterate over values. - - The return value is a typed iterator (:cpp:class:`iterator` wrapped using - :cpp:class:`typed`), whose template parameter is given by the type of - ``(*first).second``. - -N-dimensional array type ------------------------- - -The following type can be used to exchange n-dimension arrays with frameworks -like NumPy, PyTorch, Tensorflow, JAX, CuPy, and others. It requires an -additional include directive: - -.. code-block:: cpp - - #include - -Detailed documentation including example code is provided in a :ref:`separate -section `. - -.. cpp:function:: bool ndarray_check(handle h) noexcept - - Test whether the Python object represents an ndarray. - - Objects with a ``__dlpack__`` attribute or objects that implement the buffer - protocol are considered as ndarray objects. In addition, arrays from NumPy, - PyTorch, TensorFlow and XLA are also regarded as ndarrays. - -.. cpp:class:: template ndarray - - .. cpp:type:: Scalar - - The scalar type underlying the array (or ``void`` if not specified) - - .. cpp:var:: static constexpr bool ReadOnly - - A ``constexpr`` Boolean value that is ``true`` if the ndarray template - arguments (`Args... `) include the ``nb::ro`` annotation or a - ``const``-qualified scalar type. - - .. cpp:var:: static constexpr char Order - - A ``constexpr`` character value set based on the ndarray template - arguments (`Args... `). It equals - - - ``'C'`` if :cpp:class:`c_contig` is specified, - - ``'F'`` if :cpp:class:`f_contig` is specified, - - ``'A'`` if :cpp:class:`any_contig` is specified, - - ``'\0'`` otherwise. - - .. cpp:var:: static constexpr int DeviceType - - A ``constexpr`` integer value set to the device type ID extracted from - the ndarray template arguments (`Args... `), or - :cpp:struct:`device::none::value ` when none was specified. - - .. cpp:type:: VoidPtr = std::conditional_t - - A potentially ``const``-qualified ``void*`` pointer type used by some - of the ``ndarray`` constructors. - - .. cpp:function:: ndarray() = default - - Create an invalid array. - - .. cpp:function:: template explicit ndarray(const ndarray &other) - - Reinterpreting constructor that wraps an existing nd-array (parameterized - by `Args... `) into a new ndarray (parameterized by `Args2... - `). No copy or conversion is made. - - Dropping parameters is always safe. For example, a function that - returns different array types could call it to convert ``ndarray`` to - ``ndarray<>``. When adding constraints, the constructor is only safe to - use following a runtime check to ensure that newly created array actually - possesses the advertised properties. - - .. cpp:function:: ndarray(const ndarray &) - - Copy constructor. Increases the reference count of the referenced array. - - .. cpp:function:: ndarray(ndarray &&) - - Move constructor. Steals the referenced array without changing reference counts. - - .. cpp:function:: ~ndarray() - - Decreases the reference count of the referenced array and potentially destroy it. - - .. cpp:function:: ndarray& operator=(const ndarray &) - - Copy assignment operator. Increases the reference count of the referenced array. - Decreases the reference count of the previously referenced array and potentially destroy it. - - .. cpp:function:: ndarray& operator=(ndarray &&) - - Move assignment operator. Steals the referenced array without changing reference counts. - Decreases the reference count of the previously referenced array and potentially destroy it. - - .. _ndarray_dynamic_constructor: - - .. cpp:function:: ndarray(VoidPtr data, const std::initializer_list shape = { }, handle owner = { }, std::initializer_list strides = { }, dlpack::dtype dtype = nanobind::dtype(), int32_t device_type = DeviceType, int32_t device_id = 0, char order = Order) - - Create an array wrapping an existing memory allocation. - - Only the `data` parameter is strictly required, while some other - parameters can be be inferred from static :cpp:class:`nb::ndarray\<...\> - ` template parameters. - - The parameters have the following meaning: - - - `data`: a CPU/GPU/.. pointer to the memory region storing the array - data. - - When the array is parameterized by a ``const`` scalar type, or when it - has a :cpp:class:`nb::ro ` read-only annotation, a ``const`` - pointer can be passed here. - - - `shape`: an initializer list that simultaneously specifies the number - of dimensions and the size along each axis. If left at its default - ``{}``, the :cpp:class:`nb::shape ` template parameter - will take precedence (if present). - - - `owner`: if provided, the array will hold a reference to this object - until its destruction. This makes it possible to create zero-copy views - into other data structures, while guaranteeing the memory safety of - array accesses. - - - `strides`: an initializer list explaining the layout of the data in - memory. Each entry denotes the number of elements to jump over to - advance to the next item along the associated axis. - - `strides` must either have the same size as `shape` or be empty. In the - latter case, strides are automatically computed according to the - `order` parameter. - - Note that strides in nanobind express *element counts* rather than - *byte counts*. This convention differs from other frameworks (e.g., - NumPy) and is a consequence of the underlying `DLPack - `_ protocol. - - - `dtype` describes the numeric data type of array elements (e.g., - floating point, signed/unsigned integer) and their bit depth. - - You can use the :cpp:func:`nb::dtype\() ` function to obtain the right - value for a given type. - - - `device_type` and `device_id` specify where the array data is stored. - The `device_type` must be an enumerant like - :cpp:class:`nb::device::cuda::value `, while the meaning - of the device ID is unspecified and platform-dependent. - - Note that the `device_id` is set to ``0`` by default and cannot be - inferred by nanobind. If your extension creates arrays on multiple - different compute accelerators, you *must* provide this parameter. - - - The `order` parameter denotes the coefficient order in memory and is only - relevant when `strides` is empty. Specify ``'C'`` for C-style or ``'F'`` - for Fortran-style. When this parameter is not explicitly specified, the - implementation uses the order specified as an ndarray template - argument, or C-style order as a fallback. - - Both ``strides`` and ``shape`` will be copied by the constructor, hence - the targets of these initializer lists do not need to remain valid - following the constructor call. - - .. warning:: - - The Python *global interpreter lock* (GIL) must be held when calling - this function. - - .. cpp:function:: ndarray(VoidPtr data, size_t ndim, const size_t * shape, handle owner, const int64_t * strides = nullptr, dlpack::dtype dtype = nanobind::dtype(), int device_type = DeviceType, int device_id = 0, char order = Order) - - Alternative form of the above constructor, which accepts the `shape` - and `strides` arguments using pointers instead of initializer lists. - The number of dimensions must be specified via the `ndim` parameter - in this case. - - See the previous constructor for details, the remaining behavior is - identical. - - .. cpp:function:: dlpack::dtype dtype() const - - Return the data type underlying the array - - .. cpp:function:: size_t ndim() const - - Return the number of dimensions. - - .. cpp:function:: size_t size() const - - Return the size of the array (i.e. the product of all dimensions). - - .. cpp:function:: size_t itemsize() const - - Return the size of a single array element in bytes. The returned value - is rounded up to the next full byte in case of bit-level representations - (query :cpp:member:`dtype::bits` for bit-level granularity). - - .. cpp:function:: size_t nbytes() const - - Return the size of the entire array bytes. The returned value is rounded - up to the next full byte in case of bit-level representations. - - .. cpp:function:: size_t shape(size_t i) const - - Return the size of dimension `i`. - - .. cpp:function:: int64_t stride(size_t i) const - - Return the stride (in number of elements) of dimension `i`. - - .. cpp:function:: const int64_t* shape_ptr() const - - Return a pointer to the shape array. Note that the return type is - ``const int64_t*``, which may be unexpected as the scalar version - :cpp:func:`shape()` casts its result to a ``size_t``. - - This is a consequence of the DLPack tensor representation that uses - signed 64-bit integers for all of these fields. - - .. cpp:function:: const int64_t* stride_ptr() const - - Return pointer to the stride array. - - .. cpp:function:: bool is_valid() const - - Check whether the array is in a valid state. - - .. cpp:function:: int device_type() const - - ID denoting the type of device hosting the array. This will match the - ``value`` field of a device class, such as :cpp:class:`device::cpu::value - ` or :cpp:class:`device::cuda::value `. - - .. cpp:function:: int device_id() const - - In a multi-device/GPU setup, this function returns the ID of the device - storing the array. - - .. cpp:function:: Scalar * data() const - - Return a pointer to the array data. - If :cpp:var:`ReadOnly` is true, a pointer-to-const is returned. - - .. cpp:function:: template auto& operator()(Args2... indices) - - Return a reference to the element stored at the provided index/indices. - If :cpp:var:`ReadOnly` is true, a reference-to-const is returned. - Note that ``sizeof...(Args2)`` must match :cpp:func:`ndim()`. - - This accessor is only available when the scalar type and array dimension - were specified as template parameters. - - This function should only be used when the array storage is accessible - through the CPU's virtual memory address space. - - .. cpp:function:: template auto view() - - Returns an nd-array view that is optimized for fast array access on the - CPU. You may optionally specify additional ndarray constraints via the - `Extra` parameter (though a runtime check should first be performed to - ensure that the array possesses these properties). - - The returned view provides the operations ``data()``, ``ndim()``, - ``shape()``, ``stride()``, and ``operator()`` following the conventions - of the `ndarray` type. - - .. cpp:function:: auto cast(rv_policy policy = rv_policy::automatic_reference, handle parent = {}) - - The expression ``array.cast(policy, parent)`` is almost equivalent to - :cpp:func:`nb::cast(array, policy, parent) `. - - The main difference is that the return type of :cpp:func:`nb::cast - ` is :cpp:class:`nb::object `, which renders as a rather - non-descriptive ``object`` in Python bindings. The ``.cast()`` method - returns a custom wrapper type that still derives from - :cpp:class:`nb::object `, but whose type signature in bindings - reproduces that of the original nd-array. - -Data types -^^^^^^^^^^ - -Nanobind uses the `DLPack `_ ABI to represent -metadata describing n-dimensional arrays (even when they are exchanged using -the buffer protocol). Consequently, the set of possible dtypes is :ref:`more -restricted ` than that of other nd-array libraries (e.g., -NumPy). Relevant data structures are located in the ``nanobind::dlpack`` -sub-namespace. - - -.. cpp:enum-class:: dlpack::dtype_code : uint8_t - - This enumeration characterizes the elementary array data type regardless of - bit depth. - - .. cpp:enumerator:: Int = 0 - - Signed integer format - - .. cpp:enumerator:: UInt = 1 - - Unsigned integer format - - .. cpp:enumerator:: Float = 2 - - IEEE-754 floating point format - - .. cpp:enumerator:: Bfloat = 4 - - "Brain" floating point format - - .. cpp:enumerator:: Complex = 5 - - Complex numbers parameterized by real and imaginary component - -.. cpp:struct:: dlpack::dtype - - Represents the data type underlying an n-dimensional array. Use the - :cpp:func:`dtype\() <::nanobind::dtype>` function to return a populated - instance of this data structure given a scalar C++ arithmetic type. - - .. cpp:member:: uint8_t code = 0; - - This field must contain the value of one of the - :cpp:enum:`dlpack::dtype_code` enumerants. - - .. cpp:member:: uint8_t bits = 0; - - Number of bits per entry (e.g., 32 for a C++ single precision ``float``) - - .. cpp:member:: uint16_t lanes = 0; - - Number of SIMD lanes (typically ``1``) - -.. cpp:function:: template dlpack::dtype dtype() - - Returns a populated instance of the :cpp:class:`dlpack::dtype` structure - given a scalar C++ arithmetic type. - -Array annotations -^^^^^^^^^^^^^^^^^ - -The :cpp:class:`ndarray\<..\> ` class admits optional template -parameters. They constrain the type of array arguments that may be passed to a -function. - -The following are supported: - -Data type -+++++++++ - -The data type of the underlying scalar element. The following are supported. - -- ``[u]int8_t`` up to ``[u]int64_t`` and other variations (``unsigned long long``, etc.) -- ``float``, ``double`` -- ``bool`` - -Annotate the data type with ``const`` to indicate a read-only array. Note that -only the buffer protocol/NumPy interface considers ``const``-ness at the -moment; data exchange with other array libraries will ignore this annotation. - -When the is unspecified (e.g., to accept arbitrary input arrays), the -:cpp:class:`ro` annotation can instead be used to denote read-only access: - -.. cpp:class:: ro - - Indicate read-only access (use only when no data type is specified.) - - -nanobind does not support non-standard types as documented in the section on -:ref:`dtype limitations `. - -Shape -+++++ - -.. cpp:class:: template shape - - Require the array to have ``sizeof...(Is)`` dimensions. Each entry of `Is` - specifies a fixed size constraint for that specific dimension. An entry - equal to ``-1`` indicates that *any* size should be accepted for this - dimension. - - (An alias named ``nb::any`` representing ``-1`` was removed in nanobind 2). - -.. cpp:class:: template ndim - - Alternative to the above that only constrains the array dimension. - ``nb::ndim<2>`` is equivalent to ``nb::shape<-1, -1>``. - -Contiguity -++++++++++ - -.. cpp:class:: c_contig - - Request that the array storage uses a C-contiguous representation. - -.. cpp:class:: f_contig - - Request that the array storage uses a F (Fortran)-contiguous representation. - -.. cpp:class:: any_contig - - Accept both C- and F-contiguous arrays. - -If you prefer not to require contiguity, simply do not provide any of the -``*_contig`` template parameters listed above. - -Device type -+++++++++++ - -.. cpp:class:: device - - The following helper classes can be used to constrain the device and - address space of an array. Each class has a ``static constexpr int32_t - value`` field that will then match up with - :cpp:func:`ndarray::device_id()`. - - .. cpp:class:: cpu - - CPU heap memory - - .. cpp:class:: cuda - - NVIDIA CUDA device memory - - .. cpp:class:: cuda_host - - NVIDIA CUDA host-pinned memory - - .. cpp:class:: cuda_managed - - NVIDIA CUDA managed memory - - .. cpp:class:: vulkan - - Vulkan device memory - - .. cpp:class:: metal - - Apple Metal device memory - - .. cpp:class:: rocm - - AMD ROCm device memory - - .. cpp:class:: rocm_host - - AMD ROCm host memory - - .. cpp:class:: oneapi - - Intel OneAPI device memory - -Framework -+++++++++ - -Framework annotations cause :cpp:class:`nb::ndarray ` objects to -convert into an equivalent representation in one of the following frameworks: - -.. cpp:class:: numpy - -.. cpp:class:: tensorflow - -.. cpp:class:: pytorch - -.. cpp:class:: jax - -.. cpp:class:: cupy - -Eigen convenience type aliases ------------------------------- - -The following helper type aliases require an additional include directive: - -.. code-block:: cpp - - #include - -.. cpp:type:: DStride = Eigen::Stride - - This type alias refers to an Eigen stride object that is sufficiently flexible - so that can be easily called with NumPy arrays and array slices. - -.. cpp:type:: template DRef = Eigen::Ref - - This templated type alias creates an ``Eigen::Ref<..>`` with flexible strides for - zero-copy data exchange between Eigen and NumPy. - -.. cpp:type:: template DMap = Eigen::Map - - This templated type alias creates an ``Eigen::Map<..>`` with flexible strides for - zero-copy data exchange between Eigen and NumPy. - -.. _chrono_conversions: - -Timestamp and duration conversions ----------------------------------- - -nanobind supports bidirectional conversions of timestamps and -durations between their standard representations in Python -(:py:class:`datetime.datetime`, :py:class:`datetime.timedelta`) and in C++ -(``std::chrono::time_point``, ``std::chrono::duration``). -A few unidirectional conversions from other Python types to these -C++ types are also provided and explained below. - -These type casters require an additional include directive: - -.. code-block:: cpp - - #include - -.. The rest of this section is adapted from pybind11/docs/advanced/cast/chrono.rst - -An overview of clocks in C++11 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The C++11 standard defines three different clocks, and users can -define their own. Each ``std::chrono::time_point`` is defined relative -to a particular clock. When using the ``chrono`` type caster, you must be -aware that only ``std::chrono::system_clock`` is guaranteed to convert -to a Python :py:class:`~datetime.datetime` object; other clocks may convert to -:py:class:`~datetime.timedelta` if they don't represent calendar time. - -The first clock defined by the standard is ``std::chrono::system_clock``. -This clock measures the current date and time, much like the Python -:py:func:`time.time` function. It can change abruptly due to -administrative actions, daylight savings time transitions, or -synchronization with an external time server. That makes this clock a -poor choice for timing purposes, but a good choice for wall-clock time. - -The second clock defined by the standard is ``std::chrono::steady_clock``. -This clock ticks at a steady rate and is never adjusted, like -:py:func:`time.monotonic` in Python. That makes it excellent for timing -purposes, but the value in this clock does not correspond to the -current date and time. Often this clock will measure the amount of -time your system has been powered on. This clock will never be -the same clock as the system clock, because the system clock can -change but steady clocks cannot. - -The third clock defined in the standard is ``std::chrono::high_resolution_clock``. -This clock is the clock that has the highest resolution out of all the -clocks in the system. It is normally an alias for either ``system_clock`` -or ``steady_clock``, but can be its own independent clock. Due -to this uncertainty, conversions of time measured on the -``high_resolution_clock`` to Python produce platform-dependent types: -you'll get a :py:class:`~datetime.datetime` if ``high_resolution_clock`` is -an alias for ``system_clock`` on your system, or a :py:class:`~datetime.timedelta` -value otherwise. - -Provided conversions -^^^^^^^^^^^^^^^^^^^^ - -The C++ types described in this section may be instantiated with any -precision. Conversions to a less-precise type will round towards zero. -Since Python's built-in date and time objects support only microsecond -precision, any precision beyond that on the C++ side will be lost when -converting to Python. - -.. rubric:: C++ to Python - -- ``std::chrono::system_clock::time_point`` → :py:class:`datetime.datetime` - A system clock time will be converted to a Python - :py:class:`~datetime.datetime` instance. The result describes a time in the - local timezone, but does not have any timezone information - attached to it (it is a naive datetime object). - -- ``std::chrono::duration`` → :py:class:`datetime.timedelta` - A duration will be converted to a Python :py:class:`~datetime.timedelta`. - Any precision beyond microseconds is lost by rounding towards zero. - -- ``std::chrono::[other_clock]::time_point`` → :py:class:`datetime.timedelta` - A time on any clock except the system clock will be converted to a Python - :py:class:`~datetime.timedelta`, which measures the number of seconds between - the clock's epoch and the time point of interest. - -.. rubric:: Python to C++ - -- :py:class:`datetime.datetime` or :py:class:`datetime.date` or :py:class:`datetime.time` → ``std::chrono::system_clock::time_point`` - A Python date, time, or datetime object can be converted into a - system clock timepoint. A :py:class:`~datetime.time` with no date - information is treated as that time on January 1, 1970. A - :py:class:`~datetime.date` with no time information is treated as midnight - on that date. **Any timezone information is ignored.** - -- :py:class:`datetime.timedelta` → ``std::chrono::duration`` - A Python time delta object can be converted into a duration - that describes the same number of seconds (modulo precision limitations). - -- :py:class:`datetime.timedelta` → ``std::chrono::[other_clock]::time_point`` - A Python time delta object can be converted into a timepoint on a - clock other than the system clock. The resulting timepoint will be - that many seconds after the target clock's epoch time. - -- ``float`` → ``std::chrono::duration`` - A floating-point value can be converted into a duration. The input is - treated as a number of seconds, and fractional seconds are supported - to the extent representable. - -- ``float`` → ``std::chrono::[other_clock]::time_point`` - A floating-point value can be converted into a timepoint on a - clock other than the system clock. The input is treated as a - number of seconds, and fractional seconds are supported to the - extent representable. The resulting timepoint will be that many - seconds after the target clock's epoch time. - - -Evaluating Python expressions from strings ------------------------------------------- - -The following functions can be used to evaluate Python functions and -expressions. They require an additional include directive: - -.. code-block:: cpp - - #include - -Detailed documentation including example code is provided in a :ref:`separate -section `. - -.. cpp:enum-class:: eval_mode - - This enumeration specifies how the content of a string should be - interpreted. Used in Py_CompileString(). - - .. cpp:enumerator:: eval_expr = Py_eval_input - - Evaluate a string containing an isolated expression - - .. cpp:enumerator:: eval_single_statement = Py_single_input - - Evaluate a string containing a single statement. Returns \c None - - .. cpp:enumerator:: eval_statements = Py_file_input - - Evaluate a string containing a sequence of statement. Returns \c None - -.. cpp:function:: template object eval(const char (&s)[N], handle global = handle(), handle local = handle()) - - Evaluate the given Python code in the given global/local scopes, and return - the value. - -.. cpp:function:: inline void exec(const str &expr, handle global = handle(), handle local = handle()) - - Execute the given Python code in the given global/local scopes. - -Intrusive reference counting helpers ------------------------------------- - -The following functions and classes can be used to augment user-provided -classes with intrusive reference counting that greatly simplifies shared -ownership in larger C++/Python binding projects. - -This functionality requires the following include directives: - -.. code-block:: cpp - - #include - #include - -These headers reference several functions, whose implementation must be -provided. You can do so by including the following file from a single ``.cpp`` -file of your project: - -.. code-block:: cpp - - #include - -The functionality in these files consist of the following classes and -functions: - -.. cpp:class:: intrusive_counter - - Simple atomic reference counter that can optionally switch over to - Python-based reference counting. - - The various copy/move assignment/constructors intentionally don't transfer - the reference count. This is so that the contents of classes containing an - ``intrusive_counter`` can be copied/moved without disturbing the reference - counts of the associated instances. - - .. cpp:function:: intrusive_counter() noexcept = default - - Initialize with a reference count of zero. - - .. cpp:function:: intrusive_counter(const intrusive_counter &o) - - Copy constructor, which produces a zero-initialized counter. - Does *not* copy the reference count from `o`. - - .. cpp:function:: intrusive_counter(intrusive_counter &&o) - - Move constructor, which produces a zero-initialized counter. - Does *not* copy the reference count from `o`. - - .. cpp:function:: intrusive_counter &operator=(const intrusive_counter &o) - - Copy assignment operator. Does *not* copy the reference count from `o`. - - .. cpp:function:: intrusive_counter &operator=(intrusive_counter &&o) - - Move assignment operator. Does *not* copy the reference count from `o`. - - .. cpp:function:: void inc_ref() const noexcept - - Increase the reference count. When the counter references an object - managed by Python, the operation calls ``Py_INCREF()`` to increase - the reference count of the Python object instead. - - The :cpp:func:`inc_ref() ` top-level function - encapsulates this logic for subclasses of :cpp:class:`intrusive_base`. - - .. cpp:function:: bool dec_ref() const noexcept - - Decrease the reference count. When the counter references an object - managed by Python, the operation calls ``Py_DECREF()`` to decrease - the reference count of the Python object instead. - - When the C++-managed reference count reaches zero, the operation returns - ``true`` to signal to the caller that it should use a *delete expression* - to destroy the instance. - - The :cpp:func:`dec_ref() ` top-level function - encapsulates this logic for subclasses of :cpp:class:`intrusive_base`. - - .. cpp:function:: void set_self_py(PyObject * self) - - Set the Python object associated with this instance. This operation - is usually called by nanobind when ownership is transferred to the - Python side. - - Any references from prior calls to - :cpp:func:`intrusive_counter::inc_ref()` are converted into Python - references by calling ``Py_INCREF()`` repeatedly. - - .. cpp:function:: PyObject * self_py() - - Return the Python object associated with this instance (or ``nullptr``). - -.. cpp:class:: intrusive_base - - Simple polymorphic base class for a intrusively reference-counted object - hierarchy. The member functions expose corresponding functionality of - :cpp:class:`intrusive_counter`. - - .. cpp:function:: void inc_ref() const noexcept - - See :cpp:func:`intrusive_counter::inc_ref()`. - - .. cpp:function:: bool dec_ref() const noexcept - - See :cpp:func:`intrusive_counter::dec_ref()`. - - .. cpp:function:: void set_self_py(PyObject * self) - - See :cpp:func:`intrusive_counter::set_self_py()`. - - .. cpp:function:: PyObject * self_py() - - See :cpp:func:`intrusive_counter::self_py()`. - -.. cpp:function:: void intrusive_init(void (* intrusive_inc_ref_py)(PyObject * ) noexcept, void (* intrusive_dec_ref_py)(PyObject * ) noexcept) - - Function to register reference counting hooks with the intrusive reference - counter class. This allows its implementation to not depend on Python. - - You would usually call this function as follows from the initialization - routine of a Python extension: - - .. code-block:: cpp - - NB_MODULE(my_ext, m) { - nb::intrusive_init( - [](PyObject * o) noexcept { - nb::gil_scoped_acquire guard; - Py_INCREF(o); - }, - [](PyObject * o) noexcept { - nb::gil_scoped_acquire guard; - Py_DECREF(o); - }); - - // ... - } - -.. cpp:function:: inline void inc_ref(intrusive_base * o) noexcept - - Reference counting helper function that calls ``o->inc_ref()`` if ``o`` is - not equal to ``nullptr``. - -.. cpp:function:: inline void dec_ref(intrusive_base * o) noexcept - - Reference counting helper function that calls ``o->dec_ref()`` if ``o`` is - not equal to ``nullptr`` and ``delete o`` when the reference count reaches - zero. - -.. cpp:class:: template ref - - RAII scoped reference counting helper class - - :cpp:class:`ref\ ` is a simple RAII wrapper class that encapsulates a - pointer to an instance with intrusive reference counting. - - It takes care of increasing and decreasing the reference count as needed and - deleting the instance when the count reaches zero. - - For this to work, compatible functions :cpp:func:`inc_ref()` and - :cpp:func:`dec_ref()` must be defined before including the file - ``nanobind/intrusive/ref.h``. Default implementations for subclasses of the - type :cpp:class:`intrusive_base` are already provided as part of the file - ``counter.h``. - - .. cpp:function:: ref() = default - - Create a null reference - - .. cpp:function:: ref(T * ptr) - - Create a reference from a pointer. Increases the reference count of the - object (if not ``nullptr``). - - .. cpp:function:: ref(const ref &r) - - Copy a reference. Increase the reference count of the object (if not - ``nullptr``). - - .. cpp:function:: ref(ref &&r) noexcept - - Move a reference. Object reference counts are unaffected by this operation. - - .. cpp:function:: ~ref() - - Destroy a reference. Decreases the reference count of the object (if not - ``nullptr``). - - .. cpp:function:: ref& operator=(ref &&r) noexcept - - Move-assign another reference into this one. - - .. cpp:function:: ref& operator=(const ref &r) - - Copy-assign another reference into this one. - - .. cpp:function:: ref& operator=(const T * ptr) - - Overwrite this reference with a pointer to another object - - .. cpp:function:: void reset() - - Clear the reference and reduces the reference count of the object (if not - ``nullptr``) - - .. cpp:function:: bool operator==(const ref &r) const - - Compare this reference with another reference (pointer equality) - - .. cpp:function:: bool operator!=(const ref &r) const - - Compare this reference with another reference (pointer inequality) - - .. cpp:function:: bool operator==(const T * ptr) const - - Compare this reference with another object (pointer equality) - - .. cpp:function:: bool operator!=(const T * ptr) const - - Compare this reference with another object (pointer inequality) - - .. cpp:function:: T * operator->() - - Access the object referenced by this reference - - .. cpp:function:: const T * operator->() const - - Access the object referenced by this reference (const version) - - .. cpp:function:: T& operator*() - - Return a C++ reference to the referenced object - - .. cpp:function:: const T& operator*() const - - Return a C++ reference to the referenced object (const version) - - .. cpp:function:: T* get() - - Return a C++ pointer to the referenced object - - .. cpp:function:: const T* get() const - - Return a C++ pointer to the referenced object (const version) - -Typing ------- - -The following functions for typing-related functionality require an additional -include directive: - -.. code-block:: cpp - - #include - -.. cpp:function:: template object type_var(Args&&... args) - - Create a `type variable - `__ (i.e., an - instance of ``typing.TypeVar``). All arguments of the original Python - construction are supported, e.g.: - - .. code-block:: cpp - - m.attr("T") = nb::type_var("T", - "contravariant"_a = true, - "covariant"_a = false, - "bound"_a = nb::type()); - - -.. cpp:function:: template object type_var_tuple(Args&&... args) - - Analogousto :cpp:func:`type_var`, create a `type variable tuple - `__ - (i.e., an instance of ``typing.TypeVarTuple``). - -.. cpp:function:: object any_type() - - Convenience wrapper, which returns ``typing.Any``. diff --git a/src/nanobind/docs/basics.rst b/src/nanobind/docs/basics.rst deleted file mode 100644 index 0deafe5..0000000 --- a/src/nanobind/docs/basics.rst +++ /dev/null @@ -1,481 +0,0 @@ -.. _basics: - -.. cpp:namespace:: nanobind - -Creating your first extension -############################# - -This section assumes that you have followed the instructions to :ref:`install -` nanobind and set up a basic :ref:`build system `. - -We are now ready to define a first basic extension that wraps a function -to add two numbers. Create a new file ``my_ext.cpp`` with the following -contents (the meaning of this code will be explained shortly): - -.. code-block:: cpp - - #include - - int add(int a, int b) { return a + b; } - - NB_MODULE(my_ext, m) { - m.def("add", &add); - } - -Afterwards, you should be able to compile and run the extension. - -Building using CMake --------------------- - -Launch ``cmake`` in the project directory to set up a build system that will -write all output into a separate ``build`` subdirectory. - -.. code-block:: bash - - cmake -S . -B build - -.. note:: - - If this step fails with an error message saying that Python cannot be - found, you will need to install a suitable Python 3 development package. - - For example, on Ubuntu you would run: - - .. code-block:: bash - - apt install libpython3-dev - - On Windows, you we recommend downloading and running one of the `installers - `_ provided by the Python foundation. - -.. note:: - - If you have multiple versions of Python on your system, the CMake build - system may not find the specific version you had in mind. This is - problematic: extension built for one version of Python usually won't run on - another version. You can provide a hint to the build system to help it find - a specific version. - - In this case, delete the ``build`` folder (if you already created one) and - re-run `cmake` while specifying the command line parameter ``-DPython_EXECUTABLE=``. - - .. code-block:: bash - - rm -Rf build - cmake -S . -B build -DPython_EXECUTABLE= - -Assuming the ``cmake`` ran without issues, you can now compile the extension using -the following command: - -.. code-block:: bash - - cmake --build build - -Finally, navigate into the ``build`` directory and launch an interactive Python -session: - -.. code-block:: bash - - cd build - python3 - -(The default build output directory is different on Windows: use ``cd build\Debug`` and ``python`` instead of the above.) - -You should be able to import the extension and call the newly defined function ``my_ext.add()``. - -.. code-block:: pycon - - Python 3.11.1 (main, Dec 23 2022, 09:28:24) [Clang 14.0.0 (clang-1400.0.29.202)] on darwin - Type "help", "copyright", "credits" or "license" for more information. - >>> import my_ext - >>> my_ext.add(1, 2) - 3 - - -Binding functions ------------------ - -Let's step through the example binding code to understand what each line does. -The directive on the first line includes the core parts of nanobind: - -.. code-block:: cpp - - #include - -nanobind also provides many optional add-on components that are aren't -included by default. They are discussed throughout this documentation along -with pointers to the header files that must be included when using them. - -Next is the function to be exposed in Python, followed by the -mysterious-looking :c:macro:`NB_MODULE` macro. - -.. code-block:: cpp - - int add(int a, int b) { return a + b; } - - NB_MODULE(my_ext, m) { - m.def("add", &add); - } - -:c:macro:`NB_MODULE(my_ext, m) ` declares the extension with the -name ``my_ext``. This name **must** match the extension name provided to the -``nanobind_add_module()`` function in the CMake build system---otherwise, -importing the extension will fail with an obscure error about a missing -symbol. The second argument (``m``) names a variable of -type :cpp:class:`nanobind::module_` that represents the created module. - -The part within curly braces (``{``, ``}``) consists of a sequence of -statements that initialize the desired function and class bindings. It is best -thought of as the ``main()`` function that will run when a user imports the -extension into a running Python session. - -In this case, there is only one binding declaration that wraps the ``add`` -referenced using the ampersand (``&``) operator. nanobind determines the -function's type signature and generates the necessary binding code. All of -this happens automatically at compile time. - -.. note:: - - Notice how little code was needed to expose our function to Python: all - details regarding the function’s parameters and return value were - automatically inferred using template metaprogramming. This overall - approach and the used syntax go back to `Boost.Python - `_, though the implementation in - nanobind is very different. - -.. _keyword_and_default_args: - -Keyword and default arguments ------------------------------ - -There are limits to what nanobind can determine at compile time. For example, -the argument names were lost and calling ``add()`` in Python using keyword -arguments fails: - -.. code-block:: pycon - - >>> my_ext.add(a=1, b=2) - TypeError: add(): incompatible function arguments. The following argument types are supported: - 1. add(arg0: int, arg1: int, /) -> int - - Invoked with types: kwargs = { a: int, b: int } - -Let's improve the bindings to fix this. We will also add a docstring and a -default ``b`` argument so that ``add()`` increments when only one value is -provided. The modified binding code looks as follows: - -.. code-block:: cpp - - #include - - namespace nb = nanobind; - using namespace nb::literals; - - int add(int a, int b = 1) { return a + b; } - - NB_MODULE(my_ext, m) { - m.def("add", &add, "a"_a, "b"_a = 1, - "This function adds two numbers and increments if only one is provided."); - } - -Let's go through all of the changed lines. The first sets up a short -namespace alias named ``nb``: - -.. code-block:: cpp - - namespace nb = nanobind; - -This is convenient because binding code usually ends up referencing many -classes and functions from this namespace. The subsequent ``using`` -declaration is optional and enables a convenient syntax for annotating -function arguments: - -.. code-block:: cpp - - using namespace nb::literals; - -Without it, you would have to change every occurrence of the pattern ``"..."_a`` -to the more verbose ``nb::arg("...")``. - -The function binding declaration includes several changes. It is common to pile -on a few attributes and modifiers in :cpp:func:`.def(...) ` -binding declarations, which can be specified in any order. - -.. code-block:: cpp - - m.def("add", &add, "a"_a, "b"_a = 1, - "This function adds two numbers and increments if only one is provided."); - -The string at the end is a `docstring `_ -that will later show up in generated documentation. The argument annotations -(``"a"_a, "b"_a``) associate parameters with names for keyword-based -argument passing. - -Besides argument names, nanobind also cannot infer *default arguments*---you -*must repeat them* in the binding declaration. In the above snippet, the -``"b"_a = 1`` annotation informs nanobind about the value of the default -argument. - -Exporting values ----------------- - -To export a value, use the :cpp:func:`attr() ` -function to register it in the module as shown below. Bound classes and -built-in types are automatically converted when they are assigned in this -way. - -.. code-block:: cpp - - m.attr("the_answer") = 42; - -.. _docstrings: - -Docstrings ----------- - -Let's add one more bit of flourish by assigning a docstring to the extension -module itself. Include the following line anywhere in the body of the -``NB_MODULE() {...}`` declaration: - -.. code-block:: cpp - - m.doc() = "A simple example python extension"; - -After recompiling the extension, you should be able to view the associated -documentation using the ``help()`` builtin or the ``?`` operator in -IPython. - -.. code-block:: pycon - - >>> import my_ext - >>> help(my_ext) - - Help on module my_ext: - - NAME - my_ext - A simple example python extension - - DATA - add = - add(a: int, b: int = 1) -> int - - This function adds two numbers and increments if only one is provided - - the_answer = 42 - - FILE - /Users/wjakob/my_ext/my_ext.cpython-311-darwin.so - -The automatically generated documentation covers functions, classes, -parameter and return value type information, argument names, and default -arguments. - - -.. _binding_types: - -Binding a custom type ---------------------- - -Let's now turn to an object oriented example. We will create bindings for a -simple C++ type named ``Dog`` defined as follows: - -.. code-block:: cpp - - #include - - struct Dog { - std::string name; - - std::string bark() const { - return name + ": woof!"; - } - }; - -The ``Dog`` bindings look as follows: - -.. code-block:: cpp - - #include - #include - - namespace nb = nanobind; - - NB_MODULE(my_ext, m) { - nb::class_(m, "Dog") - .def(nb::init<>()) - .def(nb::init()) - .def("bark", &Dog::bark) - .def_rw("name", &Dog::name); - } - -Let's look at selected lines of this example, starting with the added include directive: - -.. code-block:: cpp - - #include - -nanobind has a minimal core and initially doesn't know how to deal with STL -types like ``std::string``. This line imports a *type caster* that realizes a -bidirectional conversion (C++ ``std::string`` ↔ Python ``str``) to make the -example usable. An upcoming :ref:`documentation section ` will -provide more detail on type casters and other alternatives. - -The class binding declaration :class:`nb::class_\() ` supports both -``class`` and ``struct``-style data structures. - -.. code-block:: cpp - - nb::class_(m, "Dog") - -Here, it associates the C++ type ``Dog`` with a new Python type named ``"Dog"`` -and installs it in the :cpp:class:`nb::module_ ` ``m``. - -Initially, this type is completely empty---it has no members and cannot be -instantiated. The subsequent chain of binding declarations binds two -constructor overloads (via :cpp:class:`nb::init\<...\>() `), a method, -and the mutable ``name`` field (via :cpp:func:`.def_rw(..) -`, where ``rw`` stands for read/write access). - -.. code-block:: cpp - - .def(nb::init<>()) - .def(nb::init()) - .def("bark", &Dog::bark) - .def_rw("name", &Dog::name); - -An interactive Python session demonstrating this example is shown below: - -.. code-block:: pycon - - Python 3.11.1 (main, Dec 23 2022, 09:28:24) [Clang 14.0.0 (clang-1400.0.29.202)] on darwin - Type "help", "copyright", "credits" or "license" for more information. - >>> import my_ext - >>> d = my_ext.Dog('Max') - >>> print(d) - - >>> d.name - 'Max' - >>> d.name = 'Charlie' - >>> d.bark() - 'Charlie: woof!' - -The example showed how to bind constructors, methods, and mutable fields. Many -other things can be bound using analogous :cpp:class:`nb::class_\<...\> -` methods: - -.. list-table:: - :widths: 40 60 - :header-rows: 1 - - * - Type - - Method - * - Methods & constructors - - :cpp:func:`.def() ` - * - Fields - - :cpp:func:`.def_ro() `, - :cpp:func:`.def_rw() ` - * - Properties - - :cpp:func:`.def_prop_ro() `, - :cpp:func:`.def_prop_rw() ` - * - Static methods - - :cpp:func:`.def_static() ` - * - Static fields - - :cpp:func:`.def_ro_static() `, - :cpp:func:`.def_rw_static() ` - * - Static properties - - :cpp:func:`.def_prop_ro_static() `, - :cpp:func:`.def_prop_rw_static() ` - -.. note:: - - All of these binding declarations support :ref:`docstrings `, - :ref:`keyword, and default argument ` annotations - as before. - -.. _binding_lambdas: - -Binding lambda functions ------------------------- - -Note how ``print(d)`` produced a rather useless summary in the example above: - -.. code-block:: pycon - - >>> print(d) - - -To address this, we can add a special Python method named ``__repr__`` that -returns a human-readable summary. Unfortunately, a corresponding function with -such functionality does not currently exist in the C++ type, and it would be -nice if we did not have to modify it. We can bind a *lambda function* to -achieve both goals: - -.. code-block:: cpp - - nb::class_(m, "Dog") - // ... skipped ... - .def("__repr__", - [](const Dog &p) { return ""; }); - -nanobind supports both stateless [#f1]_ and stateful lambda closures. - -Higher order functions ----------------------- - -nanobind's support for higher-order functions [#f2]_ further blurs the language -boundary. The snippet below extends the ``Dog`` class with higher-order -function ``bark_later()`` that calls :cpp:func:`nb::cpp_function() -` to convert and return a *stateful* C++ lambda function -(``callback``) as a Python function object. - -.. code-block:: cpp - - nb::class_(m, "Dog") - // ... skipped ... - .def("bark_later", [](const Dog &p) { - auto callback = [name = p.name] { - nb::print(nb::str("{}: woof!").format(name)); - }; - return nb::cpp_function(callback); - }); - -The lambda function captures the ``Dog::name()`` property (a C++ -``std::string``) and in turn calls Python functions (:cpp:func:`nb::print() -`, :cpp:func:`nb::str::format() `) to print onto the -console. Here is an example use of the binding in Python: - -.. code-block:: pycon - - >>> f = d.bark_later() - >>> f - - >>> f() - Charlie: woof! - -Wrap-up -------- - -This concludes the basic part of the documentation, which provided a first -taste of nanobind and typical steps needed to create a custom extension. - -The upcoming intermediate-level material covers performance and -safety-critical points: - -- C++ and Python can exchange information in various different ways. - - *Which one is best for a particular task?* - -- A bound object can simultaneously exist in both C++ and Python. - - *Who owns it?* - *When is it safe to delete it?* - -Following these topics, the documentation revisits function and class -bindings in full detail. - -.. [#f1] Stateless closures are those with an empty pair of brackets ``[]`` as - the capture object. - -.. [#f2] Higher-order functions are functions that take functions as arguments - and/or return them. - diff --git a/src/nanobind/docs/bazel.rst b/src/nanobind/docs/bazel.rst deleted file mode 100644 index 40172a8..0000000 --- a/src/nanobind/docs/bazel.rst +++ /dev/null @@ -1,198 +0,0 @@ -.. _bazel: - -Building extensions using Bazel -=============================== - -If you prefer the Bazel build system to CMake, you can build extensions using -the `nanobind-bazel `__ project. - -.. note:: - - This project is a community contribution maintained by - `Nicholas Junge `__, please report issues - directly in the nanobind-bazel repository linked above. - -.. _bazel-setup: - -Adding nanobind-bazel to your Bazel project -------------------------------------------- - -To use nanobind-bazel in your project, you need to add it to your project's -dependency graph. Using bzlmod, the de-facto dependency management system -in Bazel starting with version 7.0, you can simply specify it as a ``bazel_dep`` -in your MODULE.bazel file: - -.. code-block:: python - - # 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.7.0. - bazel_dep(name = "nanobind_bazel", version = "2.7.0") - -To instead use a development version from GitHub, you can declare the -dependency as a ``git_override()`` in your MODULE.bazel: - -.. code-block:: python - - # MODULE.bazel - bazel_dep(name = "nanobind_bazel", version = "") - git_override( - module_name = "nanobind_bazel", - commit = COMMIT_SHA, # replace this with the actual commit you want. - remote = "https://github.com/nicholasjng/nanobind-bazel", - ) - -In local development scenarios, you can clone nanobind-bazel to your machine, -and then declare it as a ``local_path_override()`` dependency: - -.. code-block:: python - - # MODULE.bazel - bazel_dep(name = "nanobind_bazel", version = "") - local_path_override( - module_name = "nanobind_bazel", - path = "/path/to/nanobind-bazel/", # replace this with the actual path. - ) - -.. note:: - - At minimum, Bazel version 7.0.0 is required to use nanobind-bazel. - - -.. _bazel-build: - -Declaring and building nanobind extension targets -------------------------------------------------- - -The main tool to build nanobind C++ extensions for your Python bindings is the -:py:func:`nanobind_extension` rule. - -Like all public nanobind-bazel APIs, it resides in the ``build_defs`` submodule. -To import it into a BUILD file, use the builtin ``load`` command: - -.. code-block:: python - - # In a BUILD file, e.g. my_project/BUILD - load("@nanobind_bazel//:build_defs.bzl", "nanobind_extension") - - nanobind_extension( - name = "my_ext", - srcs = ["my_ext.cpp"], - ) - -In this short snippet, a nanobind Python module called ``my_ext`` is declared, -with its contents coming from the C++ source file of the same name. -Conveniently, only the actual module name must be declared - its place in your -Python project hierarchy is automatically determined by the location of your -build file. - -For a comprehensive list of all available build rules in nanobind-bazel, refer -to the rules section in the :ref:`nanobind-bazel API reference `. - -.. _bazel-stable-abi: - -Building against the stable ABI -------------------------------- - -As in nanobind's CMake config, you can build bindings targeting Python's -stable ABI, starting from version 3.12. To do this, specify the target -version using the ``@nanobind_bazel//:py-limited-api`` flag. For example, -to build extensions against the CPython 3.12 stable ABI, pass the option -``@nanobind_bazel//:py-limited-api="cp312"`` to your ``bazel build`` command. - -For more information about available flags, refer to the flags section in the -:ref:`nanobind-bazel API reference `. - -Generating stubs for built extensions -------------------------------------- - -You can also use Bazel to generate stubs for an extension directly at build -time with the ``nanobind_stubgen`` macro. Here is an example of a nanobind -extension with a stub file generation target declared directly alongside it: - -.. code-block:: python - - # Same as before in a BUILD file - load( - "@nanobind_bazel//:build_defs.bzl", - "nanobind_extension", - "nanobind_stubgen", - ) - - nanobind_extension( - name = "my_ext", - srcs = ["my_ext.cpp"], - ) - - nanobind_stubgen( - name = "my_ext_stubgen", - module = ":my_ext", - ) - -You can then generate stubs on an extension by invoking -``bazel run //my_project:my_ext_stubgen``. Note that this requires actually -running the target instead of only building it via ``bazel build``, since a -Python script needs to be executed for stub generation. - -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. - -Building extensions for free-threaded Python --------------------------------------------- - -Starting from CPython 3.13, bindings extensions can be built for a free-threaded -CPython interpreter. This requires two things: First, an eligible toolchain must -be defined in your MODULE.bazel file, e.g. like so: - -.. code-block:: python - - bazel_dep(name = "rules_python", version = "1.0.0") - - python = use_extension("@rules_python//python/extensions:python.bzl", "python") - python.toolchain(python_version = "3.13") - -And secondly, the ``@rules_python//python/config_settings:py_freethreaded`` flag must -be set to "yes" when building your nanobind extension target, e.g. as -``bazel build //path/to:my_ext --@rules_python//python/config_settings:py_freethreaded=yes``. - -Then, ``rules_python`` will bootstrap a free-threaded version of your target interpreter, -and ``nanobind_bazel`` will define the ``NB_FREE_THREADED`` macro for the libnanobind -build, indicating that nanobind should be built with free-threading support. -For a comprehensive overview on nanobind with free-threaded Python, refer to the -:ref:`free-threading documentation `. - -nanobind-bazel and Python packaging ------------------------------------ - -Unlike CMake, which has a variety of projects supporting PEP517-style -Python package builds, Bazel does not currently have a fully featured -PEP517-compliant packaging backend available. - -To produce Python wheels containing bindings built with nanobind-bazel, -you have various options, with two of the most prominent strategies being - -1. Using a wheel builder script with the facilities provided by a Bazel -support package for Python, such as ``py_binary`` or ``py_wheel`` from -`rules_python `__. This is -a lower-level, more complex workflow, but it provides more granular -control of how your Python wheel is built. - -2. Building all extensions with Bazel through a subprocess, by extending -a Python build backend such as ``setuptools``. This allows you to stick to -those well-established build tools, like ``setuptools``, at the expense -of more boilerplate Python code and slower build times, since Bazel is -only invoked to build the bindings extensions (and their dependencies). - -In general, while the latter method requires less setup and customization, -its drawbacks weigh more severely for large projects with more extensions. - -.. note:: - - An example of packaging with the mentioned setuptools customization method - can be found in the - `nanobind_example `__ - repository, specifically, on the ``bazel`` branch. It also contains an - example of how to customize flag names and set default build options across - platforms with a ``.bazelrc`` file. diff --git a/src/nanobind/docs/benchmark.rst b/src/nanobind/docs/benchmark.rst deleted file mode 100644 index 92fe88b..0000000 --- a/src/nanobind/docs/benchmark.rst +++ /dev/null @@ -1,174 +0,0 @@ -.. _benchmarks: - -Benchmarks -========== - -.. note:: - - **TL;DR**: nanobind bindings compile up to **~4× faster** and produce **~5× - smaller** binaries with **~10× lower** 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). - -The following experiments analyze the performance of a large function-heavy -(``func``) and class-heavy (``class``) binding microbenchmark compiled using -`Boost.Python `__, `Cython -`__, `pybind11 `__. The -``pybind11 + smart_holder`` results below refer to a `special branch -`__ that addresses -long-standing issues related to holder types in pybind11. - -Each experiment is shown twice: light gray ``[debug]`` columns provide data for -a debug build, and ``[opt]`` shows a size-optimized build that is representative -of a deployment scenario. The former is included to show that nanobind -performance is also good during a typical development workflow. - -A comparison with `cppyy `_, which -uses dynamic compilation, is also shown later. Details on the experimental -setup can be found :ref:`below `. - -Compilation time ----------------- - -The first plot contrasts the compilation time, where “*number* ×” -annotations denote the amount of time spent relative to nanobind. As -shown below, nanobind achieves a ~\ **2.7-4.4× improvement** -compared to pybind11 and a **1.6-4.4x improvement** compared to Cython. - - -.. image:: images/times.svg - :width: 800 - :alt: Compilation time benchmark - -Binary size ------------ - -The extremely large size of generated binaries has been a persistent problem of -many prior binding libraries. nanobind significantly improves this metric in -size-optimized builds. There is a ~\ **11× improvement** compared to -Boost.Python, a **3-5× improvement** compared to pybind11, and a **3-12× -improvement** compared to Cython. - -.. image:: images/sizes.svg - :width: 800 - :alt: Compilation size benchmark - -Performance ------------ - -The last experiment compares the runtime performance overheads by calling a -bound function many times in a loop. Here, it is also interesting to -additionally compare against `cppyy -`__ (green bar) and a pure Python -implementation that runs bytecode without binding overheads (hatched gray bar). -The `smart_holder` branch of pybind11 is not explicitly listed since its -runtime performance matches the base version. - -.. image:: images/perf.svg - :width: 850 - :alt: Runtime performance benchmark - -This data shows that the overhead of calling a nanobind function is -lower than that of an equivalent function call done within CPython. The -functions benchmarked here don’t perform CPU-intensive work, so this -this mainly measures the overheads of performing a function call, -boxing/unboxing arguments and return values, etc. - -The difference to pybind11 is **significant**: a ~\ **3× improvement** -for simple functions, and an **~10× improvement** when classes are being -passed around. Complexities in pybind11 related to overload -resolution, multiple inheritance, and holders are the main reasons for -this difference. Those features were either simplified or completely -removed in nanobind. - -The runtime performance of Cython and nanobind are similar (Cython leads in one -experiment and trails in another one). Cython generates specialized binding -code for every function and class, which is highly redundant (long compile -times, large binaries) but can also be beneficial for performance. - -Finally, there is a **~1.6-2.1× improvement** in both experiments compared to -cppyy (please ignore the two ``[debug]`` columns—I did not feel comfortable -adjusting the JIT compilation flags; all cppyy bindings are therefore -optimized.) - -Discussion ----------- - -Performance improvements compared to pybind11 are the result of optimizations -discussed in the :ref:`previous section `. - -`cppyy `_ also achieves good -performance in the comparison above. It is based on dynamic parsing of C++ code -and *just-in-time* (JIT) compilation of bindings via the LLVM compiler -infrastructure. The authors of cppyy report that their tool produces bindings -with much lower overheads compared to pybind11, and the above plots show that -this is indeed true. - -While nanobind retakes the performance lead, there are other qualitative -factors make these two tools appropriate to different audiences: cppyy has its -origin in CERN's ROOT mega-project and must be highly dynamic to work with that -codebase: it can parse header files to generate bindings as needed. cppyy works -particularly well together with PyPy and can avoid boxing/unboxing overheads -with this combination. The main downside of cppyy is that it depends on -Cling/Clang/LLVM that must be deployed on the user's side and then run there. -There isn't a way of pre-generating bindings and then shipping just the output -of this process. - -nanobind is relatively static in comparison: you must tell it which functions -to expose via binding declarations. These declarations offer a high degree of -flexibility that users will typically use to create bindings that feel -*pythonic*. At compile-time, those declarations turn into a sequence of CPython -API calls, which produces self-contained bindings that are easy to redistribute -via `PyPI `_ or elsewhere. Tools like `cibuildwheel -`_ and `scikit-build -`_ can fully automate -the process of generating *Python wheels* for each target platform. A `minimal -example project `_ shows how to do -this automatically via `GitHub Actions `_. - -.. _benchmark_details: - -Details -------- - -The microbenchmark wraps a *large* number of trivial functions that only -perform a few additions. The objective of this is to quantify the overhead of -bindings on compilation time, binary size, and runtime performance. The -function-heavy benchmark (``func_*``) consists of 720 declarations of the form -(with permuted integer types) - -.. code-block:: cpp - - m.def("test_0050", [](uint16_t a, int64_t b, int32_t c, uint64_t d, uint32_t e, float f) { - return a+b+c+d+e+f; - }); - -while the latter (``class_*``) does exactly the same computation but packaged -up in ``struct``\ s with bindings. - -.. code-block:: cpp - - struct Struct50 { - uint16_t a; int64_t b; int32_t c; uint64_t d; uint32_t e; float f; - Struct50(uint16_t a, int64_t b, int32_t c, uint64_t d, uint32_t e, float f) - : a(a), b(b), c(c), d(d), e(e), f(f) { } - float sum() const { return a+b+c+d+e+f; } - }; - - py::class_(m, "Struct50") - .def(py::init()) - .def("sum", &Struct50::sum); - - -The code to generate the plots shown above is available `here -`_. - -Each test was compiled in debug mode (``debug``) and with optimizations -(``opt``) that minimize size (i.e., ``-Os``). Benchmarking was performed on a -AMD Ryzen 9 7950X workstation running Ubuntu 22.04.2 LTS. CPU boost was -disabled, and all core clock frequencies were pinned. Reported timings are the -median of five runs. Compilation used clang++ 15.0.7 with consistent compilation flags for -all experiments (see the referenced notebook file for detail). The used package -versions were Python 3.10.6, cppyy 1.12.13, Cython 0.29.28, and nanobind 1.2.0. diff --git a/src/nanobind/docs/building.rst b/src/nanobind/docs/building.rst deleted file mode 100644 index 346ef5f..0000000 --- a/src/nanobind/docs/building.rst +++ /dev/null @@ -1,118 +0,0 @@ -.. _building: - -Setting up a build system -######################### - -This section assumes that you have followed the instructions to :ref:`install -` nanobind. The easiest way to compile a nanobind-based extension -involves a CMake-based build system. Other build systems can likely be used as -well, but they are not officially supported. -(The first section of the :ref:`CMake API reference ` mentions -some alternatives.) - -Here, we will create a new package from scratch. If you already have an -existing CMake build system, it should be straightforward to merge some of the -following snippets into it. - -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 -name of this module changed across CMake versions, hence the additional -conditional check. - -.. code-block:: cmake - - cmake_minimum_required(VERSION 3.15...3.27) - project(my_project) # Replace 'my_project' with the name of your project - - if (CMAKE_VERSION VERSION_LESS 3.18) - set(DEV_MODULE Development) - else() - set(DEV_MODULE Development.Module) - endif() - - find_package(Python 3.8 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 -addition, binding code may run slowly and produce large binaries. - -.. code-block:: cmake - - if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE) - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") - endif() - -Finding nanobind ----------------- - -Next, we must inform CMake about the presence of nanobind so that it can load -the functionality needed to compile extension modules. The details of this -step depend on *how you installed* nanobind, in the :ref:`previous section -`. - -1. If you installed nanobind as a Pip or Conda package, append the following - lines at the end of ``CMakeLists.txt``. They query the package to determine - its installation path and then import it. - - .. code-block:: cmake - - # Detect the installed nanobind package and import it into CMake - execute_process( - COMMAND "${Python_EXECUTABLE}" -m nanobind --cmake_dir - OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE nanobind_ROOT) - find_package(nanobind CONFIG REQUIRED) - -2. If you installed nanobind as a `Git submodule - `_, append the - following lines at the end of ``CMakeLists.txt`` to point CMake to the - directory where nanobind is checked out. - - .. code-block:: cmake - - add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/ext/nanobind) - -Building an extension ---------------------- - -Finally, we are ready to build an extension! Append the following line at the end of -``CMakeLists.txt``. It will compile a new extension named ``my_ext`` from the -source code contained in the file ``my_ext.cpp``. - -.. code-block:: cmake - - nanobind_add_module(my_ext my_ext.cpp) - -:cmake:command:`nanobind_add_module` resembles standard CMake commands like -``add_executable()`` and ``add_library()``. Any number of source code and -header files can be declared when the extension is more complex and spread out -over multiple files. - -.. note:: - - One opinionated choice of :cmake:command:`nanobind_add_module` is that it - optimizes the *size* of the extension by default (i.e., ``-Os`` is passed to - the compiler regardless of the project-wide settings). You must specify the - ``NOMINSIZE`` parameter to the command to disable this behavior and, e.g., - optimize extension code for speed (i.e., ``-O3``): - - .. code-block:: cmake - - nanobind_add_module(my_ext NOMINSIZE my_ext.cpp) - - The default is chosen this way since extension code usually wraps existing - C++ libraries, in which the main computation takes place. Optimizing the - bindings for speed does not measurably improve performance, but it does make - the bindings *significantly* larger. - - If you observe slowdowns when porting a pybind11 extension, or if your - extension performs significant amounts of work within the binding layer, - then you may want to experiment with passing the ``NOMINSIZE`` parameter. - -The :ref:`next section ` will review the contents of example module -implementation in ``my_ext.cpp``. diff --git a/src/nanobind/docs/changelog.rst b/src/nanobind/docs/changelog.rst deleted file mode 100644 index 2d64ffc..0000000 --- a/src/nanobind/docs/changelog.rst +++ /dev/null @@ -1,1587 +0,0 @@ -.. _changelog: - -.. cpp:namespace:: nanobind - -Changelog -######### - -nanobind uses a `semantic versioning `__ policy for its API. -It also has a separate ABI version that is *not* subject to semantic -versioning. - -The ABI version is relevant whenever a type binding from one extension module -should be visible in another nanobind-based extension module. In this -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 TBD (unreleased) ------------------------- - -- Added :cpp:class:`nb::fallback ` wrapper type, which is a - :cpp:class:`nb::handle ` that always requires implicit conversion - during casting. This is convenient when adding catch-all overloads that must - handle arbitrary Python objects, without interfering with implicit conversion - of arguments in other overloads. - -- The ``nanobind::literals`` namespace now includes ``_s`` to create a Python string - from source code literals. (PR `#1051 - `__). - -- Nanobind now uses multi-phase (as opposed to single-phase) initialization API - when registering modules. However, multi-interpreter extensions remain - unsupported. (PR `#1059 `__). - -- Added :cpp:func:`nb::dict::empty() `, - :cpp:func:`nb::list::empty() `, :cpp:func:`nb::set::empty() - `, and :cpp:func:`nb::tuple::empty() ` convenience - methods. - -- Nanobind now uses multi-phase (as opposed to single-phase) initialization API - when registering modules. However, multi-interpreter extensions remain - unsupported. (PR `#1059 `__). - -- Miscellaneous fixes and improvements ( - commits - `d4b245 `__, - `667451 `__, - `62fc99 `__). - - -- Added :cpp:class:`nb::frozenset` that wraps the Python ``frozenset`` type. - -Version 2.7.0 (Apr 18, 2025) ----------------------------- - -- nanobind now provides a zero-copy type caster for - ``Eigen::Map``. (PRs `#1003 - `__, `#782 - `__). - -- Made handling of return value policies in Eigen type casters more consistent - with the rest of nanobind. (Issue `#971 - `__, commit `5cdf59 - `__). - -- The Eigen sparse matrix caster now correctly handles ``scipy.sparse`` objects - with unsorted indices. (PR `#981 - `__). - -- Nanobind's CMake stub generation command :cmake:command:`nanobind_add_stub` - now detects when an extension uses sanitizers (TSAN, ASAN, UBSAN). It then - injects the sanitizer library into the Python process ahead of time so that - the extension can be loaded. Previously, stub generation failed in - such cases. (PR `#1000 `__). - -- The entries of stub files are now sorted in their original definition order. - Previously, they were alphabetically sorted, which caused issues with - external tooling. (PR `#938 - `__). - -- Fixed detection and handling of imports and types in external modules in - stubgen that could lead to incorrect declarations in some cases. (PRs `#939 - `__, `#940 - `__). - -- The stub generator now detects method aliases and preserves this information - instead of duplicating the definition. (PR `#735 - `__). - -- Corrected a flaw in the recommended implementation of ``tp_traverse`` in - garbage-collected bindings. (PRs `#1015 - `__). - -- Added support for binding functions that accept a ``std::variant<...>`` that - is not default-constructible (because its first alternative isn't). (PR `#987 - `__). - -- Added support for casting const-qualified ``std::unique_ptr`` values. (PR - `#988 `__). - -- ``nb::typed`` now supports construction from ``T``, making it more - ergonomic to return values with type annotations. (PR `#1012 - `__ - -- Miscellaneous fixes and improvements (PRs - `#1014 `__, - `#1005 `__, - `#1004 `__, - `#990 `__, - `#997 `__, commits - `f2b08c `__, - `eef931 `__, - `f1b2f5 `__, - `dbd602 `__, - `2c83fb `__, - `87de84 `__). - -Version 2.6.1 (Mar 28, 2025) ----------------------------- - -- nanobind assigns an ABI tag to compiled extensions and uses it to isolate - incompatible extensions from each other. This tag was unnecessarily - fine-grained, often causing isolation where an actual ABI compatibility was - not present. This release updates the tagging scheme to address this - long-standing inconvenience. (PR `#778 - `__). - -- Added specialized function dispatchers to accelerate calls to 0 and - 1-argument functions. (PR `#944 - `__). - -- Improved the efficiency of :cpp:func:`nb::getattr(obj, key, - default) ` in cases where ``obj[key]`` does not exist. (commit - `bb05f5 - `__). - -- ABI version 16. - -- Miscellaneous fixes and improvements (PRs `#913 - `__, `#914 - `__, `#916 - `__, `#931 - `__, `#978 - `__, commit `1595d2 - `__). - -Version 2.6.0 (Mar 28, 2025) ----------------------------- - -- This release was yanked due to a regression. - -Version 2.5.0 (Feb 2, 2025) ---------------------------- - -- Added :cpp:class:`nb::def_visitor\<..\> `, which can be used to - define your own binding logic that operates on a :cpp:class:`nb::class_\<..\> - ` when an instance of the visitor object is passed to - :cpp:func:`class_::def()`. This generalizes the mechanism used by - :cpp:class:`init`, :cpp:class:`new_`, etc, so that you can create binding - abstractions that "feel like" the built-in ones. (PR `#884 - `__) - -- Added some special forms for :cpp:class:`nb::typed\ ` - (PR `#835 `__): - - - ``nb::typed`` or ``nb::typed`` produces - a parameter or return value that will be described like ``T`` in function - signatures but accepts any Python object at runtime. - - - ``nb::typed`` produces a Python callable signature - ``Callable[[Args...], R]``; similarly, ``nb::typed`` - (with a literal ellipsis) produces the Python ``Callable[..., R]``. - -- It is now possible to create Python subclasses of C++ classes that define - their constructor bindings using :cpp:struct:`nb::new_() `. Previously, - attempting to instantiate such a Python subclass would instead produce an - instance of the base C++ type. Note that it is still not possible to override - virtual methods in such a Python subclass, because the object returned by the - :cpp:struct:`new_() ` constructor will generally not be an instance of - the alias/trampoline type. (PR `#859 - `__) - -- Fixed the :cpp:class:`nb::int_ ` constructor so that it casts to - an integer when invoked with a floating point argument. - -- Multi-level inheritance (e.g., ``A → B → C``) previously did not work on Python - 3.12+ when a base class (e.g., ``A``) provided a trampoline implementation. - This is now fixed. (commit `92d9cb - `__). - -- A new ``NB_SUPPRESS_WARNINGS`` parameter of - :cmake:command:`nanobind_add_module` that marks the nanobind and Python - include directories as - `SYSTEM `__ - include directories, which suppresses any potential warning messages - originating there. This is mainly of relevance for projects that artificially - raise the warning level using flags like ``-pedantic``, ``-Wcast-qual``, - ``-Wsign-conversion``. (PR `#868 - `__). - -- Fixed (benign) reference leaks that could occur when ``std::shared_ptr`` - instances were still alive at interpreter shutdown time. (commit `fb8157 - `__). - -- The floating-point type caster now only performs value-changing narrowing - conversions during the implicit conversion phase. They can be entirely - avoided by passing the :cpp:func:`.noconvert() ` argument - annotation. (PR `#829 `__) - -- The ``std::complex`` type caster now only performs value-changing narrowing - conversions during the implicit conversion phase. They can be entirely - avoided by passing the :cpp:func:`.noconvert() ` argument - annotation. Also, during the implicit conversion phase, if the Python object - is not a complex number object but has a ``__complex__()`` method, it will be - called. (PR `#854 `__) - -- Fixed an overly strict check that could cause a function taking an - :cpp:class:`nb::ndarray\<...\> ` to refuse specific types of - column-major input without implicit conversion. (PR `#847 - `__, commit `b95eb7 - `__). - -Fixes for free-threaded builds -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -- Fixed a race condition in free-threaded extensions that could occur when - :cpp:func:`nb::make_iterator ` was concurrently used by - multiple threads. (PR `#832 `__). - -- Fixed a race condition in free-threaded extensions that could occur when - multiple threads access the Python object associated with the same C++ - instance, which does not exist yet and therefore must be created. (issue - `#867 `__, PR `#887 - `__). - -- Removed double-checked locking patterns in accesses to internal data - structures to ensure correct free-threaded behavior on architectures with - weak memory ordering such as ARM (PR `#819 - `__). - -Version 2.4.0 (Dec 6, 2024) ---------------------------- - -- Added a function annotation :cpp:class:`nb::call_policy\() - ` which supports custom function wrapping logic, - calling ``Policy::precall()`` before the bound function and - ``Policy::postcall()`` after. This is a low-level interface intended - for advanced users. The precall and postcall hooks are able to - observe the Python objects forming the function arguments and return - value, and the precall hook can change the arguments. See the linked - documentation for more details, important caveats, and an example policy. - (PR `#767 `__) - -- :cpp:func:`nb::make_iterator ` now accepts its iterator - arguments by value, rather than by forwarding reference, in order to - eliminate the hazard of storing a dangling C++ iterator reference in the - returned Python iterator object. (PR `#788 - `__) - -- The ``std::variant`` type_caster now does two passes when converting from Python. - The first pass is done without implicit conversions. This fixes an issue where - ``std::variant`` might cast a Python object wrapping a ``T`` to a ``U`` if - there is an implicit conversion available from ``T`` to ``U``. - (issue `#769 `__) - -- Restored support for constructing types with an overloaded ``__new__`` that - takes no arguments, which regressed with the constructor vector call - acceleration that was added in nanobind 2.2.0. - (issue `#786 `__) - -- Bindings for augmented assignment operators (as generated, for example, by - ``.def(nb::self += nb::self)``) now return the same object in Python in the - typical case where the C++ operator returns a reference to ``*this``. - Previously, after ``a += b``, ``a`` would be replaced with a copy. - (PR `#803 `__) - -- Added an overload to :cpp:func:`nb::isinstance ` which tests if a - Python object is an instance of a Python class. This is in addition to the - existing overload, which tests if a Python object is an instance of a bound - C++ class. (PR `#805 `__). - -- Added support for overriding static properties, such as those defined using - ``def_prop_ro_static``, in subclasses. Previously this would fail with an - error. (PR `#806 `__). - -- Other minor fixes and improvements. (PRs `#771 - `__, `#772 - `__, `#748 - `__, and `#753 - `__) - -Version 2.3.0 -------------- - -There is no version 2.3.0 due to a deployment mishap. - -- Added casters for `Eigen::Map` types from the `Eigen library - `__. (PR `#782 - `_). - -Version 2.2.0 (October 3, 2024) -------------------------------- - -- nanobind can now target `free-threaded Python - `__, which replaces the `Global - Interpreter Lock (GIL) - `__ with a - fine-grained locking scheme (see `PEP 703 - `__) to better leverage multi-core - parallelism. A :ref:`separate documentation page ` explains this in - detail (PRs `#695 `__, `#720 - `__) - -- nanobind has always used `PEP 590 vector calls - `__ to efficiently dispatch calls - to function and method bindings, but it lacked the ability to do so for - constructors (e.g., ``MyType(arg1, arg2, ...)``). - - Version 2.2.0 adds this missing part, which accelerates object - construction by up to a factor of 2×. The difference is - especially pronounced when passing keyword arguments to - constructors. Note that this improvement only applies to - Python version 3.9 and newer (PR - `#706 `__, commits - `#e24d7f `__, - `#0acecb `__, - `#77f910 `__, - `#2c96d5 `__). - -* A new :cpp:class:`nb::is_flag() ` annotation in - :cpp:class:`nb::enum_\() ` produces enumeration - bindings deriving from :py:class:`enum.Flag`, which enables - bit-wise combination using compatible operators (``&``, ``|``, - ``^``, and ``~``). Further combining the annotation with - :cpp:class:`nb::is_arithmetic() ` creates - enumerations deriving from :py:class:`enum.IntFlag`. (PRs - `#599 `__, - `#688 `__, - `#688 `__, - `#727 `__, - `#732 `__) - -* A refactor of :cpp:class:`nb::ndarray\<...\> ` was an opportunity to - realize three usability improvements: - - 1. The constructor used to return new nd-arrays from C++ now considers - all template arguments: - - - **Memory order**: :cpp:class:`c_contig`, :cpp:class:`f_contig`. - - **Shape**: :cpp:class:`nb::shape\<3, 4, 5\> `, etc. - - **Device type**: :cpp:class:`nb::device::cpu `, - :cpp:class:`nb::device::cuda `, etc. - - **Framework**: :cpp:class:`nb::numpy `, - :cpp:class:`nb::pytorch `, etc. - - **Data type**: ``uint64_t``, ``std::complex``, etc. - - Previously, only the **framework** and **data type** annotations were - taken into account when returning nd-arrays, while all of them were - examined when *accepting* arrays during overload resolution. This - inconsistency was a repeated source of confusion among users. - - To give an example, the following now works out of the box without the - need to redundantly specify the shape and strides to the ``Array`` - constructor below: - - .. code-block:: cpp - - using Array = nb::ndarray, nb::f_contig>; - - struct Matrix4f { - float m[4][4]; - Array data() { return Array(m); } - }; - - nb::class_(m, "Matrix4f") - .def("data", &Matrix4f::data, nb::rv_policy::reference_internal); - - 2. A new nd-array :cpp:func:`.cast() ` method forces the - immediate creation of a Python object with the specified target framework - and return value policy, while preserving the type signature in return - values. This is useful to :ref:`return temporaries (e.g. stack-allocated - memory) ` from functions. - - 3. Added a new and more general mechanism ``nanobind::detail::dtype_traits`` - to declare custom ndarray data types like ``float16`` or ``bfloat16``. The old - interface (``nanobind::ndarray_traits``) still exists but is deprecated - and will be removed in the next major release. See the :ref:`documentation - ` for details. - - There are two minor but potentially breaking changes: - - 1. The nd-array type caster now interprets the - :cpp:enumerator:`nb::rv_policy::automatic_reference - ` return value policy analogously to the - :cpp:enumerator:`nb::rv_policy::automatic `, which - means that it references a memory region when the user specifies an - ``owner``, and it otherwise copies. This makes it safe to use the - :cpp:func:`nb::cast() ` and :cpp:func:`nb::ndarray::cast() - ` functions that use this policy as a default. - - 2. The :cpp:class:`nb::any_contig ` memory order annotation, - which previously did nothing, now accepts C- or F-contiguous arrays and - rejects non-contiguous ones. - - For further details on the nd-array changes, see PR `#721 - `__, For further details on the - nd-array changes, see PR `#742 - `__, and commit `4647ef - `__. - -- The NVIDIA CUDA compiler (``nvcc``) is now explicitly supported and included - in nanobind's CI test suite (PR `#710 - `__). - -* Added support for return value policy customization to the type casters of - ``Eigen::Ref<...>`` and ``Eigen::Map<...>`` (commit `67316e - `__). - -* Added the :cpp:class:`bytearray` wrapper type. (PR `#654 - `__) - -* The :cpp:class:`nb::ellipsis ` type now renders as ``...`` when - used in :cpp:class:`nb::typed\<...\> ` (PR `#705 - `__). - -* The :cpp:class:`nb::sig("...") ` annotation now supports `inline type - parameter lists - `__ such - as ``def first[T](l: Sequence[T]) -> T`` (PR `#704 - `__). - -* Fixed implicit conversion of complex nd-arrays. (issue `#709 - `__) - -* Casting via :cpp:func:`nb::cast ` can now specify an owner object for - use with the :cpp:enumerator:`nb::rv_policy::reference_internal - ` return value policy (PR `#667 - `__). - -* The ``std::optional`` type caster is now implemented in such a way that it - can also accommodate non-STL frameworks, such as Boost, Abseil, etc. (PR - `#675 `__) - -* ABI version 15. - -* Minor fixes and improvements (PRs - `#703 `__, - `#724 `__, - `#723 `__, - `#722 `__, - `#715 `__, - `#696 `__, - `#693 `__, - commit `75d259 `__). - -Version 2.1.0 (Aug 11, 2024) ----------------------------- - -* Temporary workaround for a internal compiler error in version 17.10 of the MSVC - compiler. This workaround will be removed once fixed versions are deployed on - GitHub actions. (issue `#613 - `__, commit `f2438b - `__). - -* nanobind no longer prevents casting to a C++ container of pointers ``T*`` - where ``T`` is a type with a user-defined type caster if the caster seems to - operate by extracting a ``T*`` from the Python object rather than a ``T``. - This change was prompted by discussion `#605 - `__. - -* Switched nanobind wheel generation from `setuptools - `__ to `scikit-build-core - `__ (PR `#618 - `__). - -* Improved handling of ``const``-ness in :cpp:class:`nb::ndarray ` (PR - `#491 `__). - -* Keyword argument annotations are now properly supported with - :cpp:struct:`nb::new_ `, passed in the same way they would be with - :cpp:struct:`nb::init `. (issue `#668 - `__) - -* Ability to use :cpp:func:`nb::cast ` to create object with the - :cpp:enumerator:`nb::rv_policy::reference_internal - ` return value policy (PR `#667 - `__). - -* Enable ``char`` type caster to produce ``'\0'`` (PR `#661 - `__). - -* Added ``.def_static()`` member to :cpp:class:`nb::enum_ `, which had - been lost in a redesign of the enumeration implementation in nanobind version - 2.0.0. (commit `38990e - `__). - -* Fixes for two minor sources of memory leaks (PR - `#595 `__, - `#647 `__). - -* The nd-array wrapper :cpp:class:`nb::ndarray ` now properly handles - CuPy arrays (`#594 `__). - -* Added :cpp:func:`nb::hash() `, a wrapper for the Python ``hash()`` - function (commit `91fafa5 - `__). - -* Various minor ``stubgen`` fixes (PRs - `#667 `__, - `#658 `__, - `#632 `__, - `#620 `__, - `#592 `__). - -Version 2.0.0 (May 23, 2024) ----------------------------- - -The 2.0.0 release of nanobind is entirely dedicated to *types* [#f1]_! The -project has always advertised seamless Python ↔ C++ interoperability, and this -release tries to bring a similar level of interoperability to static type -checkers like `MyPy `__, `PyRight -`__, `PyType -`__, and editors with interactive -autocompletion like `Visual Studio Code `__, -`PyCharm `__, and many other `LSP -`__-compatible IDEs. - -This required work on three fronts: - -1. **Stub generation**: the above tools all analyze Python code statically - without running it. Because the import mechanism of compiled extensions - depends the Python interpreter, these tools weren't able to inspect the - contents of nanobind-based extensions. - - The usual solution involves writing `stubs - `__ that expose - the module contents to static analysis tools. However, writing stubs by hand - is tedious and error-prone. - - This release adds tooling to automatically extract stubs from existing - extensions. The process is fully integrated into the CMake-based build - system and explained in a :ref:`new documentation section `. - -2. **Better default annotations**: once stubs were available, this revealed the - next problem: the default nanobind-provided function and class signatures - were too rudimentary, and this led to a user poor experience. - - The release therefore improves many builtin type caster so that they produce - more accurate type signatures. For example, the STL ``std::vector`` - caster now renders as ``collections.abc.Sequence[T]`` in stubs when it is - used as an *input*, and ``list[T]`` when it is used as part of a return - value. The :cpp:func:`nb::make_*_iterator() ` family of - functions return typed iterators, etc. - -3. **Advanced customization**: a subset of the type signatures in larger - binding projects will generally require further customization. The features - listed below aim to enable precisely this: - - * In Python, many built-in types are *generic* and can be *parameterized* (e.g., - ``list[int]``). The :cpp:class:`nb::typed\ ` wrapper - enables such parameterization within C++ (for example, the - ``int``-specialized list would be written as ``nb::typed``). :ref:`Read more `. - - * The opposite is also possible: passing :cpp:class:`nb::is_generic() - ` to the class binding constructor - - .. code-block:: cpp - - nb::class_(m, "MyType", nb::is_generic()) - - produces a *generic* type that can be parameterized in Python (e.g. - ``MyType[int]``). :ref:`Read more `. - - * The :cpp:class:`nb::sig ` annotation overrides the - signature of a function or method, e.g.: - - .. code-block:: cpp - - m.def("f", &f, nb::sig("def f(x: Foo = Foo(0)) -> None"), "docstring"); - - Each binding of an overloaded function can be customized separately. This - feature can be used to add decorators or control how default arguments are - rendered. :ref:`Read more `. - - * The :cpp:class:`nb::sig ` annotation can also override *class - signatures* in generated stubs. Stubs often take certain liberties in - deviating somewhat from the precise type signature of the underlying - implementation. For example, the following annotation adds an abstract - base class advertising that the class implements a typed iterator. - - .. code-block:: cpp - - using IntVec = std::vector; - - nb::class_(m, "IntVec", - nb::sig("class IntVec(collections.abc.Iterable[int])")); - - Nanobind can't subclass Python types, hence this declaration is - technically untrue. On the flipside, such a declaration can assist static - checkers and improve auto-completion in visual IDEs. This is fine since - these tools only perform a static analysis and never import the actual - extension. :ref:`Read more `. - - * The :cpp:struct:`nb::for_setter ` and - :cpp:struct:`nb::for_getter ` annotations enable passing - function binding annotations (e.g., signature overrides) specifically to - the setter or the getter part of a property. - - * The :cpp:class:`nb::arg("name") ` argument annotation (and - ``"name"_a`` shorthand) now have a :cpp:func:`.sig("signature") - ` member to control how a default value is rendered in the stubs - and docstrings. This provides more targeted control compared to overriding - the entire function signature. - - * Finally, nanobind's stub generator supports :ref:`pattern files - ` containing custom stub replacement rules. This catch-all - solution addresses the needs of advanced binding projects, for which the - above list of features may still not be sufficient. - -Most importantly, it was possible to support these improvements with minimal -changes to the core parts of nanobind. - -These release breaks API and ABI compatibility, requiring a new major version -according to `SemVer `__. The following changes are -noteworthy: - -* The :cpp:class:`nb::enum_\() ` binding declaration is now a - wrapper that creates either a :py:class:`enum.Enum` or :py:class:`enum.IntEnum`-derived type. - Previously, nanobind relied on a custom enumeration base class that was a - frequent source of friction for users. - - This change may break code that casts entries to integers, which now only - works for arithmetic (:py:class:`enum.IntEnum`-derived) enumerations. Replace - ``int(my_enum_entry)`` with ``my_enum_entry.value`` to work around the issue. - -* The :cpp:func:`nb::bind_vector\() ` and - :cpp:func:`nb::bind_map\() ` interfaces were found to be - severely flawed since element access (``__getitem__``) created views into the - internal state of the STL type that were not stable across subsequent - modifications. - - This could lead to unexpected changes to array elements and undefined - behavior when the underlying storage was reallocated (i.e., use-after-free). - - nanobind 2.0.0 improves these types so that they are safe to use, but this - means that element access must now copy by default, potentially making them - less convenient. The documentation of :cpp:func:`nb::bind_vector\() - ` discusses the issue at length and presents alternative - solutions. - -* The functions :cpp:func:`nb::make_iterator() `, - :cpp:func:`nb::make_value_iterator() ` and - :cpp:func:`nb::make_key_iterator() ` suffer from the same - issue as :cpp:func:`nb::bind_vector() ` explained above. - - nanobind 2.0.0 improves these operations so that they are safe to use, but - this means that iterator access must now copy by default, potentially making - them less convenient. The documentation of :cpp:func:`nb::make_iterator() - ` discusses the issue and presents alternative solutions. - -* The ``nb::raw_doc`` annotation was found to be too inflexible and was - removed in this version. - -* The ``nb::typed`` wrapper listed above actually already existed in previous - nanobind versions but was awkward to use, as it required the user to provide - a custom type formatter. This release makes the interface more convenient. - -* The ``nb::any`` placeholder to specify an unconstrained - :cpp:class:`nb::ndarray ` axis was removed. This name was given to a - new wrapper type :cpp:class:`nb::any` indicating ``typing.Any``-typed - values. - - All use of ``nb::any`` in existing code must be replaced with ``-1`` (for - example, ``nb::shape<3, nb::any, 4>`` → ``nb::shape<3, -1, 4>``). - -* :ref:`Keyword-only arguments ` are now supported, and can be - indicated using the new :cpp:struct:`nb::kw_only() ` function - annotation. (PR `#448 `__). - -* nanobind classes now permit overriding ``__new__``, in order to - support C++ singletons, caches, and other types that expose factory - functions rather than ordinary constructors. Read the section on - :ref:`customizing Python object creation ` for more details. - (PR `#473 `__). - -* When binding methods on a class ``T``, nanobind will now produce a Python - function that expects a self argument of type ``T``. Previously, it would - use the type of the member pointer to determine the Python function - signature, which could be a base of ``T``, which would create problems - if nanobind did not know about that base. - (PR `#471 `__). - -* nanobind can now handle keyword arguments that are not interned, which avoids - spurious ``TypeError`` exceptions in constructs like - ``fn(**pickle.loads(...))``. The speed of normal function calls (which - generally do have interned keyword arguments) should be unaffected. (PR `#469 - `__). - -* The ``owner=nb::handle()`` default value of the :cpp:class:`nb::ndarray - ` constructor was removed since it was bug-prone. You now have to - specify the owner explicitly. The previous default (``nb::handle()``) - continues to be a valid argument. - -* There have been some changes to the API for type casters in order to - avoid undefined behavior in certain cases. (PR `#549 - `__). - - * Type casters that implement custom cast operators must now define a - member function template ``can_cast()``, which returns false if - ``operator cast_t()`` would raise an exception and true otherwise. - ``can_cast()`` will be called only after a successful call to - ``from_python()``, and might not be called at all if the caller of - ``operator cast_t()`` can cope with a raised exception. - (Users of the ``NB_TYPE_CASTER()`` convenience macro need not worry - about this; it produces cast operators that never raise exceptions, - and therefore provides a ``can_cast()`` that always returns true.) - - * Many type casters for container types (``std::vector``, - ``std::optional``, etc) implement their ``from_python()`` methods - by delegating to another, "inner" type caster (``T`` in these examples) - that is allocated on the stack inside ``from_python()``. Container casters - implemented in this way should make two changes in order to take advantage - of the new safety features: - - * Wrap your ``flags`` (received as an argument of the outer caster's - ``from_python`` method) in ``flags_for_local_caster()`` before - passing them to ``inner_caster.from_python()``. This allows nanobind - to prevent some casts that would produce dangling pointers or references. - - * If ``inner_caster.from_python()`` succeeds, then also verify - ``inner_caster.template can_cast()`` before you execute - ``inner_caster.operator cast_t()``. A failure of - ``can_cast()`` should be treated the same as a failure of - ``from_python()``. This avoids the possibility of an exception - being raised through the noexcept ``load_python()`` method, - which would crash the interpreter. - - The previous ``cast_flags::none_disallowed`` flag has been removed; - it existed to avoid one particular source of exceptions from a cast - operator, but ``can_cast()`` now handles that problem more generally. - -* ABI version 14. - -.. rubric:: Footnote - -.. [#f1] The author of this library had somewhat of a revelation after - switching to a `new editor `__ and experiencing the - benefits of interactive Python code completion and type checking for the - first time. This experience also showed how nanobind-based extension were - previously a second-class citizen in this typed world, prompting the changes - in this release. - -Version 1.9.2 (Feb 23, 2024) ----------------------------- - -* Nanobind instances can now be :ref:`made weak-referenceable ` by - specifying the :cpp:class:`nb::is_weak_referenceable ` tag - in the :cpp:class:`nb::class_\<..\> ` constructor. (PR `#335 - `__, commits `fc7709 - `__, - `3562f6 `__). - -* Added a :cpp:class:`nb::bool_ ` wrapper type. (PR `#382 - `__, commit `90dfba - `__). - -* Ensure that the GIL is held when releasing :cpp:class:`nb::ndarray - `. (issue `#377 `__, - commit `a968e8 - `__). - -* :cpp:func:`nb::try_cast() ` no longer crashes the interpreter when - attempting to cast a Python ``None`` to a C++ type that was bound using - :cpp:class:`nb::class_\<...\> `. Previously this would raise an - exception from the cast operator, which would result in a call to - ``std::terminate()`` because :cpp:func:`try_cast() ` is declared - ``noexcept``. (PR `#386 `__). - -* Fixed memory corruption in a PyPy-specific code path in - :cpp:func:`nb::module_::def_submodule() ` (commit - `21eaff - `__). - -* Don't implicitly convert complex to non-complex nd-arrays. (issue `#364 - `__, commit `ea2569 - `__). - -* Support for non-assignable types in the ``std::optional`` type caster (PR - `#358 `__, commit `9c9b64 - `__). - -* nanobind no longer assumes that docstrings provided to function binding (of - type ``const char *``) have an infinite lifetime and it makes copy. (issue - `#393 `__, commit `b3b6f4 - `__). - -* Don't pass compiler flags if they may be unsupported by the used compiler. - This gets NVCC to work out of the box (that said, this change does not - elevate NVCC to being an *officially* supported compiler). (issue `#383 - `__, commit `a307ea - `__). - -* Added a CMake install target to the nanobind build system. (PR `#356 - `__, commit `6bde65 - `__, - commit `978dbb - `__, - commit `f5d8de - `__). - -* ABI version 13. - -* Minor fixes and improvements. - -Version 1.9.0-1.9.1 (Feb 18, 2024) ----------------------------------- - -Releases withdrawn because of a regression. The associated changes are -listed above in the 1.9.2 release notes. - -Version 1.8.0 (Nov 2, 2023) ---------------------------- - -* nanobind now considers two C++ ``std::type_info`` instances to be equal when - their mangled names match. The previously used pointer comparison was fast - but fragile and often caused multi-part extensions to not recognize each - other's types. This version introduces a two-level caching scheme (search by - pointer, then by name) to fix such problems once and for all, while avoiding - the cost of constantly comparing very long mangled names. (commit `b515b1 - `__). - -* Fixed casting of complex-valued constant :cpp:class:`nb::ndarray\ - ` instances. (PR `#338 - `__, commit `ba8c7f - `__). - -* Added a type caster for ``std::nullopt_t`` (PR `#350 - `__). - -* Added the missing C++ → Python portion of the type caster for - ``Eigen::Ref<..>`` (PR `#334 - `__). - -* Minor fixes and improvements. - -* ABI version 12. - - -Version 1.7.0 (Oct 19, 2023) ----------------------------- - -New features -^^^^^^^^^^^^ - -* The nd-array class :cpp:class:`nb::ndarray\ ` now supports - complex-valued ``T`` (e.g., ``std::complex``). For this, the header - file ``nanobind/stl/complex.h`` must be included. (PR `#319 - `__, commit `6cbd13 - `__). - -* Added the function :cpp:func:`nb::del() `, which takes an arbitrary - accessor object as input and tries to delete the associated entry. - The C++ statement - - .. code-block:: cpp - - nb::del(o[key]); - - is equivalent to ``del o[key]`` in Python. (commit `4dd745 - `__). - -* Exposed several convenience functions for raising exceptions as public API: - :cpp:func:`nb::raise `, :cpp:func:`nb::raise_type_error - `, and :cpp:func:`nb::raise_python_error - `. (commit `0b7f3b - `__). - -* Added :cpp:func:`nb::globals() `. (PR `#311 - `__, commit `f0a9eb - `__). - -* The ``char*`` type caster now accepts ``nullptr`` and converts it into a - Python ``None`` object. (PR `#318 - `__, commit `30a6ba - `__). - -* Added the function :cpp:func:`nb::is_alive() `, which returns - ``false`` when nanobind was destructed by Python (e.g., during interpreter - shutdown) making further use of the API illegal. (commit `b431d0 - `__). - -* Minor fixes and improvements. - -* ABI version 11. - -Bugfixes -^^^^^^^^ - -* The behavior of the :cpp:class:`nb::keep_alive\ - ` function binding annotation was changed as follows: when the - function call requires the implicit conversion of an argument, the lifetime - constraint now applies to the newly produced argument instead of the original - object. The change was rolled into a minor release since the former behavior - is arguably undesirable and dangerous. (commit `9d4b2e - `__). - -* STL type casters previously raised an exception when casting a Python container - containing a ``None`` element into a C++ container that was not able to - represent ``nullptr`` (e.g., ``std::vector`` instead of - ``std::vector``). However, this exception was raised in a context where - exceptions were not allowed, causing the process to be ``abort()``-ed, which - is very bad. This issue is now fixed, and such conversions are refused. (PR - `#318 `__, commits `d1ad3b - `__ - and `5f25ae - `__). - -* The STL sequence casters (``std::vector``, etc.) now refuse to unpack - ``str`` and ``bytes`` objects analogous to pybind11. (commit `7e4a88 - `__). - - -Version 1.6.2 (Oct 3, 2023) ---------------------------- - -* Added a missing include file used by the new intrusive reference counting - sample implementation from v1.6.0. (commit `31d115 - `__). - -Version 1.6.1 (Oct 2, 2023) ---------------------------- - -* Added missing namespace declaration to the :cpp:class:`ref` intrusive - reference counting RAII helper class added in version 1.6.0. (commit `3ba352 - `__). - - -Version 1.6.0 (Oct 2, 2023) ---------------------------- - -New features -^^^^^^^^^^^^ - -* Several :cpp:class:`nb::ndarray\<..\> ` improvements: - - 1. CPU loops involving nanobind nd-arrays weren't getting properly vectorized. - This release of nanobind adds *views*, which provide an efficient - abstraction that enables better code generation. See the documentation - section on :ref:`array views ` for details. - (commit `8f602e - `__). - - 2. Added support for nonstandard arithmetic types (e.g., ``__int128`` or - ``__fp16``) in nd-arrays. See the :ref:`documentation section - ` for details. (commit `49eab2 - `__). - - 3. Shape constraints like :cpp:class:`nb::shape\ - ` are tedious to write. Now, there is a shorter form: - :cpp:class:`nb::ndim\<3\> `. (commit `1350a5 - `__). - - 4. Added an explicit constructor that can be used to add or remove nd-array - constraints. (commit `a1ac207 - `__). - -* Added the wrapper class :cpp:class:`nb::weakref `. (commit `78887f - `__). - -* Added the methods :cpp:func:`nb::dict::contains() ` and - :cpp:func:`nb::mapping::contains() ` to the Python type - wrappers. (commit `64d87a - `__). - -* Added :cpp:func:`nb::exec() ` and :cpp:func:`nb:eval() `. (PR `#299 - `__). - -* Added a type caster for ``std::complex``. (PR `#292 - `__, commit `dcbed4 - `__). - -* Added an officially supported sample implementation of :ref:`intrusive - reference counting ` via the :cpp:class:`intrusive_counter` - :cpp:class:`intrusive_base`, and :cpp:class:`ref` classes. (commit `3fa1af - `__). - -Bugfixes -^^^^^^^^ - -* Fixed a serious issue involving combinations of bound types (e.g., ``T``) and - type casters (e.g., ``std::vector``), where nanobind was too aggressive in - its use of *move semantics*. Calling a bound function from Python taking such - a list (e.g., ``f([t1, t2, ..])``) would destruct ``t1, t2, ..`` if the type - ``T`` exposed a move constructor, which is highly non-intuitive and no - longer happens as of this fix. - - Further investigation also revealed inefficiencies in the previous - implementation where moves were actually possible but not done (e.g., for - functions taking an STL vector by value). Some binding projects may see - speedups as a consequence of this change. (issue `#307 - `__, commit `122015 - `__). - - -Version 1.5.2 (Aug 24, 2023) ----------------------------- - -* Fixed a severe issue with inheritance of the ``Py_TPFLAGS_HAVE_GC`` flag - affecting classes that derive from other classes with a - :cpp:class:`nb::dynamic_attr ` annotation. (issue `#279 - `__, commit `dbedad - `__). -* Implicit conversion of nd-arrays to conform to contiguity constraints such as - :cpp:class:`c_contig` and :cpp:class:`f_contig` previously failed in some - cases that are now addressed. (issue `#278 - `__ commit `ed929b - `__). - -Version 1.5.1 (Aug 23, 2023) ----------------------------- - -* Fixed serious reference counting issue introduced in nanobind version 1.5.0, - which affected the functions :cpp:func:`python_error::traceback()` and - :cpp:func:`python_error::what()`, causing undefined behavior via - use-after-free. Also addressed an unrelated minor UB sanitizer warning. - (issue `#277 `__, commits - `30d30c - `__ - and `c48b18 - `__). -* Extended the internal data structure tag so that it isolates different MSVC - versions from each other (they are often not ABI compatible, see pybind11 - issue `#4779 `__). This means - that nanobind 1.5.1 effectively bumps the ABI version to "10.5" when - compiling for MSVC, and the internals will be isolated from extensions built - with nanobind v1.5.0 or older. (commit `c7f3cd - `__). -* Incorporated fixes so that nanobind works with PyPy 3.10. (commits `fb5508 - `__ - and `2ed10a - `__). -* Fixed type caster for ``std::vector``. (PR `#256 - `__). -* Fixed compilation in debug mode on MSVC. (PR `#253 - `__). - -Version 1.5.0 (Aug 7, 2023) ---------------------------- - -* Support for creating :ref:`chained exceptions ` via the - :cpp:func:`nb::raise_from() ` and :cpp:func:`nb::chain_error() - ` functions. (commits `041520 - `__ - and `beb699 - `__). -* Many improvements to the handling of return value policies in - :cpp:class:`nb::ndarray\<..\> ` to avoid unnecessary copies. (commit `ffd22b - `__, - `a79575 - `__, - and `6f0c3f - `__). -* The :cpp:class:`nb::ndarray\<..\> ` class now has an additional - convenience constructor that takes the shape and (optionally) strides using - ``std::initializer_list``. (commit `de1117 - `__). -* Added a non-throwing function :cpp:func:`nb::try_cast() ` as an - alternative to :cpp:func:`nb::cast() `. (commit `6ca852 - `__). -* The ``nb::list`` and ``nb::tuple`` default constructors now construct an empty list/tuple instead - of an invalid null-initialized handle. - (commit `506185 `__) -* New low-level interface for wrapping existing C++ instances via - :cpp:func:`nb::inst_take_ownership() ` - :cpp:func:`nb::inst_reference() `. Also added convenience - functions to replace the contents of an instance with that of another. - :cpp:func:`nb::inst_replace_copy() ` along with - :cpp:func:`nb::inst_replace_move() ` (commit `1c462d - `__). -* Added a low-level abstraction around :cpp:func:`nb::type_get_slot() - ` around ``PyType_GetSlot``, but with more consistent behavior - across Python versions. (commit `d555e9 - `__). -* The :cpp:func:`nb::list::append() ` method now performs perfect - forwarding. (commit `2219d0 - `__). -* Inference of ``automatic*`` return value policy was entirely moved to the - base C++ class type caster. (commit `1ff9df - `__). -* Switch to the new Python 3.12 error status API if available. (commit `36751c - `__). -* Various minor fixes and improvements. -* ABI version 10. - -Version 1.4.0 (June 8, 2023) ----------------------------- - -* Improved the efficiency of the function dispatch loop. (PR `#227 - `__). -* Significant improvements to the Eigen type casters (generalized stride - handling to avoid unnecessary copies, support for conversion via - ``nb::cast()``, many refinements to the ``Eigen::Ref`` interface). (PR - `#215 `__). -* Added a ``NB_DOMAIN`` parameter to :cmake:command:`nanobind_add_module` which - can isolate extensions from each other to avoid binding clashes. See the - associated :ref:`FAQ entry ` for details. (commit `977119 - `__). -* Reduced the severity of nanobind encountering a duplicate type binding - (commits `f3b0e6 - `__, - and `2c9124 - `__). -* Support for pickling/unpickling nanobind objects. (commit `59843e - `__). -* ABI version 9. - -Version 1.3.2 (June 2, 2023) ----------------------------- - -* Fixed compilation on 32 bit processors (only ``i686`` tested so far). - (PR `#224 `__). -* Fixed compilation on PyPy 3.8. (commit `cd8135 - `__). -* Reduced binary bloat of musllinux wheels. (commit `f52513 - `__). - -Version 1.3.1 (May 31, 2023) ----------------------------- - -* CMake build system improvements for stable ABI wheel generation. - (PR `#222 `__). - -Version 1.3.0 (May 31, 2023) ----------------------------- - -This is a big release. The sections below cover added features, efficiency -improvements, and miscellaneous fixes and improvements. - -New features -^^^^^^^^^^^^ -* nanobind now supports binding types that inherit from - ``std::enable_shared_from_this``. See the :ref:`advanced section - on object ownership ` for more details. - (PR `#212 `__). -* Added a type caster between Python ``datetime``/``timedelta`` objects and - C++ ``std::chrono::duration``/``std::chrono::time_point``, ported - from pybind11. (PR `#175 `__). -* The :cpp:class:`nb::ndarray\<..\> ` class can now use the buffer - protocol to receive and return arrays representing read-only memory. (PR - `#217 `__). -* Added :cpp:func:`nb::python_error::discard_as_unraisable() - ` as a wrapper around - ``PyErr_WriteUnraisable()``. (PR `#175 - `__). - -Efficiency improvements: -^^^^^^^^^^^^^^^^^^^^^^^^ - -* Reduced the per-instance overhead of nanobind by 1 pointer and simplified the - internal hash table types to crunch ``libnanobind``. (commit `de018d - `__). -* Supplemental type data specified via :cpp:class:`nb::supplement\() - ` is now stored directly within the type object instead of being - referenced through an indirection. (commit `d82ca9 - `__). -* Reduced the number of exception-related exports to further crunch - ``libnanobind``. (commit `763962 - `__). -* Reduced the size of nanobind type objects by 5 pointers. (PR `#194 - `__, `#195 - `__, and commit `d82ca9 - `__). -* Internal nanobind types (``nb_type``, ``nb_static_property``, ``nb_ndarray``) - are now constructed on demand. This reduces the size of the ``libnanobind`` - component in static (``NB_STATIC``) builds when those features are not used. - (commits `95e45a - `__, - `375083 - `__, - and `e033c8 - `__). -* Added a small function cache to improve code generation in limited API - builds. (commit `f0f4aa - `__). -* Refined compiler and linker flags across platforms to ensure compact binaries - especially in ``NB_STATIC`` builds. (commit `5ead9f - `__) -* nanobind enums now take advantage of :ref:`supplemental data ` - to improve the speed of object and name lookups. Note that this prevents - use of ``nb::supplement()`` with enums for other purposes. - (PR `#195 `__). - -Miscellaneous fixes and improvements -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -* Use the new `PEP-697 `__ interface to - access data in type objects when compiling stable ABI3 wheels. This improves - forward compatibility (the Python team may at some point significantly - refactor the layout and internals of type objects). (PR `#211 - `__): -* Added introspection attributes ``__self__`` and ``__func__`` to nanobind - bound methods, to make them more like regular Python bound methods. - Fixed a bug where ``some_obj.method.__call__()`` would behave differently - than ``some_obj.method()``. - (PR `#216 `__). -* Updated the implementation of :cpp:class:`nb::enum_ ` so it does - not take advantage of any private nanobind type details. As a side effect, - the construct ``nb::class_(..., nb::is_enum(...))`` is no longer permitted; - use ``nb::enum_(...)`` instead. - (PR `#195 `__). -* Added the :cpp:class:`nb::type_slots_callback` class binding annotation, - similar to :cpp:class:`nb::type_slots` but allowing more dynamic choices. - (PR `#195 `__). -* nanobind type objects now treat attributes specially whose names - begin with ``@``. These attributes can be set once, but not - rebound or deleted. This safeguard allows a borrowed reference to - the attribute value to be safely stashed in the type supplement, - allowing arbitrary Python data associated with the type to be accessed - without a dictionary lookup while keeping this data visible to the - garbage collector. (PR `#195 `__). -* Fixed surprising behavior in enumeration comparisons and arithmetic - (PR `#207 `__): - - * Enum equality comparisons (``==`` and ``!=``) now can only be true - if both operands have the same enum type, or if one is an enum and - the other is an ``int``. This resolves some confusing - results and ensures that enumerators of different types have a - distinct identity, which is important if they're being put into - the same set or used as keys in the same dictionary. All of the - following were previously true but will now evaluate as false: - - * ``FooEnum(1) == BarEnum(1)`` - * ``FooEnum(1) == 1.2`` - * ``FooEnum(1) == "1"`` - - * Enum ordering comparisons (``<``, ``<=``, ``>=``, ``>``) and - arithmetic operations (when using the :cpp:struct:`is_arithmetic` - annotation) now require that any non-enum operand be a Python number - (an object that defines ``__int__``, ``__float__``, and/or ``__index__``) - and will avoid truncating non-integer operands to integers. Note that - unlike with equality comparisons, ordering and arithmetic operations - *do* still permit two operands that are enums of different types. - Some examples of changed behavior: - - * ``FooEnum(1) < 1.2`` is now true (used to be false) - * ``FooEnum(2) * 1.5`` is now 3.0 (used to be 2) - * ``FooEnum(3) - "2"`` now raises an exception (used to be 1) - - * Enum comparisons and arithmetic operations with unsupported types - now return `NotImplemented` rather than raising an exception. - This means equality comparisons such as ``some_enum == None`` will - return unequal rather than failing; order comparisons such as - ``some_enum < None`` will still fail, but now with a more - informative error. - -* ABI version 8. - -Version 1.2.0 (April 24, 2023) ------------------------------- - -* Improvements to the internal C++ → Python instance map data structure to improve - performance and address type confusion when returning previously registered instances. - (commit `716354 `__, - discussion `189 `__). -* Added up-to-date nanobind benchmarks on Linux including comparisons to Cython. - (commit `834cf3 - `__ - and `39e163 - `__). -* Removed the superfluous ``nb_enum`` metaclass. - (commit `9c1985 `__). -* Fixed a corner case that prevented ``nb::cast`` from working. - (commit `9ae320 `__). - -Version 1.1.1 (April 6, 2023) ------------------------------ - -* Added documentation on packaging and distributing nanobind modules. (commit - `0715b2 - `__). -* Made the conversion :cpp:func:`handle::operator bool() ` explicit. (PR `#173 `__). -* Support :cpp:class:`nb::typed\<..\> ` in return values. (PR `#174 - `__). -* Tweaks to definitions in ``nb_types.h`` to improve compatibility with further - C++ compilers (that said, there is no change about the official set of - supported compilers). (commit `b8bd10 - `__) - -Version 1.1.0 (April 5, 2023) ------------------------------ - -* Added :cpp:func:`size `, :cpp:func:`shape_ptr - `, :cpp:func:`stride_ptr ` members - to to the :cpp:class:`nb::ndarray\<..\> ` class. (PR `#161 - `__). -* Allow macros in :c:macro:`NB_MODULE(..) ` name parameter. (PR - `#168 `__). -* The :cpp:class:`nb::ndarray\<..\> ` interface is more tolerant when - converting Python (PyTorch/NumPy/..) arrays with a size-0 dimension that have - mismatched strides. (PR `#162 - `__). -* Removed the ```` label from docstrings of anonymous functions, - which caused issues in MyPy. (PR `#172 - `__). -* Fixed an issue in the propagation of return value policies that broke - user-provided/custom policies in properties (PR `#170 - `__). -* The Eigen interface now converts 1x1 matrices to 1x1 NumPy arrays instead of - scalars. (commit `445781 - `__). -* The ``nanobind`` package now has a simple command line interface. (commit - `d5ccc8 - `__). - -Version 1.0.0 (March 28, 2023) ------------------------------- - -* Nanobind now has a logo. (commit `b65d31 - `__). -* Fixed a subtle issue involving function/method properties and the IPython - command line interface. (PR `#151 - `__). -* Added a boolean type to the :cpp:class:`nb::ndarray\<..\> ` - interface. (PR `#150 `__). -* Minor fixes and improvements. - - -Version 0.3.1 (March 8, 2023) ------------------------------ - -* Added a type caster for ``std::filesystem::path``. (PR `#138 - `__ and commit `0b05cd - `__). -* Fixed technical issues involving implicit conversions (commits `022935 - `__ - and `5aefe3 - `__) - and construction of type hierarchies with custom garbage collection hooks - (commit `022935 - `__). -* Re-enabled the 'chained fixups' linker optimization for recent macOS - deployment targets. (commit `2f29ec - `__). - -Version 0.3.0 (March 8, 2023) ------------------------------ - -* Botched release, replaced by 0.3.1 on the same day. - -Version 0.2.0 (March 3, 2023) ------------------------------ -* Nanobind now features documentation on `readthedocs - `__. -* The documentation process revealed a number of inconsistencies in the - :cpp:func:`class_\::def* ` naming scheme. nanobind will from - now on use the following shortened and more logical interface: - - .. list-table:: - :widths: 40 60 - :header-rows: 1 - - * - Type - - method - * - Methods & constructors - - :cpp:func:`.def() ` - * - Fields - - :cpp:func:`.def_ro() `, - :cpp:func:`.def_rw() ` - * - Properties - - :cpp:func:`.def_prop_ro() `, - :cpp:func:`.def_prop_rw() ` - * - Static methods - - :cpp:func:`.def_static() ` - * - Static fields - - :cpp:func:`.def_ro_static() `, - :cpp:func:`.def_rw_static() ` - * - Static properties - - :cpp:func:`.def_prop_ro_static() `, - :cpp:func:`.def_prop_rw_static() ` - - Compatibility wrappers with deprecation warnings were also added to help port - existing code. They will be removed when nanobind reaches version 1.0. - (commits `cb0dc3 - `__ - and `b5ed96 - `__) -* The ``nb::tensor<..>`` class has been renamed to :cpp:class:`nb::ndarray\<..\> `, - and it is now located in a different header file (``nanobind/ndarray.h``). A - compatibility wrappers with a deprecation warning was retained in the - original header file. It will be removed when nanobind reaches version 1.0. - (commit `a6ab8b - `__). -* Dropped the first two arguments of the :c:macro:`NB_OVERRIDE_*() - ` macros that turned out to be unnecessary in nanobind. (commit - `22bc21 - `__). -* Added casters for dense matrix/array types from the `Eigen library - `__. (PR `#120 - `__). -* Added casters for sparse matrix/array types from the `Eigen library - `__. (PR `#126 - `_). -* Implemented `nb::bind_vector\() ` analogous to similar - functionality in pybind11. (commit `f2df8a - `__). -* Implemented :cpp:func:`nb::bind_map\() ` analogous to - similar functionality in pybind11. (PR `#114 - `__). -* nanobind now :ref:`automatically downcasts ` - polymorphic objects in return values analogous to pybind11. (commit `cab96a - `__). -* nanobind now supports :ref:`tag-based polymorphism `. - (commit `6ade94 - `__). -* Updated tuple/list iterator to satisfy the ``std::forward_iterator`` concept. - (PR `#117 `__). -* Fixed issues with non-writeable tensors in NumPy. (commit `25cc3c - `__). -* Removed use of some C++20 features from the codebase. This now makes it - possible to use nanobind on Visual Studio 2017 and GCC 7.3.1 (used on RHEL 7). - (PR `#115 `__). -* Added the :cpp:class:`nb::typed\<...\> ` wrapper to override the type signature of an - argument in a bound function in the generated docstring. (commit `b3404c4 - `__). -* Added an :cpp:func:`nb::implicit_convertible\() ` function analogous to the one in - pybind11. (commit `aba4af - `__). -* Updated :cpp:func:`nb::make_*_iterator\<..\>() ` so that it returns references of elements, not - copies. (commit `8916f5 - `__). -* Changed the CMake build system so that the library component - (``libnanobind``) is now compiled statically by default. (commit `8418a4 - `__). -* Switched shared library linking on macOS back to a two-level namespace. - (commit `fe4965 - `__). -* Various minor fixes and improvements. -* ABI version 7. - -Version 0.1.0 (January 3, 2023) -------------------------------- - -* Allow nanobind methods on non-nanobind) classes. (PR `#104 - `__). -* Fix dangling `tp_members` pointer in type initialization. (PR `#99 - `__). -* Added a runtime setting to suppress leak warnings. (PR `#109 - `__). -* Added the ability to hash ``nb::enum_<..>`` instances (PR `#106 - `__). -* Fixed the signature of ``nb::enum_<..>::export_values()``. (commit `714d17 - `__). -* Double-check GIL status when performing reference counting operations in - debug mode. (commit `a1b245 - `__). -* Fixed a reference leak that occurred when module initialization fails. - (commit `adfa9e - `__). -* Improved robustness of ``nb::tensor<..>`` caster. (commit `633672 - `__). -* Upgraded the internally used ``tsl::robin_map<>`` hash table to address a - rare `overflow issue `__ - discovered in this codebase. (commit `3b81b1 - `__). -* Various minor fixes and improvements. -* ABI version 6. - -Version 0.0.9 (Nov 23, 2022) ----------------------------- - -* PyPy 7.3.10 or newer is now supported subject to `certain limitations - `__. (commits - `f935f93 - `__ - and `b343bbd - `__). -* Three changes that reduce the binary size and improve runtime performance of - binding libraries. (commits `07b4e1fc - `__, - `9a803796 - `__, - and `cba4d285 - `__). -* Fixed a reference leak in ``python_error::what()`` (commit `61393ad - `__). -* Adopted a new policy for function type annotations. (commit `c855c90 `__). -* Improved the effectiveness of link-time-optimization when building extension modules - with the ``NB_STATIC`` flag. This leads to smaller binaries. (commit `f64d2b9 - `__). -* Nanobind now relies on standard mechanisms to inherit the ``tp_traverse`` and - ``tp_clear`` type slots instead of trying to reimplement the underlying - CPython logic (commit `efa09a6b - `__). -* Moved nanobind internal data structures from ``builtins`` to Python - interpreter state dictionary. (issue `#96 - `__, commit `ca23da7 - `__). -* Various minor fixes and improvements. - - -Version 0.0.8 (Oct 27, 2022) ----------------------------- - -* Caster for ``std::array<..>``. (commit `be34b16 - `__). -* Caster for ``std::set<..>`` and ``std::unordered_set`` (PR `#87 - `__). -* Ported ``nb::make[_key_,_value]_iterator()`` from pybind11. (commit `34d0be1 - `__). -* Caster for untyped ``void *`` pointers. (commit `6455fff - `__). -* Exploit move constructors in ``nb::class_::def_readwrite()`` and - ``nb::class_::def_readwrite_static()`` (PR `#94 - `__). -* Redesign of the ``std::function<>`` caster to enable cyclic garbage collector - traversal through inter-language callbacks (PR `#95 - `__). -* New interface for specifying custom type slots during Python type - construction. (commit `38ba18a - `__). -* Fixed potential undefined behavior related to ``nb_func`` garbage collection by - Python's cyclic garbage collector. (commit `662e1b9 - `__). -* Added a workaround for spurious reference leak warnings caused by other - extension modules in conjunction with ``typing.py`` (commit `5e11e80 - `__). -* Various minor fixes and improvements. -* ABI version 5. - -Version 0.0.7 (Oct 14, 2022) ----------------------------- - -* Fixed a regression involving function docstrings in ``pydoc``. (commit - `384f4a - `__). - -Version 0.0.6 (Oct 14, 2022) ----------------------------- - -* Fixed undefined behavior that could lead to crashes when nanobind types were - freed. (commit `39266e - `__). -* Refactored nanobind so that it works with ``Py_LIMITED_API`` (PR `#37 `__). -* Dynamic instance attributes (PR `#38 `__). -* Intrusive pointer support (PR `#43 `__). -* Byte string support (PR `#62 `__). -* Casters for ``std::variant<..>`` and ``std::optional<..>`` (PR `#67 `__). -* Casters for ``std::map<..>`` and ``std::unordered_map<..>`` (PR `#73 `__). -* Caster for ``std::string_view<..>`` (PR `#68 `__). -* Custom exception support (commit `41b7da `__). -* Register nanobind functions with Python's cyclic garbage collector (PR `#86 `__). -* Various minor fixes and improvements. -* ABI version 3. - -Version 0.0.5 (May 13, 2022) ----------------------------- - -* Enumeration export. -* Implicit number conversion for NumPy scalars. -* Various minor fixes and improvements. - -Version 0.0.4 (May 13, 2022) ----------------------------- - -* Botched release, replaced by 0.0.5 on the same day. - -Version 0.0.3 (Apr 14, 2022) ----------------------------- - -* DLPack support. -* Iterators for various Python type wrappers. -* Low-level interface to instance creation. -* Docstring generation improvements. -* Various minor fixes and improvements. - -Version 0.0.2 (Mar 10, 2022) ----------------------------- - -* Initial release of the nanobind codebase. -* ABI version 1. - -Version 0.0.1 (Feb 21, 2022) ----------------------------- - -* Placeholder package on PyPI. diff --git a/src/nanobind/docs/classes.rst b/src/nanobind/docs/classes.rst deleted file mode 100644 index fb6fc8a..0000000 --- a/src/nanobind/docs/classes.rst +++ /dev/null @@ -1,1143 +0,0 @@ -.. _classes: - -.. cpp:namespace:: nanobind - -Classes -======= - -The material below builds on the section on :ref:`binding custom types -` and reviews advanced scenarios involving object-oriented code. - -Frequently used ---------------- -Click on the following :cpp:class:`nb::class_\<..\>::def_* ` members for -examples on how to bind various different kinds of methods, fields, etc. - -.. list-table:: - :widths: 40 60 - :header-rows: 1 - - * - Type - - method - * - Methods & constructors - - :cpp:func:`.def() ` - * - Fields - - :cpp:func:`.def_ro() `, - :cpp:func:`.def_rw() ` - * - Properties - - :cpp:func:`.def_prop_ro() `, - :cpp:func:`.def_prop_rw() ` - * - Static methods - - :cpp:func:`.def_static() ` - * - Static fields - - :cpp:func:`.def_ro_static() `, - :cpp:func:`.def_rw_static() ` - * - Static properties - - :cpp:func:`.def_prop_ro_static() `, - :cpp:func:`.def_prop_rw_static() ` - -.. _inheritance: - -Subclasses ----------- - -Consider the following two data structures with an inheritance relationship: - -.. code-block:: cpp - - struct Pet { - std::string name; - }; - - struct Dog : Pet { - std::string bark() const { return name + ": woof!"; } - }; - -To indicate the inheritance relationship to nanobind, specify the C++ base -class as an extra template parameter of :cpp:class:`nb::class_\<..\> `: - -.. code-block:: cpp - :emphasize-lines: 8 - - #include - - NB_MODULE(my_ext, m) { - nb::class_(m, "Pet") - .def(nb::init()) - .def_rw("name", &Pet::name); - - nb::class_(m, "Dog") - .def(nb::init()) - .def("bark", &Dog::bark); - } - -Alternatively, you can also pass the type object as an ordinary parameter. - -.. code-block:: cpp - :emphasize-lines: 5 - - auto pet = nb::class_(m, "Pet") - .def(nb::init()) - .def_rw("name", &Pet::name); - - nb::class_(m, "Dog", pet /* <- Parent type object */) - .def(nb::init()) - .def("bark", &Dog::bark); - -Instances expose fields and methods of both types as expected: - -.. code-block:: pycon - - >>> d = my_ext.Dog("Molly") - >>> d.name - 'Molly' - >>> d.bark() - 'Molly: woof!' - -.. _automatic_downcasting: - -Automatic downcasting ---------------------- - -nanobind obeys signatures when returning regular non-polymorphic C++ objects -from functions: building on the :ref:`previous example `, consider -the following function that returns a ``Dog`` object as a ``Pet`` base pointer. - -.. code-block:: cpp - - m.def("pet_store", []() { return (Pet *) new Dog{"Molly"}; }); - -nanobind cannot safely determine that this is in fact an instance of the -``Dog`` subclass. Consequently, only fields and methods of the base type remain -accessible: - -.. code-block:: pycon - - >>> p = my_ext.pet_store() - >>> type(p) - - >>> p.bark() - AttributeError: 'Pet' object has no attribute 'bark' - -In C++, a type is only considered `polymorphic -`_ if it (or one of its base -classes) has at least one *virtual function*. Let's add a virtual default -destructor to make ``Pet`` and its subtypes polymorphic. - -.. code-block:: cpp - - struct Pet { - virtual ~Pet() = default; - std::string name; - }; - -With this change, nanobind is able to inspect the returned C++ instance's -`virtual table `_ and infer -that it can be represented by a more specialized Python object of type -``my_ext.Dog``. - -.. code-block:: pycon - - >>> p = my_ext.pet_store() - >>> type(p) - - >>> p.bark() - 'Molly: woof!' - -.. note:: - - Automatic downcasting of polymorphic instances is only supported when the - subtype has been registered using :cpp:class:`nb::class_\<..\> `. - Otherwise, the return type listed in the function signature takes - precedence. - -.. _overloaded_methods: - -Overloaded methods ------------------- - -Sometimes there are several overloaded C++ methods with the same name taking -different kinds of input arguments: - -.. code-block:: cpp - - struct Pet { - Pet(const std::string &name, int age) : name(name), age(age) { } - - void set(int age_) { age = age_; } - void set(const std::string &name_) { name = name_; } - - std::string name; - int age; - }; - -Attempting to bind ``Pet::set`` will cause an error since the compiler does not -know which method the user intended to select. We can disambiguate by casting -them to function pointers. Binding multiple functions to the same Python name -automatically creates a chain of function overloads that will be tried in -sequence. - -.. code-block:: cpp - - nb::class_(m, "Pet") - .def(nb::init()) - .def("set", nb::overload_cast(&Pet::set), "Set the pet's age") - .def("set", nb::overload_cast(&Pet::set), "Set the pet's name"); - -Here, :cpp:func:`nb::overload_cast ` only requires the parameter -types to be specified, and it deduces the return type. - -.. note:: - - In cases where a function overloads by ``const``-ness, an additional - ``nb::const_`` parameter is needed to select the right overload, e.g., - ``nb::overload_cast(&Pet::get, nb::const_)``. - - To define overloaded constructors, simply declare one after the other using - the normal :cpp:class:`.def(nb::init\<...\>()) ` syntax. - -The overload signatures are also visible in the method's docstring: - -.. code-block:: pycon - - >>> help(my_ext.Pet) - class Pet(builtins.object) - | Methods defined here: - | - | __init__(...) - | __init__(self, arg0: str, arg1: int, /) -> None - | - | set(...) - | set(self, arg: int, /) -> None - | set(self, arg: str, /) -> None - | - | Overloaded function. - | - | 1. ``set(self, arg: int, /) -> None`` - | - | Set the pet's age - | - | 2. ``set(self, arg: str, /) -> None`` - | - | Set the pet's name - -The format of the docstring with a leading overload list followed by a repeated -list with details is designed to be compatible with the `Sphinx -`_ documentation generator. - -.. _enumerations_and_internal: - -Enumerations and internal types -------------------------------- - -Let's now suppose that the example class contains internal types like enumerations, e.g.: - -.. code-block:: cpp - - struct Pet { - enum Kind { - Dog = 0, - Cat - }; - - struct Attributes { - float age = 0; - }; - - Pet(const std::string &name, Kind type) : name(name), type(type) { } - - std::string name; - Kind type; - Attributes attr; - }; - -The binding code for this example looks as follows: - -.. code-block:: cpp - - nb::class_ pet(m, "Pet"); - - pet.def(nb::init()) - .def_rw("name", &Pet::name) - .def_rw("type", &Pet::type) - .def_rw("attr", &Pet::attr); - - nb::enum_(pet, "Kind") - .value("Dog", Pet::Kind::Dog) - .value("Cat", Pet::Kind::Cat) - .export_values(); - - nb::class_(pet, "Attributes") - .def(nb::init<>()) - .def_rw("age", &Pet::Attributes::age); - -To ensure that the nested types ``Kind`` and ``Attributes`` are created within -the scope of ``Pet``, the ``pet`` type object is passed as the ``scope`` -argument of the subsequent :cpp:class:`nb::enum_\ ` and -:cpp:class:`nb::class_\ ` binding declarations. The -:cpp:func:`.export_values() ` function exports the -enumeration entries into the parent scope, which should be skipped for newer -C++11-style strongly typed enumerations. - -.. code-block:: pycon - - >>> from my_ext import Pet - >>> p = Pet("Lucy", Pet.Cat) - >>> p.attr.age = 3 - >>> p.type - my_ext.Kind.Cat - >>> p.type.__name__ - 'Cat' - >>> int(p.type) - 1 - -.. note:: - - When the annotation :cpp:class:`nb::is_arithmetic() ` is - passed to :cpp:class:`nb::enum_\ `, the resulting Python type - will support arithmetic and bit-level operations (and, or, - xor, negation). The operands of these operations may be either enumerators. - When the annotation :cpp:class:`nb::is_flag() ` is passed to - :cpp:class:`nb::enum_\ `, the resulting Python type will be a class - derived from ``enum.Flag``, meaning its enumerators can be combined using bit-wise - operators in a type-safe way: the result will have the same enumeration type - as the operands, and only enumerators of the same type can be combined. - When passing both ``is_arithmetic`` and ``is_flag``, the resulting Python type - will be ``enum.IntFlag``, supporting both arithmetic and bit-wise operations. - - .. code-block:: cpp - - nb::enum_(pet, "Kind", nb::is_arithmetic()) - ... - - By default, these are omitted. - -.. _dynamic_attributes: - -Dynamic attributes ------------------- - -Native Python classes can pick up new attributes dynamically: - -.. code-block:: pycon - - >>> class Pet: - ... name = "Molly" - ... - >>> p = Pet() - >>> p.name = "Charly" # overwrite existing - >>> p.age = 2 # dynamically add a new attribute - -By default, classes exported from C++ do not support this and the only writable -attributes are the ones explicitly defined using :func:`class_::def_rw` -or :func:`class_::def_prop_rw`. - -.. code-block:: cpp - - nb::class_(m, "Pet") - .def(nb::init<>()) - .def_rw("name", &Pet::name); - -Trying to set any other attribute results in an error: - -.. code-block:: pycon - - >>> p = my_ext.Pet() - >>> p.name = "Charly" # OK, attribute defined in C++ - >>> p.age = 2 # fail - AttributeError: 'Pet' object has no attribute 'age' - -To enable dynamic attributes for C++ classes, the :class:`nb::dynamic_attr -` tag must be added to the :class:`nb::class_ ` -constructor: - -.. code-block:: cpp - - nb::class_(m, "Pet", nb::dynamic_attr()) - .def(nb::init<>()) - .def_rw("name", &Pet::name); - -Now everything works as expected: - -.. code-block:: pycon - - >>> p = my_ext.Pet() - >>> p.name = "Charly" # OK, overwrite value in C++ - >>> p.age = 2 # OK, dynamically add a new attribute - -Note that there is a small runtime cost for a class with dynamic attributes. -Not only because of the addition of an instance dictionary, but also because of -more expensive garbage collection tracking which must be activated to resolve -possible circular references. Native Python classes incur this same cost by -default, so this is not anything to worry about. By default, nanobind classes -are more efficient than native Python classes. Enabling dynamic attributes just -brings them on par. - -.. _weak_refs: - -Weak references ---------------- - -By default, nanobind instances cannot be referenced via Python's ``weakref`` -class, and attempting to do so will raise an exception. - -To support this, add the :class:`nb::is_weak_referenceable -` tag to the :class:`nb::class_ ` constructor. -Note that this will increase the size of every instance by ``sizeof(void*)`` -due to the need to store a weak reference list. - -.. code-block:: cpp - - nb::class_(m, "Pet", nb::is_weak_referenceable()); - -.. _inheriting_in_python: - -Extending C++ classes in Python -------------------------------- - -Bound C++ types can be extended within Python, which is helpful to dynamically -extend compiled code with further fields and other functionality. Bind classes -with the :cpp:class:`is_final` annotation to forbid subclassing. - -Consider the following example bindings of a ``Dog`` and ``DogHouse`` class. - -.. code-block:: cpp - - #include - - namespace nb = nanobind; - - struct Dog { - std::string name; - std::string bark() const { return name + ": woof!"; } - }; - - struct DogHouse { - Dog dog; - }; - - NB_MODULE(my_ext, m) { - nb::class_(m, "Dog") - .def(nb::init()) - .def("bark", &Dog::bark) - .def_rw("name", &Dog::name); - - nb::class_(m, "DogHouse") - .def(nb::init()) - .def_rw("dog", &DogHouse::dog); - } - -The following Python snippet creates a new ``GuardDog`` type that extends -``Dog`` with an ``.alarm()`` method. - -.. code-block:: pycon - - >>> import my_ext - >>> class GuardDog(my_ext.Dog): - ... def alarm(self, count = 3): - ... for i in range(count): - ... print(self.bark()) - ... - >>> gd = GuardDog("Max") - >>> gd.alarm() - Max: woof! - Max: woof! - Max: woof! - -This Python subclass is best thought of as a "rich wrapper" around an existing -C++ base object. By default, that wrapper will disappear when nanobind makes a -copy or transfers ownership to C++. - -.. code-block:: pycon - - >>> d = my_ext.DogHouse() - >>> d.dog = gd - >>> d.dog.alarm() - AttributeError: 'Dog' object has no attribute 'alarm' - -To preserve it, adopt a shared ownership model using :ref:`shared pointers -` or :ref:`intrusive reference counting `. For -example, updating the code as follows fixes the problem: - -.. code-block:: cpp - - #include - - struct DogHouse { - std::shared_ptr dog; - }; - -.. code-block:: pycon - - >>> d = my_ext.DogHouse() - >>> d.dog = gd - >>> d.dog.alarm() - Max: woof! - Max: woof! - Max: woof! - -.. _trampolines: - -Overriding virtual functions in Python --------------------------------------- - -Building on the previous example on :ref:`inheriting C++ types in Python -`, let's investigate how a C++ *virtual function* can be -overridden in Python. In the code below, the virtual method ``bark()`` is -called by a global ``alarm()`` function (now written in C++). - -.. code-block:: cpp - :emphasize-lines: 6 - - #include - - struct Dog { - std::string name; - Dog(const std::string &name) : name(name) { } - virtual std::string bark() const { return name + ": woof!"; } - }; - - void alarm(Dog *dog, size_t count = 3) { - for (size_t i = 0; i < count; ++i) - std::cout << dog->bark() << std::endl; - } - -Normally, the binding code would look as follows: - -.. code-block:: cpp - - #include - - namespace nb = nanobind; - using namespace nb::literals; - - NB_MODULE(my_ext, m) { - nb::class_(m, "Dog") - .def(nb::init()) - .def("bark", &Dog::bark) - .def_rw("name", &Dog::name); - - m.def("alarm", &alarm, "dog"_a, "count"_a = 3); - } - -However, this don't work as expected. We can subclass and override without -problems, but virtual function calls originating from C++ aren't being -propagated to Python: - -.. code-block:: pycon - - >>> class ShihTzu(my_ext.Dog): - ... def bark(self): - ... return self.name + ": yip!" - ... - - >>> dog = ShihTzu("Mr. Fluffles") - - >>> dog.bark() - Mr. Fluffles: yip! - - >>> my_ext.alarm(dog) - Mr. Fluffles: woof! # <-- oops, alarm() is calling the base implementation - Mr. Fluffles: woof! - Mr. Fluffles: woof! - -To fix this behavior, you must implement a *trampoline class*. A trampoline has -the sole purpose of capturing virtual function calls in C++ and forwarding them -to Python. - -.. code-block:: cpp - - #include - - struct PyDog : Dog { - NB_TRAMPOLINE(Dog, 1); - - std::string bark() const override { - NB_OVERRIDE(bark); - } - }; - -This involves an additional include directive and the line -:c:macro:`NB_TRAMPOLINE(Dog, 1) ` to mark the class as a -trampoline for the ``Dog`` base type. The count (``1``) denotes to the total -number of virtual method slots that can be overridden within Python. - -.. note:: - - The number of virtual method slots is used to preallocate memory. - Trampoline declarations with an insufficient size may eventually trigger a - Python ``RuntimeError`` exception with a descriptive label, e.g.: - - .. code-block:: text - - nanobind::detail::get_trampoline('PyDog::bark()'): the trampoline ran out of - slots (you will need to increase the value provided to the NB_TRAMPOLINE() macro) - -The macro :c:macro:`NB_OVERRIDE(bark) ` intercepts the virtual -function call, checks if a Python override exists, and forwards the call in -that case. If no override was found, it falls back to the base class -implementation. You will need to replicate this pattern for every method that -should support overriding in Python. - -The macro accepts an variable argument list to pass additional parameters. For -example, suppose that the virtual function ``bark()`` had an additional ``int -volume`` parameter---in that case, the syntax would need to be adapted as follows: - -.. code-block:: cpp - - std::string bark(int volume) const override { - NB_OVERRIDE(bark, volume); - } - -The macro :c:macro:`NB_OVERRIDE_PURE() ` should be used for -pure virtual functions, and :c:macro:`NB_OVERRIDE() ` should be -used for functions which have a default implementation. There are also two -alternate macros :c:macro:`NB_OVERRIDE_PURE_NAME() ` and -:c:macro:`NB_OVERRIDE_NAME() ` which take a string as first -argument to specify the name of function in Python. This is useful when the C++ -and Python versions of the function have different names (e.g., ``operator+`` -vs ``__add__``). - -The binding code needs a tiny adaptation (highlighted) to inform nanobind of -the trampoline that will be used whenever Python code extends the C++ class. - -.. code-block:: cpp - - nb::class_(m, "Dog") - -If the :cpp:class:`nb::class_\<..\> ` declaration also specifies a base -class, you may specify it and the trampoline in either order. Also, note that -binding declarations should be made against the actual class, not the -trampoline: - -.. code-block:: cpp - - nb::class_(m, "Dog") - .def(nb::init()) - .def("bark", &PyDog::bark); /* <--- THIS IS WRONG, use &Dog::bark */ - -With the trampoline in place, our example works as expected: - -.. code-block:: pycon - - >>> my_ext.alarm(dog) - Mr. Fluffles: yip! - Mr. Fluffles: yip! - Mr. Fluffles: yip! - -The following special case needs to be mentioned: you *may not* implement a -Python trampoline for a method that returns a reference or pointer to a -type requiring :ref:`type casting `. For example, attempting to -expose a hypothetical virtual method ``const std::string &get_name() const`` -as follows - -.. code-block:: cpp - - const std::string &get_name() const override { - NB_OVERRIDE(get_name); - } - -will fail with a static assertion failure: - -.. code-block:: text - - include/nanobind/nb_cast.h:352:13: error: static_assert failed due to requirement '...' - "nanobind::cast(): cannot return a reference to a temporary." - -This is not a fluke. The Python would return a ``str`` object that nanobind can -easily type-cast into a temporary ``std::string`` instance. However, when the -virtual function call returns on the C++ side, that temporary will already have -expired. There isn't a good solution to this problem, and nanobind therefore -simply refuses to do it. You will need to change your approach by either using -:ref:`bindings ` instead of :ref:`type casters ` or -changing your virtual method interfaces to return by value. - -.. _operator_overloading: - -Operator overloading --------------------- - -Suppose that we're given the following ``Vector2`` class with a vector addition -and scalar multiplication operation, all implemented using overloaded operators -in C++. - -.. code-block:: cpp - - class Vector2 { - public: - Vector2(float x, float y) : x(x), y(y) { } - - Vector2 operator+(const Vector2 &v) const { return Vector2(x + v.x, y + v.y); } - Vector2 operator*(float value) const { return Vector2(x * value, y * value); } - Vector2 operator-() const { return Vector2(-x, -y); } - Vector2& operator+=(const Vector2 &v) { x += v.x; y += v.y; return *this; } - Vector2& operator*=(float v) { x *= v; y *= v; return *this; } - - friend Vector2 operator*(float f, const Vector2 &v) { - return Vector2(f * v.x, f * v.y); - } - - std::string to_string() const { - return "[" + std::to_string(x) + ", " + std::to_string(y) + "]"; - } - private: - float x, y; - }; - -The following snippet shows how the above operators can be conveniently exposed -to Python. - -.. code-block:: cpp - - #include - - NB_MODULE(my_ext, m) { - nb::class_(m, "Vector2") - .def(nb::init()) - .def(nb::self + nb::self) - .def(nb::self += nb::self) - .def(nb::self *= float()) - .def(float() * nb::self) - .def(nb::self * float()) - .def(-nb::self) - .def("__repr__", &Vector2::to_string); - } - -Note that a line involving :cpp:var:`nb::self ` like - -.. code-block:: cpp - - .def(nb::self * float()) - -is really just short hand notation for - -.. code-block:: cpp - - .def("__mul__", [](const Vector2 &a, float b) { - return a * b; - }, nb::is_operator()) - -This can be useful for exposing additional operators that don't exist on the -C++ side, or to perform other types of customization. The -:cpp:class:`nb::is_operator() ` flag marker is needed to inform -nanobind that this is an operator, which returns ``NotImplemented`` when -invoked with incompatible arguments rather than throwing a type error. - -When binding *in-place* operators such as ``operator+=``, and when their -implementation is guaranteed to end with ``return *this``, it is recommended -that you set a return value policy of :cpp:enumerator:`rv_policy::none`, i.e., - -.. code-block:: cpp - - .def(nb::self += nb::self, nb::rv_policy::none) - -Otherwise, the function binding will return a new copy of the object, which is -usually not desired. - -Binding protected member functions ----------------------------------- - -It's normally not possible to expose ``protected`` member functions to Python: - -.. code-block:: cpp - - class A { - protected: - int foo() const { return 42; } - }; - - nb::class_(m, "A") - .def("foo", &A::foo); // error: 'foo' is a protected member of 'A' - -On one hand, this is good because non-``public`` members aren't meant to be -accessed from the outside. But we may want to make use of ``protected`` -functions in derived Python classes. - -The following pattern makes this possible: - -.. code-block:: cpp - - class A { - protected: - int foo() const { return 42; } - }; - - class Publicist : public A { // helper type for exposing protected functions - public: - using A::foo; // inherited with different access modifier - }; - - nb::class_(m, "A") // bind the primary class - .def("foo", &Publicist::foo); // expose protected methods via the publicist - -This works because ``&Publicist::foo`` is exactly the same function as -``&A::foo`` (same signature and address), just with a different access -modifier. The only purpose of the ``Publicist`` helper class is to make -the function name ``public``. - -If the intent is to expose ``protected`` ``virtual`` functions which can be -overridden in Python, the publicist pattern can be combined with the previously -described trampoline: - -.. code-block:: cpp - - class A { - public: - virtual ~A() = default; - - protected: - virtual int foo() const { return 42; } - }; - - class Trampoline : public A { - public: - NB_TRAMPOLINE(A, 1); - int foo() const override { NB_OVERRIDE(foo); } - }; - - class Publicist : public A { - public: - using A::foo; - }; - - nb::class_(m, "A") // <-- `Trampoline` here - .def("foo", &Publicist::foo); // <-- `Publicist` here, not `Trampoline`! - -Binding classes with template parameters ----------------------------------------- - -nanobind can also wrap classes that have template parameters. Consider these classes: - -.. code-block:: cpp - - struct Cat {}; - struct Dog {}; - - template struct PetHouse { - PetHouse(PetType& pet); - PetType& get(); - }; - -C++ templates may only be instantiated at compile time, so nanobind can only -wrap instantiated templated classes. You cannot wrap a non-instantiated template: - -.. code-block:: cpp - - // BROKEN (this will not compile) - nb::class_(m, "PetHouse"); - .def("get", &PetHouse::get); - -You must explicitly specify each template/type combination that you want to -wrap separately. - -.. code-block:: cpp - - // ok - nb::class_>(m, "CatHouse") - .def("get", &PetHouse::get); - - // ok - nb::class_>(m, "DogHouse") - .def("get", &PetHouse::get); - -If your class methods have template parameters you can wrap those as well, -but once again each instantiation must be explicitly specified: - -.. code-block:: cpp - - typename struct MyClass { - template T fn(V v); - }; - - nb::class_>(m, "MyClassT") - .def("fn", &MyClass::fn); - -.. _tag_based_polymorphism: - -Tag-based polymorphism ----------------------- - -The section on :ref:`automatic downcasting ` explained -how nanobind can infer the type of polymorphic C++ objects at runtime. It can -be desirable to extend this automatic downcasting behavior to non-polymorphic -classes, for example to support *tag-based polymorphism*. In this case, -instances expose a method or field to identify their type. - -For example, consider the following class hierarchy where ``Pet::kind`` -serves this purpose: - -.. code-block:: cpp - - #include - - namespace nb = nanobind; - - enum class PetKind { Cat, Dog }; - - struct Pet { const PetKind kind; }; - struct Dog : Pet { Dog() : Pet{PetKind::Dog} { } }; - struct Cat : Pet { Cat() : Pet{PetKind::Cat} { } }; - - namespace nb = nanobind; - - NB_MODULE(my_ext, m) { - nb::class_(m, "Pet"); - nb::class_(m, "Dog"); - nb::class_(m, "Cat"); - - nb::enum_(m, "PetKind") - .value("Cat", PetKind::Cat) - .value("Dog", PetKind::Dog); - - m.def("make_pet", [](PetKind kind) -> Pet* { - switch (kind) { - case PetKind::Dog: return new Dog(); - case PetKind::Cat: return new Cat(); - } - }); - } - -This code initially doesn't work as expected (the ``make_pet`` function binding -always creates instances of the ``Pet`` base class). - -.. code-block:: pycon - - >>> my_ext.make_pet(my_ext.PetKind.Cat) - - - >>> my_ext.make_pet(my_ext.PetKind.Dog) - - -To fix this, partially specialize the ``type_hook`` class to provide the -``type_hook::get()`` method: - -.. code-block:: cpp - - namespace nanobind::detail { - template <> struct type_hook { - static const std::type_info *get(Pet *p) { - if (p) { - switch (p->kind) { - case PetKind::Dog: return &typeid(Dog); - case PetKind::Cat: return &typeid(Cat); - } - } - return &typeid(Pet); - } - }; - } // namespace nanobind::detail - -The method will be invoked whenever nanobind needs to convert a C++ pointer of -type ``T*`` to a Python object. It should inspect the instance and return a -pointer to a suitable RTTI record. With this override, downcasting works as -expected: - -.. code-block:: pycon - - >>> my_ext.make_pet(my_ext.PetKind.Cat) - - - >>> my_ext.make_pet(my_ext.PetKind.Dog) - - -Binding unions --------------- - -:cpp:class:`nb::class_\<..\> ` can also be used to provide bindings -for `unions `__. -A basic and useless example: - -.. code-block:: cpp - - union Example { - int ival; - double dval; - - std::string to_string(size_t active_idx) const { - return active_idx == 1 ? std::to_string(dval) : std::to_string(ival); - } - }; - static_assert(sizeof(Example) == sizeof(double)); - - nb::class_(m, "Example") - .def_rw("ival", &Example::ival) - .def_rw("dval", &Example::dval) - .def("to_string", &Example::to_string); - -.. code-block:: pycon - - >>> u = my_ext.Example() - >>> u.ival = 42 - >>> u.to_string(0) - '42' - >>> u.dval = 1.25 - >>> u.to_string(1) - '1.250000' - -Direct binding of union variant members is only safe if all members of the -union are trivially copyable types (as in this example), but more complex -unions can also be supported by binding lambdas or member functions that -enforce the necessary invariants. - -This is a low-level feature and should be used with care; even when all -members are trivially copyable, reading from a union member -other than the most recently written one produces undefined behavior -in C++. Unless you need to bind an existing API that uses union types, -you're probably better off using ``std::variant<..>``, which knows what -member is active and can thus enforce all the ncessary invariants for you. - -Pickling --------- - -To pickle and unpickle objects bound using nanobind, expose the -``__getstate__`` and ``__setstate__`` methods. They should return and retrieve -the internal instance state using representations that themselves support -pickling. The example below, e.g., does this using a tuple. - -The ``__setstate__`` method should construct the object in-place analogous to -custom ``__init__``-style constructors. - -.. code-block:: cpp - - #include - - struct Pet { - std::string name; - int age; - Pet(const std::string &name, int age) : name(name), age(age) { } - }; - - NB_MODULE(my_ext, m) { - nb::class_(m, "Pet") - // ... - .def("__getstate__", [](const Pet &pet) { return std::make_tuple(pet.name, pet.age); }) - .def("__setstate__", [](Pet &pet, const std::tuple &state) { - new (&pet) Pet( - std::get<0>(state), - std::get<1>(state) - ); - }); - } - -.. _custom_new: - -Customizing Python object creation ----------------------------------- - -Sometimes you might need to bind a class that can't be constructed in the -usual way: - -.. code-block:: cpp - - class Pet { - private: - Pet(/* ... */); - public: - static std::unique_ptr make(std::string name, int age); - void speak(); - }; - -You can use :cpp:func:`.def_static() ` to -produce bindings that let you write ``Pet.make("Fido", 2)`` in Python, -just like you would write ``Pet::make("Fido", 2)`` in C++. But sometimes it's -nice to provide a more Pythonic interface than that, like ``Pet("Fido", 2)``. -To do that, nanobind lets you override ``__new__``. - -Since this is a rarely-used feature in Python, let's recap. Object -initialization in Python occurs in two phases: - -* the *constructor*, ``__new__``, allocates memory for the object; - -* the *initializer*, ``__init__``, sets up the object's initial state. - -So far, all the ways we've seen of binding C++ constructors -(:cpp:struct:`nb::init\<..\>() `, ``.def("__init__", ...)``) -produce Python object *initializers*. nanobind augments these with its own -Python object constructor, which allocates a Python object that has -space in its memory layout for the C++ object to slot in. -The ``__init__`` method then fills in that space by calling a C++ constructor. - -This split between ``__new__`` and ``__init__`` has a lot of benefits, -including a reduction in unnecessary allocations, but it does mean that -anything created from Python must be able to control where its -C++ innards are stored. Sometimes, as with the example of ``Pet`` above, -that's not feasible. In such cases, you can go down one level and override -``__new__`` directly: - -.. code-block:: cpp - - nb::class_(m, "Pet") - .def(nb::new_(&Pet::make), "name"_a, "age"_a) - .def("speak", &Pet::speak); - -Passing :cpp:struct:`nb::new_ ` to :cpp:func:`.def() ` -here creates two magic methods on ``Pet``: - -* A ``__new__`` that uses the given function to produce a new ``Pet``. - It is converted to a Python object in the same way as the return value - of any other function you might write bindings for. In particular, - you can pass a :cpp:enum:`nb::rv_policy ` as an additional - argument to :cpp:func:`.def() ` to control how this conversion - occurs. - -* A ``__init__`` that takes the same arguments as ``__new__`` but - performs no operation. This is necessary because Python automatically - calls ``__init__`` on the object returned by ``__new__`` in most cases. - -You can provide a lambda as the argument of :cpp:struct:`nb::new_ `. -This is most useful when the lambda returns a pointer or smart pointer; -if it's returning a value, then ``.def("__init__", ...)`` will have better -performance. Additionally, you can chain multiple calls to -``.def(nb::new_(...))`` in order to create an overload set. -The following example demonstrates both of these capabilities: - -.. code-block:: cpp - - nb::class_(m, "Pet") - .def(nb::new_([]() { return Pet::make(getRandomName(), 0); })) - .def(nb::new_(&Pet::make), "name"_a, "age"_a) - .def("speak", &Pet::speak); - -If you need even more control, perhaps because you need to access the -type object that Python passes as the first argument of ``__new__`` -(which :cpp:struct:`nb::new_ ` discards), you can write a -``.def_static("__new__", ...)`` and matching ``.def("__init__", ...)`` -yourself. - -Two limitations of :cpp:struct:`nb::new_ ` are worth noting: - -* The possibilities for Python-side inheritance from C++ classes that - are bound using :cpp:struct:`nb::new_ ` constructors are substantially - reduced. Simple inheritance situations (``class PyPet(Pet): ...``) should - work OK, but you can't :ref:`override virtual functions ` - in Python (because the C++ object returned by :cpp:struct:`new_ ` - doesn't contain the Python trampoline glue), and if :cpp:struct:`new_ ` - is used to implement a polymorphic factory (like if ``Pet::make()`` could - return an instance of ``Cat``) then Python-side inheritance won't work at all. - -* A given C++ class must expose all of its constructors via ``__new__`` or - all via ``__init__``, rather than a mixture of the two. - The only case where a class should bind both of these methods is - if the ``__init__`` methods are all stubs that do nothing. - This is because nanobind internally optimizes object instantiation by - caching the method that should be used for constructing instances of each - given type, and that optimization doesn't support trying both methods. - If you really need to combine nontrivial ``__new__`` and nontrivial - ``__init__`` in the same type, you can disable the optimization by - defining a :ref:`custom type slot ` of ``Py_tp_new`` or - ``Py_tp_init``. - -.. note:: - - Unpickling an object of type ``Foo`` normally requires that - ``Foo.__new__(Foo)`` produce something that ``__setstate__`` can be - called on. Any custom :cpp:struct:`nb::new_ ` methods will - not satisfy this requirement, because they return a - fully-constructed object. In order to maintain pickle - compatibility, nanobind by default will add an additional - ``__new__`` overload that takes no extra arguments and calls the nanobind - built-in :cpp:func:`inst_alloc`. This won't make your class constructible - with no arguments, because there's no corresponding ``__init__``; it - 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. diff --git a/src/nanobind/docs/conf.py b/src/nanobind/docs/conf.py deleted file mode 100644 index 3ec328a..0000000 --- a/src/nanobind/docs/conf.py +++ /dev/null @@ -1,250 +0,0 @@ -#!/usr/bin/env python3 -# - -import os -import re - -# -- General configuration ------------------------------------------------ - -# If your documentation needs a minimal Sphinx version, state it here. -# needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - "sphinx_copybutton", - "sphinxcontrib.rsvgconverter", - "sphinxcontrib.moderncmakedomain", - "sphinx.ext.intersphinx", -] - -intersphinx_mapping = { - "python": ("https://docs.python.org/3", None), -} - -# Add any paths that contain templates here, relative to this directory. -templates_path = [".templates"] - -# The suffix(es) of source filenames. -# You can specify multiple suffix as a list of string: -# source_suffix = ['.rst', '.md'] -source_suffix = ".rst" - -# The encoding of source files. -# source_encoding = 'utf-8-sig' - -# The master toctree document. -master_doc = "index" - -# General information about the project. -project = "nanobind" -copyright = "2023, Wenzel Jakob" -author = "Wenzel Jakob" - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. - -# Read the listed version -VERSION_REGEX = re.compile( - r"^\s*#\s*define\s+NB_VERSION_([A-Z]+)\s+(.*)$", re.MULTILINE) - -this_directory = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) - -with open(os.path.join(this_directory, "include/nanobind/nanobind.h")) as f: - matches = dict(VERSION_REGEX.findall(f.read())) - version = "{MAJOR}.{MINOR}.{PATCH}".format(**matches) - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# -# This is also used if you do content translation via gettext catalogs. -# Usually you set "language" from the command line for these cases. -language = "en" - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -# today = '' -# Else, today_fmt is used as the format for a strftime call. -# today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = [".build", "release.rst"] - -# The reST default role (used for this markup: `text`) to use for all -# documents. -default_role = "any" - -# If true, '()' will be appended to :func: etc. cross-reference text. -# add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -# add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -# show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -# pygments_style = 'monokai' - -# A list of ignored prefixes for module index sorting. -# modindex_common_prefix = [] - -# If true, keep warnings as "system message" paragraphs in the built documents. -# keep_warnings = False - -# If true, `todo` and `todoList` produce output, else they produce nothing. -todo_include_todos = False - - -# -- Options for HTML output ---------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. - -html_theme = "furo" - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -# html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. -# html_theme_path = [] - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -# html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -# html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -# html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -# html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -# html_static_path = ["_static"] - -# html_css_files = [ ] - -# Add any extra paths that contain custom files (such as robots.txt or -# .htaccess) here, relative to this directory. These files are copied -# directly to the root of the documentation. -# html_extra_path = [] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -# html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -# html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -# html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -# html_additional_pages = {} - -# If false, no module index is generated. -# html_domain_indices = True - -# If false, no index is generated. -# html_use_index = True - -# If true, the index is split into individual pages for each letter. -# html_split_index = False - -# If true, links to the reST sources are added to the pages. -# html_show_sourcelink = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -# html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -# html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -# html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -# html_file_suffix = None - -# Language to be used for generating the HTML full-text search index. -# Sphinx supports the following languages: -# 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja' -# 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr' -# html_search_language = 'en' - -# A dictionary with options for the search language support, empty by default. -# Now only 'ja' uses this config value -# html_search_options = {'type': 'default'} - -# The name of a javascript file (relative to the configuration directory) that -# implements a search results scorer. If empty, the default will be used. -# html_search_scorer = 'scorer.js' - -# Output file base name for HTML help builder. -htmlhelp_basename = "nanobind_doc" - -# -- Options for LaTeX output --------------------------------------------- - -latex_engine = "pdflatex" - -latex_elements = { - 'papersize': 'a4paper', - 'pointsize': '10pt', - "classoptions": ",openany,oneside", - "preamble": r""" -\usepackage{MnSymbol} -\DeclareUnicodeCharacter{25CB}{\ensuremath{\circ}} -\DeclareUnicodeCharacter{25CF}{\ensuremath{\bullet}} -\DeclareUnicodeCharacter{21B5}{\ensuremath{\rhookswarrow}} -\DeclareUnicodeCharacter{2194}{\ensuremath{\leftrightarrow}} -""", -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - (master_doc, "nanobind.tex", "nanobind Documentation", author, "manual"), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -# latex_logo = 'nanobind-logo.png' - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -# latex_use_parts = False - -# If true, show page references after internal links. -# latex_show_pagerefs = False - -# If true, show URL addresses after external links. -# latex_show_urls = False - -# Documents to append as an appendix to all manuals. -# latex_appendices = [] - -# If false, no module index is generated. -# latex_domain_indices = True - -primary_domain = "cpp" -highlight_language = "cpp" diff --git a/src/nanobind/docs/cppyy.h b/src/nanobind/docs/cppyy.h deleted file mode 100644 index 4d7e861..0000000 --- a/src/nanobind/docs/cppyy.h +++ /dev/null @@ -1,16 +0,0 @@ -float test_0000(uint16_t a, int32_t b, uint32_t c, int64_t d, uint64_t e, - float f) { - return a + b + c + d + e + f; -} - -struct Struct0 { - uint16_t a; - int32_t b; - uint32_t c; - int64_t d; - uint64_t e; - float f; - Struct0(uint16_t a, int32_t b, uint32_t c, int64_t d, uint64_t e, float f) - : a(a), b(b), c(c), d(d), e(e), f(f) {} - float sum() const { return a + b + c + d + e + f; } -}; diff --git a/src/nanobind/docs/eigen.rst b/src/nanobind/docs/eigen.rst deleted file mode 100644 index 4dcd63c..0000000 --- a/src/nanobind/docs/eigen.rst +++ /dev/null @@ -1,180 +0,0 @@ -.. cpp:namespace:: nanobind - -.. _eigen: - -The *Eigen* linear algebra library -================================== - -`Eigen `__ is a header-only C++ library for linear -algebra that offers dense and sparse matrix types along with a host of -algorithms that operate on them. Owing to its widespread use in many scientific -projects, nanobind includes custom type casters that enable bidirectional -conversion between Eigen and Python array programming libraries. - -These casters build on the previously discussed :ref:`n-dimensional array -` class. You can therefore think of this section as an easier -interface to the same features that is preferable if your project uses Eigen. - -Dense matrices and vectors --------------------------- - -Add the following include directive to your binding code to exchange dense -Eigen types: - -.. code-block:: cpp - - #include - -Following this, you should be able to bind functions that accept and return -values of type ``Eigen::Matrix<..>``, ``Eigen::Array<..>``, -``Eigen::Vector<..>``, ``Eigen::Ref<..>``, ``Eigen::Map<..>``, and their -various specializations. Unevaluated expression templates are also supported. - -nanobind may need to evaluate or copy the matrix/vector contents during type -casting, which is sometimes undesirable. The following cases explain when -copying is needed, and how it can be avoided. - -C++ → Python -^^^^^^^^^^^^ - -Consider the following C++ function returning a dense Eigen type -(``Eigen::MatrixXf`` in this example). The bound Python version of ``f()`` -returns this data in the form of a ``numpy.ndarray``. - -.. code-block:: cpp - - Eigen::MatrixXf f() { ... } - -If the C++ function returns *by value*, and when the Eigen type represents an -evaluated expression, nanobind will capture and wrap it in a NumPy array -without making a copy. All other cases (returning by reference, returning an -unevaluated expression template) either evaluate or copy the array. - -.. warning:: - - It can be tempting to bind functions that directly return Eigen expressions, - such as such the innocent-looking vector sum below: - - .. code-block:: cpp - - m.def("sum", [](Eigen::Vector3f a, Eigen::Vector3d b) { return a + b; }); - - However, note that this example triggers undefined behavior. The problem is - that the sum ``a + b`` is an *expression template*, which provides the means - to evaluate the expression at some later point. The expression references - variables on the stack that no longer exist when when the expression is - evaluated by the caller. The issue is not related to nanobind (i.e., this is - also a bug in pure Eigen code). - - To fix this you can - - 1. Specify a return type, e.g., - - .. code-block:: cpp - - m.def("sum", [](Eigen::Vector3f a, Eigen::Vector3d b) -> Eigen::Vector3d { return a + b; }); - - This forces an evaluation of the expression into a container that *owns* - the underlying storage. - - 2. Invoke ``Eigen::DenseBase::eval()``, which is equivalent and potentially - more compact and flexible. - - .. code-block:: cpp - - m.def("sum", [](Eigen::Vector3f a, Eigen::Vector3d b) { return (a + b).eval(); }); - - 3. If the expression to be returned only references function arguments, - then you can turn the arguments themselves into references: - - .. code-block:: cpp - - m.def("sum", [](const Eigen::Vector3f &a, const Eigen::Vector3d &b) { return a + b; }); - - This is safe, because the nanobind type casters keep the referenced - objects alive until the expression has been evaluated. - -Python → C++ -^^^^^^^^^^^^ - -The reverse direction is more tricky. Consider the following 3 -functions taking variations of a dense ``Eigen::MatrixXf``: - -.. code-block:: cpp - - void f1(const Eigen::MatrixXf &x) { ... } - void f2(const Eigen::Ref &x) { ... } - void f3(const nb::DRef &x) { ... } - -The Python bindings of these three functions can be called using any of a -number of different CPU-resident 2D array types (NumPy arrays, -PyTorch/Tensorflow/JAX tensors, etc.). However, the following limitations -apply: - -- ``f1()`` will always perform a copy of the array contents when called from - Python. This is because ``Eigen::MatrixXf`` is designed to *own* the - underlying storage, which is sadly incompatible with the idea of creating a - view of an existing Python array. - -- ``f2()`` very likely copies as well! This may seem non-intuitive, since - ``Eigen::Ref<..>`` exists to avoid this exact problem. - - The problem is that Eigen normally expects a very specific memory layout - (Fortran/column-major layout), while Python array frameworks actually use the - *opposite* by default (C/row-major layout). Array slices are even more - problematic and always require a copy. - -- ``f3()`` uses :cpp:type:`nb::DRef ` to support *any* memory layout - (row-major, column-major, slices) without copying. It may still perform an - implicit conversion when called with the *wrong data type*---for example, the - function expects a single precision array, but NumPy matrices often use - double precision. - - If that is undesirable, you may bind the function as follows, in which case - nanobind will report a ``TypeError`` if an implicit conversion would be - needed. - - .. code-block:: cpp - - m.def("f1", &f1, nb::arg("x").noconvert()); - - This parameter passing convention can also be used to mutate function - parameters, e.g.: - - .. code-block:: cpp - - void f4(nb::DRef x) { x *= 2; } - -Maps ----- - -Besides ``Eigen::Ref<...>``, nanobind also supports binding functions that take -and return ``Eigen::Map<...>``. The underlying map type caster strictly -prevents conversion of incompatible inputs into an ``Eigen::Map<...>`` when -this would require implicit layout or type conversion. This restriction exists -because the primary purpose of this interface is to efficiently access existing -memory without conversion overhead. When binding functions that return -``Eigen::Map<...>``, you must ensure that the mapped memory remains valid -throughout the map's lifetime. This typically requires appropriate lifetime -annotations (such as :cpp:enumerator:`rv_policy::reference_internal` or -:cpp:struct:`keep_alive`) to prevent access to memory that has been deallocated -on the C++ side. - -Sparse matrices ---------------- - -Add the following include directive to your binding code to exchange sparse -Eigen types: - -.. code-block:: cpp - - #include - -The ``Eigen::SparseMatrix<..>`` and ``Eigen::Map>`` -types map to either ``scipy.sparse.csr_matrix`` or ``scipy.sparse.csc_matrix`` -depending on whether row- or column-major storage is used. The previously -mentioned precautions related to returning dense maps also apply in the sparse -case. - -There is no support for Eigen sparse vectors because an equivalent type does -not exist as part of ``scipy.sparse``. diff --git a/src/nanobind/docs/exceptions.rst b/src/nanobind/docs/exceptions.rst deleted file mode 100644 index 3e97df6..0000000 --- a/src/nanobind/docs/exceptions.rst +++ /dev/null @@ -1,291 +0,0 @@ -.. cpp:namespace:: nanobind - -.. _exceptions: - -Exceptions -========== - -.. _exception_conversion: - -Automatic conversion of C++ exceptions --------------------------------------- - -When Python calls a C++ function, that function might raise an exception -instead of returning a result. In such a case, nanobind will capture the C++ -exception and then raise an equivalent exception within Python. This automatic -conversion supports ``std::exception``, common subclasses, and several classes -that convert to specific Python exceptions as shown below: - -.. list-table:: - :widths: 40 60 - :header-rows: 1 - - * - Exception thrown by C++ - - Translated to Python exception type - * - ``std::exception`` - - ``RuntimeError`` - * - ``std::bad_alloc`` - - ``MemoryError`` - * - ``std::domain_error`` - - ``ValueError`` - * - ``std::invalid_argument`` - - ``ValueError`` - * - ``std::length_error`` - - ``ValueError`` - * - ``std::out_of_range`` - - ``IndexError`` - * - ``std::range_error`` - - ``ValueError`` - * - ``std::overflow_error`` - - ``OverflowError`` - * - :cpp:func:`nb::stop_iteration ` - - ``StopIteration`` (used to implement custom iterator) - * - :cpp:func:`nb::index_error ` - - ``IndexError`` (used to indicate out of bounds access in ``__getitem__``, - ``__setitem__``, etc.) - * - :cpp:func:`nb::key_error ` - - ``KeyError`` (used to indicate an invalid access in ``__getitem__``, - ``__setitem__``, etc.) - * - :cpp:func:`nb::value_error ` - - ``ValueError`` (used to indicate an invalid value in operations like - ``container.remove(...)``) - * - :cpp:func:`nb::type_error ` - - ``TypeError`` - * - :cpp:func:`nb::buffer_error ` - - ``BufferError`` - * - :cpp:func:`nb::import_error ` - - ``ImportError`` - * - :cpp:func:`nb::attribute_error ` - - ``AttributeError`` - * - Any other exception - - ``SystemError`` - -Exception translation is not bidirectional. A C++ ``catch -(nb::key_error)`` block will not catch a Python ``KeyError``. Use -:cpp:class:`nb::python_error ` for this purpose (see the :ref:`example -below ` for details). - -The is also a special exception :cpp:class:`nb::cast_error ` that may -be raised -by the call operator :cpp:func:`nb::handle::operator() -` and :cpp:func:`nb::cast() ` when argument(s) -cannot be converted to Python objects. - -.. _custom_exceptions: - -Handling custom exceptions --------------------------- - -nanobind can also expose custom exception types. The -:cpp:class:`nb::exception\ ` helper resembles -:cpp:class:`nb::class_\ ` and registers a new exception type within -the provided scope. - -.. code-block:: cpp - - NB_MODULE(my_ext, m) { - nb::exception(m, "PyExp"); - } - -Here, it creates ``my_ext.PyExp``. Subsequently, any C++ exception of type -``CppExp`` crossing the language barrier will automatically convert to -``my_ext.PyExp``. - -A Python exception base class can optionally be specified. For example, the -snippet below causes ``PyExp`` to inherit from ``RuntimeError`` (the default is -``Exception``). The built-in Python exception classes are listed `here -`__. - -.. code-block:: cpp - - nb::exception(module, "PyExp", PyExc_RuntimeError); - -In more complex cases, :cpp:func:`nb::register_exception_translator() -` can be called to register a custom exception -translation routine. It takes a stateless callable (e.g. a function pointer or -a lambda function without captured variables) with the call signature -``void(const std::exception_ptr &, void*)`` and an optional payload pointer -value that will be passed to the second parameter of the callable. - -When a C++ exception is captured by nanobind, all registered exception -translators are tried in reverse order of registration (i.e. the last -registered translator has the first chance of handling the exception). - -Inside the translator, call ``std::rethrow_exception()`` within a -``try``-``catch`` block to re-throw the exception and capture supported -exception types. The ``catch`` block should call ``PyErr_SetString`` or -``PyErr_Format`` (`1 -`__, `2 -`__) to -set a suitable Python error status. The following example demonstrates this -pattern to convert ``MyCustomException`` into a Python ``IndexError``. - -.. code-block:: cpp - - nb::register_exception_translator( - [](const std::exception_ptr &p, void * /* unused */) { - try { - std::rethrow_exception(p); - } catch (const MyCustomException &e) { - PyErr_SetString(PyExc_IndexError, e.what()); - } - }); - -Multiple exceptions can be handled by a single translator. nanobind captures -unhandled exceptions and forwards them to the preceding translator. If none of -the exception translators succeeds, it will convert according to the previously -discussed default rules. - -.. note:: - - When the exception translator returns normally, it must have set a Python - error status. Otherwise, Python will crash with the message ``SystemError: - error return without exception set``. - - Unsupported exception types should not be caught, or may be explicitly - (re-)thrown to delegate them to the other exception translators. - -.. _handling_python_exceptions_cpp: - -Capturing Python exceptions within C++ --------------------------------------- - -When nanobind-based C++ code calls a Python function that raises an exception, -it will automatically convert into a :class:`nb::python_error ` -raised on the C++ side. This exception type can be caught and handled in C++ or -propagate back into Python, where it will undergo reverse conversion. - -.. list-table:: - :widths: 40 60 - :header-rows: 1 - - * - Exception raised in Python - - Translated to C++ exception type - * - Any Python ``Exception`` - - :cpp:class:`nb::python_error ` - -The class exposes various members to obtain further information about the -exception. The :cpp:func:`.type() ` and :cpp:func:`.value() -` methods provide information about the exception type and -value, while :cpp:func:`.what() ` generates a -human-readable representation including a backtrace. - -A use of the :cpp:func:`.matches() ` method to -distinguish different exception types is shown below: - -.. code-block:: cpp - - try { - nb::object file = nb::module_::import_("io").attr("open")("file.txt", "r"); - nb::object text = file.attr("read")(); - file.attr("close")(); - } catch (const nb::python_error &e) { - if (e.matches(PyExc_FileNotFoundError)) { - nb::print("file.txt not found"); - } else if (e.matches(PyExc_PermissionError)) { - nb::print("file.txt found but not accessible"); - } else { - throw; - } - } - -Note that the previously discussed :ref:`automatic conversion -` of C++ exception does not apply here. Errors raised -from Python *always* convert to :cpp:class:`nb::python_error `. - -Handling errors from the Python C API -------------------------------------- - -Whenever possible, use :ref:`nanobind wrappers ` instead of calling -the Python C API directly. Otherwise, you must carefully manage reference -counts and adhere to the nanobind error protocol outlined below. - -When a Python C API call fails with an error status, you must immediately -``throw nb::python_error();`` to capture the error and handle it using -appropriate C++ mechanisms. This includes calls to error setting functions such -as ``PyErr_SetString`` (:ref:`custom exception translators ` -are excluded from this rule). - -.. code-block:: cpp - - PyErr_SetString(PyExc_TypeError, "C API type error demo"); - throw nb::python_error(); - - // But it would be easier to simply... - throw nb::type_error("nanobind wrapper type error"); - -Alternately, to ignore the error, call `PyErr_Clear() -`__. Any -Python error must be thrown or cleared, or nanobind will be left in an -invalid state. - -.. _exception_chaining: - -Chaining exceptions ('raise from') ----------------------------------- - -Python has a mechanism for indicating that exceptions were caused by other -exceptions: - -.. code-block:: py - - try: - print(1 / 0) - except Exception as exc: - raise RuntimeError("could not divide by zero") from exc - -To do a similar thing in nanobind, you can use the :cpp:func:`nb::raise_from -` function, which requires a :cpp:class:`nb::python_error -` and re-raises it with a chained exception object. - -.. code-block:: cpp - - nb::callable f = ...; - int arg = 123; - try { - f(arg); - } catch (nb::python_error &e) { - nb::raise_from(e, PyExc_RuntimeError, "Could not call 'f' with %i", arg); - } - -The function is internally based on the Python function ``PyErr_FormatV`` and -takes ``printf``-style arguments following the format descriptor. - -An even lower-level interface is available via :cpp:func:`nb::chain_error -`. - -Handling unraisable exceptions ------------------------------- - -If a Python function invoked from a C++ destructor or any function marked -``noexcept(true)`` (collectively, "noexcept functions") throws an exception, there -is no way to propagate the exception, as such functions may not throw. -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 -`_ -and an auditing event is logged. - -Any noexcept function should have a try-catch block that traps -:cpp:class:`nb::python_error ` (or any other exception that can -occur). A useful approach is to convert them to Python exceptions and then -``discard_as_unraisable`` as shown below. - -.. code-block:: cpp - - void nonthrowing_func() noexcept(true) { - try { - // ... - } catch (nb::python_error &e) { - // Discard the Python error using Python APIs, using the C++ magic - // variable __func__. Python already knows the type and value and of the - // exception object. - e.discard_as_unraisable(__func__); - } catch (const std::exception &e) { - // Log and discard C++ exceptions. - third_party::log(e); - } - } diff --git a/src/nanobind/docs/exchanging.rst b/src/nanobind/docs/exchanging.rst deleted file mode 100644 index a2858fa..0000000 --- a/src/nanobind/docs/exchanging.rst +++ /dev/null @@ -1,423 +0,0 @@ -.. cpp:namespace:: nanobind - -.. _exchange: - -Exchanging information -====================== - -nanobind offers three fundamentally different ways of exchanging information -between Python and C++. Depending on the task at hand, one will usually be -preferable over the others, hence it is important to be aware of their -advantages and disadvantages. - -.. _type_casters: - -Option 1: Type Casters ----------------------- - -A *type caster* translates C++ objects into equivalent Python -objects and vice versa. The illustration below shows a translation between -C++ (blue) and Python (green) worlds, where a ``std::vector`` instance -converts from/to a Python ``list`` containing ``int`` objects. - -.. only:: not latex - - .. image:: images/caster-light.svg - :width: 400 - :align: center - :class: only-light - - .. image:: images/caster-dark.svg - :width: 400 - :align: center - :class: only-dark - -.. only:: latex - - .. image:: images/caster-light.svg - :width: 400 - :align: center - -**Example**: The following function doubles the entries of an STL vector and -returns the result. - -.. code-block:: cpp - - using IntVector = std::vector; - - IntVector double_it(const IntVector &in) { - IntVector out(in.size()); - for (size_t i = 0; i < in.size(); ++i) - out[i] = in[i] * 2; - return out; - } - -To expose it in Python, we can use the ``std::vector<...>`` type caster that -is located in an optional header file named ``nanobind/stl/vector.h``: - -.. code-block:: cpp - - #include - - NB_MODULE(my_ext, m) { - m.def("double_it", &double_it); - } - -That's all there is to it. The Python version of the function features -an automatically generated docstring, type checks, and (if needed) error reporting. - -.. code-block:: pycon - - >>> import my_ext - >>> my_ext.double_it([1, 2, 3]) - [2, 4, 6] - - >>> my_ext.double_it([1, 2, 'foo']) - TypeError: double_it(): incompatible function arguments. The following argument types are supported: - 1. double_it(arg: list[int], /) -> list[int] - -What are the implications of using type casters? - -**Pro**: this approach is simple and convenient, especially when standard -(STL) types are involved. Usually, all that is needed is an ``#include`` -directive to pull in the right header file. Complex nested types (e.g. -vectors of hash tables of strings) work automatically by combining type -casters recursively. - -The following table lists the currently available type casters along with links -to external projects that provide further casters: - -.. list-table:: - :widths: 42 48 - :header-rows: 1 - - * - Type - - Type caster header - * - ``char``, ``char*``, ``void*``, ``nullptr_t``, ``bool``, ``int``, - ``unsigned int``, ``long``, ``unsigned long``, ... - - Built-in (no include file needed) - * - ``std::array<..>`` - - ``#include `` - * - ``std::chrono::duration<..>``, ``std::chrono::time_point<..>`` - (:ref:`more details `) - - ``#include `` - * - ``std::complex<..>`` - - ``#include `` - * - ``std::filesystem::path`` - - ``#include `` - * - ``std::function<..>`` - - ``#include `` - * - ``std::list<..>`` - - ``#include `` - * - ``std::map<..>`` - - ``#include `` - * - ``std::optional<..>`` - - ``#include `` - * - ``std::pair<..>`` - - ``#include `` - * - ``std::set<..>`` - - ``#include `` - * - ``std::string`` - - ``#include `` - * - ``std::string_view`` - - ``#include `` - * - ``std::wstring`` - - ``#include `` - * - ``std::tuple<..>`` - - ``#include `` - * - ``std::shared_ptr<..>`` - - ``#include `` - * - ``std::unique_ptr<..>`` - - ``#include `` - * - ``std::unordered_set<..>`` - - ``#include `` - * - ``std::unordered_map<..>`` - - ``#include `` - * - ``std::variant<..>`` - - ``#include `` - * - ``std::vector<..>`` - - ``#include `` - * - ``nb::ndarray<..>`` - - ``#include `` - * - ``Eigen::Matrix<..>``, ``Eigen::Array<..>``, ``Eigen::Ref<..>``, ``Eigen::Map<..>`` - - ``#include `` - * - ``Eigen::SparseMatrix<..>`` - - ``#include `` - * - Apache Arrow types - - `https://github.com/maximiliank/nanobind_pyarrow `__ - * - ... - - Please reach out if you have additions to this list. - - -**Con**: Every transition between the Python and C++ side will generally require a -conversion step (in this case, to re-create all list elements). This can be -wasteful when the other side only needs to access a small part of the data. -Conversely, the overhead should not be a problem when the data is fully -"consumed" following conversion. - -A select few type casters (``std::unique_ptr<..>``, ``std::shared_ptr<..>``, -:cpp:class:`nb::ndarray `, and ``Eigen::*``) are special in the sense -that they can perform a type conversion *without* copying the underlying data. -Besides those few exceptions type casting always implies that a copy is made. - -.. _type_caster_mutable: - -Mutable reference issue -^^^^^^^^^^^^^^^^^^^^^^^ - -Another subtle limitation of type casters is that they -don't propagate updates through mutable references. Consider the -following alternative implementation of the ``double_it`` function: - -.. code-block:: cpp - - void double_it(IntVector &in) { - for (int &value : in) - value *= 2; - } - -nanobind can wrap this function without problems, but it won't behave as -expected: - -.. code-block:: pycon - - >>> x = [1, 2, 3] - >>> my_ext.double_it(x) - >>> x - [1, 2, 3] # <-- oops, unchanged! - -*How could this happen?* -The reason is that type casters convert function arguments and return values once, but further -changes will not automatically propagate across the language barrier because -the representations are not intrinsically linked to each other. This problem is -not specific to STL types---for example, the following function will similarly -not update its argument once exposed in Python. - -.. code-block:: cpp - - void double_it(int &in) { in *= 2; } - -This is because builtin types like ``int``, ``str``, ``bool``, etc., are -all handled by type casters. - -A simple alternative to propagate updates while retaining the convenience of -type casters is to bind a small wrapper lambda function that returns a tuple -with all output arguments. An example: - -.. code-block:: cpp - - int foo(int &in) { in *= 2; return std::sqrt(in); } - -And the binding code - -.. code-block:: cpp - - m.def("foo", [](int i) { int rv = foo(i); return std::make_tuple(rv, i); }); - -In this case, a type caster (``#include `` return value. - -.. _bindings: - -Option 2: Bindings ------------------- - -*Bindings* expose C++ types in Python; the ability to create them is the -main feature of nanobind. In the list-of-integer example, they cause Python -to interpret ``std::vector`` as a new Python type called -``my_ext.IntVector``. - -.. only:: not latex - - .. image:: images/binding-light.svg - :width: 400 - :align: center - :class: only-light - - .. image:: images/binding-dark.svg - :width: 400 - :align: center - :class: only-dark - -.. only:: latex - - .. image:: images/binding-light.svg - :width: 400 - :align: center - -**Example**: to switch the previous example to bindings, we first replace -the type caster header (`nanobind/stl/vector.h -`_) -by its binding variant (`nanobind/stl/bind_vector.h -`_) -and then invoke the :cpp:func:`nb::bind_vector\() ` function to create a *new -Python type named* ``IntVector`` within the module ``m``. - -.. code-block:: cpp - :emphasize-lines: 1, 9 - - #include - - using IntVector = std::vector; - IntVector double_it(const IntVector &in) { /* .. omitted .. */ } - - namespace nb = nanobind; - - NB_MODULE(my_ext, m) { - nb::bind_vector(m, "IntVector"); - m.def("double_it", &double_it); - } - -Any function taking or returning integer vectors will now use the type -binding. In the Python session below, nanobind performs an implicit -conversion from the Python list ``[1, 2, 3]`` to a ``my_ext.IntVector`` -before calling the ``double_it`` function. - -.. code-block:: pycon - - >>> import my_ext - >>> my_ext.double_it([1, 2, 3]) - my_ext.IntVector([2, 4, 6]) - - >>> my_ext.double_it.__doc__ - 'double_it(arg: my_ext.IntVector, /) -> my_ext.IntVector' - -Let's go through the implications of using bindings: - -**Pro**: bindings don't require the costly conversion step when crossing the -language boundary. They also support mutable references, so the :ref:`issue -discussed in the context of type casters ` does not -arise. Sometimes, binding is the only available option: when a C++ type does -not have an equivalent Python type, casting simply does not make sense. - -**Con**: Creating good bindings that feel natural in Python requires some -additional work. We cheated in this example by relying on the -:cpp:func:`nb::bind_vector\() ` helper function that did -all the heavy lifting. Such helpers are currently only available for a few -special cases (vectors, ordered/unordered maps, iterators): - -.. list-table:: - :widths: 42 48 - :header-rows: 1 - - * - Type - - Binding helper header - * - ``std::vector<..>`` - - ``#include `` - (:ref:`docs `) - * - ``std::map<..>`` - - ``#include `` - (:ref:`docs `) - * - ``std::unordered_map<..>`` - - ``#include `` - (:ref:`docs `) - * - Forward iterators - - ``#include `` - (:ref:`docs `) - * - Other types - - See the previous example on :ref:`binding custom types `. - -In general, you will need to write the binding code yourself. The previous -section on :ref:`binding custom types ` showed an example of -such a type binding. - -.. _wrappers: - -Option 3: Wrappers ------------------- - -The last option is only rarely used, but it can be powerful alternative in -some cases. nanobind provides *wrapper* classes to use Python types within -C++. You can think of this as a kind of *reverse binding*. For example, a -Python list can be accessed through the :cpp:class:`nb::list ` type: - -.. only:: not latex - - .. image:: images/wrapper-light.svg - :width: 400 - :align: center - :class: only-light - - .. image:: images/wrapper-dark.svg - :width: 400 - :align: center - :class: only-dark - -.. only:: latex - - .. image:: images/wrapper-light.svg - :width: 400 - :align: center - -This is what the example looks like when expressed using -:cpp:class:`nb::list ` and :cpp:class:`nb::int_ `. - -.. code-block:: cpp - - #include - - namespace nb = nanobind; - - nb::list double_it(nb::list l) { - nb::list result; - for (nb::handle h: l) - result.append(h * nb::int_(2)); - return result; - } - - NB_MODULE(my_ext, m) { - m.def("double_it", &double_it); - } - - -The implications of using wrappers are: - -**Pro**: Wrappers require no copying or type conversion. With them, C++ begins -to resemble dynamically typed Python code and can perform highly general -operations on Python objects. Wrappers are useful to tap into the powerful -Python software ecosystem (NumPy, Matplotlib, PyTorch, etc). - -**Con**: Functions based on wrappers cannot run without Python. In contrast -to option 1 (:ref:`type casters `) and 2 (:ref:`bindings -`), we can no longer reuse an existing function and process its -arguments and return value to interface the Python and C++ worlds: the -entire function must be rewritten using nanobind-specific wrapper types. -Every operation will translate into a corresponding Python C API call, which -means that wrappers aren't suitable for performance-critical loops or -multithreaded computations. - -The following wrappers are available and require no additional include -directives: -:cpp:class:`any`, -:cpp:class:`bytearray`, :cpp:class:`bytes`, :cpp:class:`callable`, :cpp:class:`capsule`, -:cpp:class:`dict`, :cpp:class:`ellipsis`, :cpp:class:`handle`, -:cpp:class:`handle_t\ `, -:cpp:class:`bool_`, :cpp:class:`int_`, :cpp:class:`float_`, -:cpp:class:`frozenset`, :cpp:class:`iterable`, :cpp:class:`iterator`, -:cpp:class:`list`, :cpp:class:`mapping`, -:cpp:class:`module_`, :cpp:class:`object`, :cpp:class:`set`, :cpp:class:`sequence`, -:cpp:class:`slice`, :cpp:class:`str`, :cpp:class:`tuple`, -:cpp:class:`weakref`, -:cpp:class:`type_object`, :cpp:class:`type_object_t\ `, -:cpp:class:`args`, :cpp:class:`kwargs`, and -:cpp:class:`fallback `. - -Discussion ----------- - -The choices outlined above are more fine-grained than they may appear. For -example, it is possible to use type casters, bindings, and wrappers to handle -multiple arguments of *a single function*. - -They can also be combined *within* a single function argument. For example, you -can type cast a ``std::vector`` containing bindings or wrappers. - -In general, we recommend that you use - -1. type casters for STL containers, and - -2. bindings for other custom types. - -If the former turn out to be a performance bottleneck, it is easy to replace -them with bindings or wrappers later on. Wrappers are only rarely useful; -you will usually know it when you need them. diff --git a/src/nanobind/docs/faq.rst b/src/nanobind/docs/faq.rst deleted file mode 100644 index e37c807..0000000 --- a/src/nanobind/docs/faq.rst +++ /dev/null @@ -1,398 +0,0 @@ -.. cpp:namespace:: nanobind - -.. _faq: - -Frequently asked questions -========================== - -Importing my module fails with an ``ImportError`` -------------------------------------------------- - -If importing the module fails as shown below, you have not specified a -matching module name in :cmake:command:`nanobind_add_module()` and -:c:macro:`NB_MODULE() `. - -.. code-block:: pycon - - >>> import my_ext - ImportError: dynamic module does not define module export function (PyInit_my_ext) - -Importing fails due to missing ``[lib]nanobind.{dylib,so,dll}`` ---------------------------------------------------------------- - -If importing the module fails as shown below, the extension cannot find the -``nanobind`` shared library component. - -.. code-block:: pycon - - >>> import my_ext - ImportError: dlopen(my_ext.cpython-311-darwin.so, 0x0002): - Library not loaded: '@rpath/libnanobind.dylib' - -This is really more of a general C++/CMake/build system issue than one of -nanobind specifically. There are two solutions: - -1. Build the library component statically by specifying the ``NB_STATIC`` flag - in :cmake:command:`nanobind_add_module()` (this is the default starting with - nanobind 0.2.0). - -2. Ensure that the various shared libraries are installed in the right - destination, and that their `rpath `_ - is set so that they can find each other. - - You can control the build output directory of the shared library component - using the following CMake command: - - .. code-block:: pycon - - set_target_properties(nanobind - PROPERTIES - LIBRARY_OUTPUT_DIRECTORY - LIBRARY_OUTPUT_DIRECTORY_RELEASE - LIBRARY_OUTPUT_DIRECTORY_DEBUG - LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO - LIBRARY_OUTPUT_DIRECTORY_MINSIZEREL - ) - - Depending on the flags provided to :cmake:command:`nanobind_add_module()`, - the shared library component may have a different name following the pattern - ``nanobind[-abi3][-lto]``. - - The following CMake commands may be useful to adjust the build and install - `rpath `_ of the extension: - - .. code-block:: cmake - - set_property(TARGET my_ext APPEND PROPERTY BUILD_RPATH "$") - set_property(TARGET my_ext APPEND PROPERTY INSTALL_RPATH ".. ?? ..") - - -Why are reference arguments not updated? ----------------------------------------- - -Functions like the following example can be exposed in Python, but they won't -propagate updates to mutable reference arguments. - -.. code-block:: cpp - - void increment(int &i) { - i++; - } - -This isn't specific to builtin types but also applies to STL collections and -other types when they are handled using :ref:`type casters `. -Please read the full section on :ref:`information exchange between C++ and -Python ` to understand the issue and alternatives. - -Why am I getting errors about leaked instances/functions/types? ---------------------------------------------------------------- - -Please see the dedicated documentation section on :ref:`reference leaks `. - -Compilation fails with a static assertion mentioning ``NB_MAKE_OPAQUE()`` -------------------------------------------------------------------------- - -If your compiler generates an error of the following sort, you are mixing type -casters and bindings in a way that has them competing for the same types: - -.. code-block:: text - - nanobind/include/nanobind/nb_class.h:207:40: error: static assertion failed: ↵ - Attempted to create a constructor for a type that won't be handled by the nanobind's ↵ - class type caster. Is it possible that you forgot to add NB_MAKE_OPAQUE() somewhere? - -For example, the following won't work: - -.. code-block:: cpp - - #include - #include - - namespace nb = nanobind; - - NB_MODULE(my_ext, m) { - // The following line cannot be compiled - nb::bind_vector>(m, "VectorInt"); - - // This doesn't work either - nb::class_>(m, "VectorInt"); - } - -This is not specific to STL vectors and will happen whenever casters and -bindings target overlapping types. - -:ref:`Type casters ` employ a pattern matching technique known as -`partial template specialization -`_. For example, -``nanobind/stl/vector.h`` installs a pattern that detects *any* use of -``std::vector``, which overlaps with the above binding of a specific -vector type. - -The deeper reason for this conflict is that type casters enable a -*compile-time* transformation of nanobind code, which can conflict with -binding declarations that are a *runtime* construct. - -To fix the conflict in this example, add the line :c:macro:`NB_MAKE_OPAQUE(T) -`, which adds another partial template specialization pattern -for ``T`` that says: "ignore ``T`` and don't use a type caster to handle it". - -.. code-block:: cpp - - NB_MAKE_OPAQUE(std::vector); - -.. warning:: - - If your extension consists of multiple source code files that involve - overlapping use of type casters and bindings, you are *treading on thin - ice*. It is easy to violate the *One Definition Rule* (ODR) [`details - `_] in such a case, which - may lead to undefined behavior (miscompilations, etc.). - - Here is a hypothetical example of an ODR violation: an extension - contains two source code files: ``src_1.cpp`` and ``src_2.cpp``. - - - ``src_1.cpp`` binds a function that returns an ``std::vector`` using - a :ref:`type caster ` (``nanobind/stl/vector.h``). - - - ``src_2.cpp`` binds a function that returns an ``std::vector`` using - a :ref:`binding ` (``nanobind/stl/bind_vector.h``), and it also - installs the needed type binding. - - The problem is that a partially specialized class in the nanobind - implementation namespace (specifically, - ``nanobind::detail::type_caster>``) now resolves to *two - different implementations* in the two compilation units. It is unclear how - such a conflict should be resolved at the linking stage, and you should - consider code using such constructions broken. - - To avoid this issue altogether, we recommend that you create a single - include file (e.g., ``binding_core.h``) containing all of the nanobind - include files (binding, type casters), your own custom type casters (if - present), and :c:macro:`NB_MAKE_OPAQUE(T) ` declarations. - Include this header consistently in all binding compilation units. The - construction shown in the example (mixing type casters and bindings for the - same type) is not allowed, and cannot occur when following the - recommendation. - -How can I preserve the ``const``-ness of values in bindings? ------------------------------------------------------------- - -This is a limitation of nanobind, which casts away ``const`` in function -arguments and return values. This is in line with the Python language, which -has no concept of const values. Additional care is therefore needed to avoid -bugs that would be caught by the type checker in a traditional C++ program. - -How can I reduce build time? ----------------------------- - -Large binding projects should be partitioned into multiple files, as shown in -the following example: - -:file:`example.cpp`: - -.. code-block:: cpp - - void init_ex1(nb::module_ &); - void init_ex2(nb::module_ &); - /* ... */ - - NB_MODULE(my_ext, m) { - init_ex1(m); - init_ex2(m); - /* ... */ - } - -:file:`ex1.cpp`: - -.. code-block:: cpp - - void init_ex1(nb::module_ &m) { - m.def("add", [](int a, int b) { return a + b; }); - } - -:file:`ex2.cpp`: - -.. code-block:: cpp - - void init_ex2(nb::module_ &m) { - m.def("sub", [](int a, int b) { return a - b; }); - } - -As shown above, the various ``init_ex`` functions should be contained in -separate files that can be compiled independently from one another, and then -linked together into the same final shared object. Following this approach -will: - -1. reduce memory requirements per compilation unit. - -2. enable parallel builds (if desired). - -3. allow for faster incremental builds. For instance, when a single class - definition is changed, only a subset of the binding code will generally need - to be recompiled. - -.. _type-visibility: - -How can I avoid conflicts with other projects using nanobind? -------------------------------------------------------------- - -Suppose that a type binding in your project conflicts with another extension, for -example because both expose a common type (e.g., ``std::latch``). nanobind will -warn whenever it detects such a conflict: - -.. code-block:: text - - RuntimeWarning: nanobind: type 'latch' was already registered! - -In the worst case, this could actually break both packages (especially if the -bindings of the two packages expose an inconsistent/incompatible API). - -The higher-level issue here is that nanobind will by default try to make type -bindings visible across extensions because this is helpful to partition large -binding projects into smaller parts. Such information exchange requires that -the extensions: - -- use the same nanobind *ABI version* (see the :ref:`Changelog ` for details). -- use the same compiler (extensions built with GCC and Clang are isolated from each other). -- use ABI-compatible versions of the C++ library. -- use the stable ABI interface consistently (stable and unstable builds are isolated from each other). -- use debug/release mode consistently (debug and release builds are isolated from each other). - -In addition, nanobind provides a feature to intentionally scope extensions to a -named domain to avoid conflicts with other extensions. To do so, specify the -``NB_DOMAIN`` parameter in CMake: - -.. code-block:: cmake - - nanobind_add_module(my_ext - NB_DOMAIN my_project - my_ext.cpp) - -In this case, inter-extension type visibility is furthermore restricted to -extensions in the ``"my_project"`` domain. - -Can I use nanobind without RTTI or C++ exceptions? --------------------------------------------------- - -Certain environments (e.g., `Google-internal development -`__, embedded devices, etc.) -require compilation without C++ runtime type information (``-fno-rtti``) and -exceptions (``-fno-exceptions``). - -nanobind requires both of these features and cannot be used when they are not -available. RTTI provides the central index to look up types of bindings. -Exceptions are needed because Python relies on exceptions that must be -converted into something equivalent on the C++ side. PRs that refactor nanobind -to work without RTTI or exceptions will not be accepted. - -For Googlers: there is already an exemption from the internal rules that -specifically permits the use of RTTI/exceptions when a project relies on -pybind11. Likely, this exemption could be extended to include nanobind as well. - -Can I make stable ABI extensions for pre-3.12 Python? ------------------------------------------------------ - -Stable ABI extensions are convenient because they can be reused across Python -versions, but this unfortunately only works on Python 3.12 and newer. Nanobind -crucially depends on several `features -`__ that were added -in version 3.12 (specifically, ``PyType_FromMetaclass()`` and limited API -bindings of the vector call protocol). - -Policy on Clang-Tidy, ``-Wpedantic``, etc. ------------------------------------------- - -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 the increased -``-Wall``, ``-Wextra`` and ``/W4`` warning levels that are already enabled) - -Their next step is to open a big pull request needed to silence all of the -resulting messages. - -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 have a clear policy: I do not wish to pepper this codebase with -``const_cast`` and ``#pragmas`` or pragma-like comments to avoid warnings in -various kinds of 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. - -I'd like to use this project, but with $BUILD_SYSTEM instead of CMake ---------------------------------------------------------------------- - -A difficult aspect of C++ software development is the sheer number of competing -build systems, including - -- `CMake `__, -- `Meson `__, -- `xmake `__, -- `Premake `__, -- `Bazel `__, -- `Conan `__, -- `Autotools `__, -- and many others. - -The author of this project has some familiarity with CMake but lacks expertise -with this large space of alternative tools. Maintaining and shipping support for -other build systems is therefore considered beyond the scope of this *nano* -project (see also the :ref:`why? ` part of the documentation that explains -the rationale for being somewhat restrictive towards external contributions). - -If you wish to create and maintain an alternative interface to nanobind, then -my request would be that you create and maintain separate repository (see, -e.g., `pybind11_bazel `__ as an -example how how this was handled in the case of pybind11). Please carefully -review the file `nanobind-config.cmake -`__. -Besides getting things to compile, it specifies a number of platform-dependent -compiler and linker options that are needed to produce *optimal* (small and -efficient) binaries. Nanobind uses a `complicated and non-standard -`__ -set of linker parameters on macOS, which is the result of a `lengthy -investigation -`__. -Other parameters like linker-level dead code elimination and size-based -optimization were similarly added following careful analysis. The CMake build -system provides the ability to compile ``libnanobind`` into either a shared or -a static library, to optionally target the stable ABI, and to isolate it from -other extensions via the ``NB_DOMAIN`` parameter. All of these are features -that would be nice to retain in an alternative build system. If you've made a -build system compatible with another tool that is sufficiently -feature-complete, then please file an issue and I am happy to reference it in -the documentation. - -Are there tools to generate nanobind bindings automatically? ------------------------------------------------------------- - -`litgen `__ is an automatic Python bindings -generator compatible with both pybind11 and nanobind, designed to create -documented and easily discoverable bindings. -It reproduces header documentation directly in the bindings, making the -generated API intuitive and well-documented for Python users. -Powered by srcML (srcml.org), a high-performance, multi-language parsing tool, -litgen takes a developer-centric approach. -The C++ API to be exposed to Python must be C++14 compatible, although the -implementation can leverage more modern C++ features. - -How to cite this project? -------------------------- - -Please use the following BibTeX template to cite nanobind in scientific -discourse: - -.. code-block:: bibtex - - @misc{nanobind, - author = {Wenzel Jakob}, - year = {2022}, - note = {https://github.com/wjakob/nanobind}, - title = {nanobind: tiny and efficient C++/Python bindings} - } diff --git a/src/nanobind/docs/free_threaded.rst b/src/nanobind/docs/free_threaded.rst deleted file mode 100644 index f53d54e..0000000 --- a/src/nanobind/docs/free_threaded.rst +++ /dev/null @@ -1,326 +0,0 @@ -.. _free-threaded: - -.. cpp:namespace:: nanobind - -Free-threaded Python -==================== - -**Free-threading** is an experimental new Python feature that replaces the -`Global Interpreter Lock (GIL) -`__ with a fine-grained -locking scheme to better leverage multi-core parallelism. The resulting -benefits do not come for free: extensions must explicitly opt-in and generally -require careful modifications to ensure correctness. - -Nanobind can target free-threaded Python since version 2.2.0. This page -explains how to do so and discusses a few caveats. Besides this page, make sure -to review `py-free-threading.github.io `__ -for a more comprehensive discussion of free-threaded Python. `PEP 703 -`__ explains the nitty gritty details. - -Opting in ---------- - -To opt into free-threaded Python, pass the ``FREE_THREADED`` parameter to the -:cmake:command:`nanobind_add_module()` CMake target command. For other build -systems, refer to their respective documentation pages. - -.. code-block:: cmake - - nanobind_add_module( - my_ext # Target name - FREE_THREADED # Opt into free-threading - my_ext.h # Source code files below - my_ext.cpp) - -nanobind ignores the ``FREE_THREADED`` parameter when the registered Python -version does not support free-threading. - -.. note:: - - **Stable ABI**: Note that there currently is no stable ABI for free-threaded - Python, hence the ``STABLE_ABI`` parameter will be ignored in free-threaded - extensions builds. It is valid to combine the ``STABLE_ABI`` and - ``FREE_THREADED`` arguments: the build system will choose between the two - depending on the detected Python version. - -.. warning:: - - Loading an Python extension that does not support free-threading disables - free-threading globally. In larger binding projects with multiple - extensions, all of them must be adapted. - -If free-threading was requested and is available, the build system will set the -``NB_FREE_THREADED`` preprocessor flag. This can be helpful to specialize -binding code with ``#ifdef`` blocks, e.g.: - -.. code-block:: cpp - - #if !defined(NB_FREE_THREADED) - ... // simple GIL-protected code - #else - ... // more complex thread-aware code - #endif - -Caveats -------- - -Free-threading can violate implicit assumptions made by extension developers -when previously serial operations suddenly run concurrently, producing -undefined behavior (race conditions, crashes, etc.). - -Let's consider a concrete example: the binding code below defines a ``Counter`` -class with an increment operation. - -.. code-block:: cpp - - struct Counter { - int value = 0; - void inc() { value++; } - }; - - nb::class_(m, "Counter") - .def("inc", &Counter::inc) - .def_ro("value", &Counter::value); - - -If multiple threads call the ``inc()`` method of a single ``Counter``, the -final count will generally be incorrect, as the increment operation ``value++`` -does not execute atomically. - -To fix this, we could modify the C++ type so that it protects its ``value`` -member from concurrent modification, for example using an atomic number type -(e.g., ``std::atomic``) or a critical section (e.g., based on -``std::mutex``). - -The race condition in the above example is relatively benign. However, -in more complex projects, combinations of concurrency and unsafe memory -accesses could introduce non-deterministic data corruption and crashes. - -Another common source of problems are *global variables* undergoing concurrent -modification when no longer protected by the GIL. They will likewise require -supplemental locking. The :ref:`next section ` explains a -Python-specific locking primitive that can be used in binding code besides -the solutions mentioned above. - -Multi-threaded code that concurrently returns the same C++ instance via the -:cpp:enumerator:`nb::rv_policy::reference` policy may observe situations, where -multiple Python objects are created that all wrap the same C++ instance -(however, this is harmless aside from the duplication). - -.. _free-threaded-locks: - -Python locks ------------- - -Nanobind provides convenience functionality encapsulating the mutex -implementation that is part of Python ("``PyMutex``"). It is slightly more -efficient than OS/language-provided synchronization primitives and generally -preferable within Python extensions. - -The class :cpp:class:`ft_mutex` is analogous to ``std::mutex``, and -:cpp:class:`ft_lock_guard` is analogous to ``std::lock_guard``. Note that they -only exist to add *supplemental* critical sections needed in free-threaded -Python, while becoming inactive (no-ops) when targeting regular GIL-protected -Python. - -With these abstractions, the previous ``Counter`` implementation could be -rewritten as: - -.. code-block:: cpp - :emphasize-lines: 3,6 - - struct Counter { - int value = 0; - nb::ft_mutex mutex; - - void inc() { - nb::ft_lock_guard guard(mutex); - value++; - } - }; - -These locks are very compact (``sizeof(nb::ft_mutex) == 1``), though this is a -Python implementation detail that could change in the future. - -.. _argument-locks: - -Argument locking ----------------- - -Modifying class and function definitions as shown above may not always be -possible. As an alternative, nanobind also provides a way to *retrofit* -supplemental locking onto existing code. The idea is to lock individual -arguments of a function *before* being allowed to invoke it. A built-in mutex -present in every Python object enables this. - -To do so, call the :cpp:func:`.lock() ` member of -:cpp:class:`nb::arg() ` annotations to indicate that an -argument must be locked, e.g.: - -- :cpp:func:`nb::arg("my_parameter").lock() ` -- :cpp:func:`"my_parameter"_a.lock() ` (short-hand form) - -In methods bindings, pass :cpp:struct:`nb::lock_self() ` to lock -the implicit ``self`` argument. Note that at most 2 arguments can be -locked per function, which is a limitation of the `Python locking API -`__. - -The example below shows how this functionality can be used to protect ``inc()`` -and a new ``merge()`` function that acquires two simultaneous locks. - -.. code-block:: cpp - - struct Counter { - int value = 0; - - void inc() { value++; } - void merge(Counter &other) { - value += other.value; - other.value = 0; - } - }; - - nb::class_(m, "Counter") - .def("inc", &Counter::inc, nb::lock_self()) - .def("merge", &Counter::merge, nb::lock_self(), "other"_a.lock()) - .def_ro("value", &Counter::value); - -The above solution has an obvious drawback: it only protects *bindings* (i.e., -transitions from Python to C++). For example, if some other part of a C++ -codebase calls ``merge()`` directly, the binding layer won't be involved, and -no locking takes place. If such behavior can introduce race conditions, a -larger-scale redesign of your project may be in order. - -.. note:: - - Adding locking annotations indiscriminately is inadvisable because locked - calls are more costly than unlocked ones. The :cpp:func:`.lock() - ` and :cpp:struct:`nb::lock_self() ` annotations are - ignored in GIL-protected builds, hence this added cost only applies to - free-threaded extensions. - - Furthermore, when adding locking annotations to a function, consider keeping - the arguments *unnamed* (i.e., :cpp:func:`nb::arg().lock() ` - instead of :cpp:func:`nb::arg("name").lock() `) if the function - will never be called with keyword arguments. Processing named arguments - causes small :ref:`binding overheads ` that may be - undesirable if a function that does very little is called at a very high - rate. - -.. note:: - - **Python API and locking**: When the lock-protected function performs Python - API calls (e.g., using :ref:`wrappers ` like :cpp:class:`nb::dict - `), Python may temporarily release locks to avoid deadlocks. Here, - even basic reference counting such as a :cpp:class:`nb::object - ` variable expiring at the end of a scope counts as an API call. - - These locks will be reacquired following the Python API call. This behavior - resembles ordinary (GIL-protected) Python code, where operations like - `Py_DECREF() - `__ can cause - cause arbitrary Python code to execute. The semantics of this kind of - relaxed critical section are described in the `Python documentation - `__. - -Miscellaneous notes -------------------- - -API ---- - -The following API specific to free-threading has been added: - -- :cpp:class:`nb::ft_mutex ` -- :cpp:class:`nb::ft_lock_guard ` -- :cpp:class:`nb::ft_object_guard ` -- :cpp:class:`nb::ft_object2_guard ` -- :cpp:func:`nb::arg::lock() ` - -API stability -_____________ - -The interface explained in this is excluded from the project's semantic -versioning policy. Free-threading is still experimental, and API breaks may be -necessary based on future experience and changes in Python itself. - -Wrappers -________ - -:ref:`Wrapper types ` like :cpp:class:`nb::list ` may be used -in multi-threaded code. Operations like :cpp:func:`nb::list::append() -` internally acquire locks and behave just like their ordinary -Python counterparts. This means that race conditions can still occur without -larger-scale synchronization, but such races won't jeopardize the memory safety -of the program. - -GIL scope guards -________________ - -Prior to free-threaded Python, the nanobind scope guards -:cpp:struct:`gil_scoped_acquire` and :cpp:struct:`gil_scoped_release` would -normally be used to acquire/release the GIL and enable parallel regions. - -These remain useful and should not be removed from existing code: while no -longer blocking operations, they set and unset the current Python thread -context and inform the garbage collector. - -The :cpp:struct:`gil_scoped_release` RAII scope guard class plays a special -role in free-threaded builds, since it releases all :ref:`argument locks -` held by the current thread. - -Immortalization -_______________ - -Python relies on a technique called *reference counting* to determine when an -object is no longer needed. This approach can become a bottleneck in -multi-threaded programs, since increasing and decreasing reference counts -requires coordination among multiple processor cores. Python type and function -objects are especially sensitive, since their reference counts change at a very -high rate. - -Similar to free-threaded Python itself, nanobind avoids this bottleneck by -*immortalizing* functions (``nanobind.nb_func``, ``nanobind.nb_method``) and -type bindings. Immortal objects don't require reference counting and therefore -cannot cause the bottleneck mentioned above. The main downside of this approach -is that these objects leak when the interpreter shuts down. Free-threaded -nanobind extensions disable the internal :ref:`leak checker `, -since it would produce many warning messages caused by immortal objects. - -Internal data structures -________________________ - -Nanobind maintains various internal data structures that store information -about instances and function/type bindings. These data structures also play an -important role to exchange type/instance data in larger projects that are split -across several independent extension modules. - -The layout of these data structures differs between ordinary and free-threaded -extensions, therefore nanobind isolates them from each other by assigning a -different ABI version tag. This means that multi-module projects will need -to consistently compile either free-threaded or non-free-threaded modules. - -Free-threaded nanobind uses thread-local and sharded data structures to avoid -lock and atomic contention on the internal data structures, which would -otherwise become a bottleneck in multi-threaded Python programs. - -Thread sanitizers -_________________ - -The `thread sanitizer -`__ (TSAN) -offers an effective way of tracking down undefined behavior in multithreaded -application. - -To use TSAN with nanonbind extensions, you *must* also create a custom Python -build that has TSAN enabled. This is because nanobind internally builds on -Python locks. If the implementation of the locks is not instrumented by TSAN, -the tool will detect a large volume of false positives. - -To make a TSAN-instrumented Python build, download a Python source release and -to pass the following options to its ``configure`` script: - -.. code-block:: bash - - $ ./configure --disable-gil --with-thread-sanitizer <.. other options ..> diff --git a/src/nanobind/docs/functions.rst b/src/nanobind/docs/functions.rst deleted file mode 100644 index ce721aa..0000000 --- a/src/nanobind/docs/functions.rst +++ /dev/null @@ -1,609 +0,0 @@ -.. _functions: - -.. cpp:namespace:: nanobind - -Functions -========= - -Binding annotations -------------------- - -Besides :ref:`keyword and default arguments `, -:ref:`docstrings `, and :ref:`return value policies `, other -function binding annotations can be specified to achieve different goals as -described below. - -Default arguments revisited ---------------------------- - -A noteworthy point about the previously discussed way of specifying -:ref:`default arguments ` is that nanobind -immediately converts them into Python objects. Consider the following example: - -.. code-block:: cpp - - nb::class_(m, "MyClass") - .def("f", &MyClass::f, "value"_a = SomeType(123)); - -nanobind must be set up to deal with values of the type ``SomeType`` (via a -prior instantiation of ``nb::class_``), or an exception will be -thrown. - -The "preview" of the default argument in the function signature is generated -using the object's ``__str__`` method. If not available, the signature may not -be very helpful, e.g.: - -.. code-block:: pycon - - >> help(my_ext.MyClass) - - class MyClass(builtins.object) - | Methods defined here: - .... - | f(...) - | f(self, value: my_ext.SomeType = ) -> None - - -In such cases, you can either refine the implementation of the type in question -or manually override how nanobind renders the default value using the -:cpp:func:`.sig("string") method `: - -.. code-block:: cpp - - nb::class_(m, "MyClass") - .def("f", &MyClass::f, "value"_a.sig("SomeType(123)") = SomeType(123)); - - -.. _noconvert: - -Implicit conversions, and how to suppress them ----------------------------------------------- - -Consider the following function taking a floating point value as input: - -.. code-block:: cpp - - m.def("double", [](float x) { return 2.f * x; }); - -We can call this function using a Python ``float``, but an ``int`` works just -as well: - -.. code-block:: pycon - - >>> my_ext.double(2) - 4.0 - -nanobind performed a so-called *implicit conversion* for convenience. The same -mechanism generalizes to custom types defining a -:cpp:class:`nb::init_implicit\() `-style constructor: - -.. code-block:: cpp - - nb::class_(m, "A") - // Following this line, nanobind will automatically convert 'B' -> 'A' if needed - .def(nb::init_implicit()); - -This behavior is not always desirable---sometimes, it is better to give up or -try another function overload. To achieve this behavior, use the -:cpp:func:`.noconvert() ` method of the :cpp:class:`nb::arg -` annotation to mark the argument as *non-converting*. An example: - -.. code-block:: cpp - - m.def("double", [](float x) { return 2.f * x; }, nb::arg("x").noconvert()); - -The same experiment now fails with a ``TypeError``: - -.. code-block:: pycon - - >>> my_ext.double(2) - TypeError: double(): incompatible function arguments. The following ↵ - argument types are supported: - 1. double(x: float) -> float - - Invoked with types: int - -You may, of course, combine this with the ``_a`` shorthand notation (see the -section on :ref:`keyword arguments `) or specify -*unnamed* non-converting arguments using :cpp:func:`nb::arg().noconvert() -`. - -.. note:: - - The number of :cpp:class:`nb::arg ` annotations must match the argument - count of the function. To enable no-convert behaviour for just one of - several arguments, you will need to specify :cpp:func:`nb::arg().noconvert() - ` for that argument, and :cpp:class:`nb::arg() ` for - the remaining ones. - -.. _none_arguments: - -None arguments --------------- - -A common design pattern in C/C++ entails passing ``nullptr`` to pointer-typed -arguments to indicate a missing value. Since nanobind cannot know whether a -function uses such a convention, it refuses conversions from ``None`` to -``nullptr`` by default. For example, consider the following binding code: - -.. code-block:: cpp - - struct Dog { }; - const char *bark(Dog *dog) { - return dog != nullptr ? "woof!" : "(no dog)"; - } - - NB_MODULE(my_ext, m) { - nb::class_(m, "Dog") - .def(nb::init<>()); - m.def("bark", &bark); - } - -Calling the function with ``None`` raises an exception: - -.. code-block:: pycon - - >>> my_ext.bark(my_ext.Dog()) - 'woof!' - >>> my_ext.bark(None) - TypeError: bark(): incompatible function arguments. The following ↵ - argument types are supported: - 1. bark(arg: my_ext.Dog, /) -> str - -To switch to a more permissive behavior, call the :cpp:func:`.none() -` method of the :cpp:class:`nb::arg ` annotation: - -.. code-block:: cpp - - m.def("bark", &bark, nb::arg("dog").none()); - -With this change, the function accepts ``None``, and its signature also changes -to reflect this fact. - -.. code-block:: pycon - - >>> my_ext.bark(None) - '(no dog)' - - >>> my_ext.bark.__doc__ - 'bark(dog: Optional[my_ext.Dog]) -> str' - -You may also specify a ``None`` default argument value, in which case the -annotation can be omitted: - -.. code-block:: cpp - - m.def("bark", &bark, nb::arg("dog") = nb::none()); - -Note that passing values *by pointer* (including null pointers) is only -supported for :ref:`bound ` types. :ref:`Type casters ` -and :ref:`wrappers ` cannot be used in such cases and will produce -compile-time errors. - -Alternatively, you can also use ``std::optional`` to pass an optional -argument *by value*. To use it, you must include the header file associated -needed by its type caster: - -.. code-block:: cpp - - #include - - NB_MODULE(my_ext, m) { - m.def("bark", [](std::optional d) { ... }, nb::arg("dog") = nb::none()); - } - - -.. _overload_resolution: - -Overload resolution order -------------------------- - -nanobind relies on a two-pass scheme to determine the right implementation when -a bound function or method with multiple overloads is called from Python. - -The first pass attempts to call each overload while disabling implicit argument -conversion---it's as if every argument had a matching -:cpp:func:`nb::arg().noconvert() ` annotation as described -:ref:`above `. The process terminates successfully when nanobind -finds an overload that is compatible with the provided arguments. - -If the first pass fails, a second pass retries all overloads while enabling -implicit argument conversion. If the second pass also fails, the function -dispatcher raises a ``TypeError``. - -Within each pass, nanobind tries overloads in the order in which they were -registered. Consequently, it prefers an overload that does not require implicit -conversion to one that does, but otherwise prefers earlier-defined overloads to -later-defined ones. Within the second pass, the precise number of implicit -conversions needed does not influence the order. - -The special exception :cpp:class:`nb::next_overload ` can also -influence overload resolution. Raising this exception from an overloaded -function causes it to be skipped, and overload resolution resumes. This can be -helpful in complex situations where the value of a parameter must be inspected -to see if a particular overload is eligible. - -.. _args_kwargs_1: - -Accepting \*args and \*\*kwargs -------------------------------- - -Python supports functions that accept an arbitrary number of positional and -keyword arguments: - -.. code-block:: python - - def generic(*args, **kwargs): - ... # do something with args and kwargs - -Such functions can also be created using nanobind: - -.. code-block:: cpp - - void generic(nb::args args, nb::kwargs kwargs) { - for (auto v: args) - nb::print(nb::str("Positional: {}").format(v)); - for (auto kv: kwargs) - nb::print(nb::str("Keyword: {} -> {}").format(kv.first, kv.second)); - } - - // Binding code - m.def("generic", &generic); - -The class :cpp:class:`nb::args ` derives from :cpp:class:`nb::tuple -` and :cpp:class:`nb::kwargs ` derives from :cpp:class:`nb::dict -`. - -You may also use them individually or even combine them with ordinary -parameters. Note that :cpp:class:`nb::kwargs ` must be the last -parameter if it is specified, and any parameters after -:cpp:class:`nb::args ` are implicitly :ref:`keyword-only `, -just like in regular Python. - -.. _args_kwargs_2: - -Expanding \*args and \*\*kwargs -------------------------------- - -Conversely, nanobind can also expand standard containers to add positional and -keyword arguments to a Python call. The example below shows how to do this -using the wrapper types :cpp:class:`nb::object `, -:cpp:class:`nb::callable `, :cpp:class:`nb::list `, -:cpp:class:`nb::dict ` - -.. code-block:: cpp - - nb::object my_call(nb::callable callable) { - nb::list list; - nb::dict dict; - - list.append("positional"); - dict["keyword"] = "value"; - - return callable(1, *list, **dict); - } - - NB_MODULE(my_ext, m) { - m.def("my_call", &my_call); - } - -Here is an example use of the above extension in Python: - -.. code-block:: pycon - - >>> def x(*args, **kwargs): - ... print(args) - ... print(kwargs) - ... - >>> import my_ext - >>> my_ext.my_call(x) - (1, 'positional') - {'keyword': 'value'} - - -.. _kw_only: - -Keyword-only parameters ------------------------ - -Python supports keyword-only parameters; these can't be filled positionally, -thus requiring the caller to specify their name. They can be used -to enforce more clarity at call sites if a function has -multiple paramaters that could be confused with each other, or to accept -named options alongside variadic ``*args``. - -.. code-block:: python - - def example(val: int, *, check: bool) -> None: - # val can be passed either way; check must be given as a keyword arg - pass - - example(val=42, check=True) # good - example(check=False, val=5) # good - example(100, check=True) # good - example(200, False) # TypeError: - # example() takes 1 positional argument but 2 were given - - def munge(*args: int, invert: bool = False) -> int: - return sum(args) * (-1 if invert else 1) - - munge(1, 2, 3) # 6 - munge(4, 5, 6, invert=True) # -15 - -nanobind provides a :cpp:struct:`nb::kw_only() ` annotation -that allows you to produce bindings that behave like these -examples. It must be placed before the :cpp:struct:`nb::arg() ` -annotation for the first keyword-only parameter; you can think of it -as equivalent to the bare ``*,`` in a Python function signature. For -example, the above examples could be written in C++ as: - -.. code-block:: cpp - - void example(int val, bool check); - int munge(nb::args args, bool invert); - - m.def("example", &example, - nb::arg("val"), nb::kw_only(), nb::arg("check")); - - // Parameters after *args are implicitly keyword-only: - m.def("munge", &munge, - nb::arg("args"), nb::arg("invert")); - - // But you can be explicit about it too, as long as you put the - // kw_only annotation in the correct position: - m.def("munge", &munge, - nb::arg("args"), nb::kw_only(), nb::arg("invert")); - -.. note:: nanobind does *not* support the ``pos_only()`` argument annotation - provided by pybind11, which marks the parameters before it as positional-only. - However, a parameter can be made effectively positional-only by giving it - no name (using an empty :cpp:struct:`nb::arg() ` specifier). - - -.. _function_templates: - -Function templates ------------------- - -Consider the following function signature with a *template parameter*: - -.. code-block:: cpp - - template void process(T t); - -A template must be instantiated with concrete types to be usable, which is a -compile-time operation. The generic version version therefore cannot be used -in bindings: - -.. code-block:: cpp - - m.def("process", &process); // <-- this will not compile - -You must bind each instantiation separately, either as a single function -with overloads, or as separately named functions. - -.. code-block:: cpp - - // Option 1: - m.def("process", &process); - m.def("process", &process); - - // Option 2: - m.def("process_int", &process); - m.def("process_string", &process); - -.. _lifetime_annotations: - -Lifetime annotations --------------------- - -The :cpp:class:`nb::keep_alive\() ` annotation -indicates that the argument with index ``Patient`` should be kept alive at least -until the argument with index ``Nurse`` is freed by the garbage collector. - -The example below applies the annotation to a hypothetical operation that -appends an entry to a log data structure. - -.. code-block:: cpp - - nb::class_(m, "Log") - .def("append", - [](Log &log, Entry *entry) -> void { ... }, - nb::keep_alive<1, 2>()); - -Here, ``Nurse = 1`` refers to the ``log`` argument, while ``Patient = 2`` -refers to ``entry``. Setting ``Nurse/Patient = 0`` would select the function -return value (here, the function doesn't return anything, so ``0`` is not a -valid choice). - -The example uses the annotation to tie the lifetime of the ``entry`` to that of -``log``. Without it, Python could potentially delete ``entry`` *before* -``log``, which would be problematic if the ``log.append()`` operation causes -``log`` to reference ``entry`` through a pointer address instead of making a -copy. Whether or not this is a good design is another question (for example, -shared ownership via ``std::shared_ptr`` or intrusive reference counting -would avoid the problem altogether). - -See the definition of :cpp:class:`nb::keep_alive ` for further -discussion and limitations of this method. - -.. _call_guards: - -Call guards ------------ - -The :cpp:class:`nb::call_guard\() ` annotation allows any scope -guard ``T`` to be placed around the function call. For example, this -definition: - -.. code-block:: cpp - - m.def("foo", foo, nb::call_guard()); - -is equivalent to the following pseudocode: - -.. code-block:: cpp - - m.def("foo", [](args...) { - T scope_guard; - return foo(args...); // forwarded arguments - }); - -The only requirement is that ``T`` is default-constructible, but otherwise -any scope guard will work. This feature is often combined with -:cpp:class:`nb::gil_scoped_release ` to release the -Python *global interpreter lock* (GIL) during a long-running C++ routine -to permit parallel execution. - -Multiple guards should be specified as :cpp:class:`nb::call_guard\ `. Construction occurs left to right, while destruction -occurs in reverse. - -If your wrapping needs are more complex than -:cpp:class:`nb::call_guard\() ` can handle, it is also -possible to define a custom "call policy", which can observe or modify the -Python object arguments and observe the return value. See the documentation of -:cpp:class:`nb::call_policy\ ` for details. - - -.. _higher_order_adv: - -Higher-order functions ----------------------- - -The C++11 standard introduced lambda functions and the generic polymorphic -function wrapper ``std::function<>``, which enable powerful new ways of working -with functions. Lambda functions come in two flavors: stateless lambda function -resemble classic function pointers that link to an anonymous piece of code, -while stateful lambda functions additionally depend on captured variables that -are stored in an anonymous *lambda closure object*. - -Here is a simple example of a C++ function that takes an arbitrary function -(stateful or stateless) with signature ``int -> int`` as an argument and runs -it with the value 10. - -.. code-block:: cpp - - int func_arg(const std::function &f) { - return f(10); - } - -The example below is more involved: it takes a function of signature ``int -> int`` -and returns another function of the same kind. The return value is a stateful -lambda function, which stores the value ``f`` in the capture object and adds 1 to -its return value upon execution. - -.. code-block:: cpp - - std::function func_ret(const std::function &f) { - return [f](int i) { - return f(i) + 1; - }; - } - -This example demonstrates using python named parameters in C++ callbacks which -requires use of the :cpp:func:`nb::cpp_function ` conversion -function. Usage is similar to defining methods of classes: - -.. code-block:: cpp - - nb::object func_cpp() { - return nb::cpp_function([](int i) { return i+1; }, - nb::arg("number")); - } - -After including the extra header file :file:`nanobind/stl/function.h`, it is almost -trivial to generate binding code for all of these functions. - -.. code-block:: cpp - - #include - - NB_MODULE(my_ext, m) { - m.def("func_arg", &func_arg); - m.def("func_ret", &func_ret); - m.def("func_cpp", &func_cpp); - } - -The following interactive session shows how to call them from Python. - -.. code-block:: pycon - - Python 3.11.1 (main, Dec 23 2022, 09:28:24) [Clang 14.0.0 (clang-1400.0.29.202)] on darwin - Type "help", "copyright", "credits" or "license" for more information. - >>> import my_ext - >>> def square(i): - ... return i*i - ... - >>> my_ext.func_arg(square) - 100 - >>> square_plus_1 = my_ext.func_ret(square) - >>> square_plus_1(4) - 17 - >>> plus_1 = my_ext.func_cpp() - >>> plus_1.__doc__ - '(number: int) -> int' - >>> plus_1(number=43) - 44 - -.. note:: - - This functionality is very useful when generating bindings for callbacks in - C++ libraries (e.g. GUI libraries, asynchronous networking libraries, - etc.). - -.. _binding-overheads: - -Minimizing binding overheads ----------------------------- - -The code that dispatches function calls from Python to C++ is in general -:ref:`highly optimized `. When it is important to further reduce -binding overheads to an absolute minimum, consider removing annotations for -:ref:`keyword and default arguments ` along with -other advanced binding annotations. - -In the snippet below, ``f1`` has lower binding overheads compared to ``f2``. - -.. code-block:: cpp - - NB_MODULE(my_ext, m) { - m.def("f1", [](int) { /* no-op */ }); - m.def("f2", [](int) { /* no-op */ }, "arg"_a); - } - -This is because ``f1``: - -1. Does *not* use any of the following advanced argument annotations features: - - - **Named function arguments**, e.g., :cpp:class:`nb::arg("name") ` or ``"name"_a``. - - - **Default argument values**, e.g., :cpp:func:`nb::arg() = 0 ` or ``"name"_a = false``. - - - **Nullability** or **implicit conversion** flags, e.g., - :cpp:func:`nb::arg().none() ` or :cpp:func:`"name"_a.noconvert() - `. - -2. Has no :cpp:class:`nb::keep_alive\() ` - annotations. - -3. Takes no variable-length positional (:cpp:class:`nb::args `) or keyword - (:cpp:class:`nb::kwargs `) arguments. - -4. Has a to total of **8 or fewer** function arguments. - -If all of the above conditions are satisfied, nanobind switches to a -specialized dispatcher that is optimized to handle a small number of positional -arguments. Otherwise, it uses the default dispatcher that works in any -situation. It is also worth noting that functions with many overloads generally -execute more slowly, since nanobind must first select a suitable one. - -These differences are mainly of interest when a function that does *very -little* is called at a *very high rate*, in which case binding overheads can -become noticeable. - -Regarding point 1 of the above list, note that **locking** is okay, as long as -the annotation does not provide an argument name. In other words, a function -binding with a :cpp:func:`nb::arg().lock() ` for some of its arguments stays on the fast -path. This is mainly of interest for :ref:`free-threaded ` -extensions. diff --git a/src/nanobind/docs/images/binding-dark.svg b/src/nanobind/docs/images/binding-dark.svg deleted file mode 100644 index 9d5901e..0000000 --- a/src/nanobind/docs/images/binding-dark.svg +++ /dev/null @@ -1,122 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/nanobind/docs/images/binding-light.svg b/src/nanobind/docs/images/binding-light.svg deleted file mode 100644 index 08e623c..0000000 --- a/src/nanobind/docs/images/binding-light.svg +++ /dev/null @@ -1,112 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/nanobind/docs/images/caster-dark.svg b/src/nanobind/docs/images/caster-dark.svg deleted file mode 100644 index 59608cc..0000000 --- a/src/nanobind/docs/images/caster-dark.svg +++ /dev/null @@ -1,118 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/nanobind/docs/images/caster-light.svg b/src/nanobind/docs/images/caster-light.svg deleted file mode 100644 index f8ce092..0000000 --- a/src/nanobind/docs/images/caster-light.svg +++ /dev/null @@ -1,108 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/nanobind/docs/images/logo.jpg b/src/nanobind/docs/images/logo.jpg deleted file mode 100644 index b7763e9d11a4be4e11ceeac8a36a8fef2e7eceac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 426666 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%0AZF5aAAYHE}@t}TLkUEYiHi(0GLc1P6BS`KJO$5 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

Emel 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 z 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 z z`!tlo%bYq5XYlutv$!>Y@q&?67#wr;da*+VF0h61*B$OBWT1BsEE%X5ht8vW(LY!;_EK?Aj}@8 zNk+fuj?cIQ~@UU;nD`pGBV$$=zqk6H!Vlrq~n%y zsQ43f*p5QSMV66%do|t`401_Kc|ezU4HX8F=9t(C(b7c7!gRO)vYW;~j1{)`1YEOn z0m)>~ZQ01pfmxaGGxQ+(r2m&Vb-!{|48eEZl4MC;@qmz4PW5<%+9q%1%^o(}&jg8Zcqzdr=~#7}1&^mIlvURUV7rdA}D_|61v# zRyKV2n*c}Z#K%uXb<}9kbR&@o1bu_Jn~E?6i(Xa<5$BledAlxoHhWn6{NEve3}9Q29E5-C?;q@0V9u*kQPz>IX={|~=*SXXvN5Thy{Te!~==|}Ga ziz|L|oJ(aHk@saCvN_Qg_QMzdJnd)l__iR13@yij5bG*YCoV6rotdq4_SM6KRN8KU zcFsLRHRZ_y%9NuA^_U|`997*wc2CcFG_+%cQ%k2w>kpIW5KpM6+{0@E@|;qDf@I=Z z>3=i7`LLTP)u_Vxn*W(Ofr7M?{WYt4W{cueI7V3stvsL{D|KSGK9|PCQycTknGITP~X5^63O( zI$d`YK5Fv$j#dHfrZf3BkRSEl?XU8V-a1U8j6?sL_$@<-YT;LhrG#RaAqPrO&W;S4 zxhSLFdUl@W=%Nzav&_F{8Xgo@clvIWQ*RX7l)c641d;HjZzwlDyYq``6j{(uT!&){n_{C-&2uz(T65+o}`4?fcCEI)7QyVj4I>phB{ z7Df1#{Fna#vD$0f4l8ZOljSVT$g#a}tX<=30MoFmh|t^Hf9+TCn7&={?JV)EU^9KH z=^w=cLS=)>^_gy;T7@FD{7&l{-?V13wu@^7cMZj`ip6Z@l~>$ZRn&;WqXGeMKu`fL z7`vEPo@@D3=@!>E?@o&O9lm^X%XOdfuh%;&VE$ute$a{XL#ZM|@>{5lH_1++P(DfY z^H=gA-2HvL^q;5k7Topeg_@ovXHb$A;X-*DRV;s(PyYZOdVd&P{{X;qN$n@vkG5;T zH2?vMV=M4MG8t%ol>SD;OY!T|_<@id8=oTUAq)$j@2o!_e$wm@mU@7 zYl9Jp%kFb+yLU}?Ri{|C$+yqCJ^uiAYj?PPb2WRXHuc@Q=61yO-paV^%npCGGXDS} zI*Sdl>({ndILn_-obSE&pt3z({I+5*Q&;)Z(3@OC^ z7DH{=J8H(`Qnb$JFK=64*QLfcS?ep_Um+N*j8RQ9`)g(8B%SCu?8^AToyThZygpWR zwCxAUZlIOl$zRODe(UQ;`NWl4J(h=C+brCDCdWidx!$p$bi4`OkewT{7L}bs0&3wc`~sStYjz; zksjFoSDVYb>i7Qujn=5gPcV3^CxB?<3(FFoTrvfv`#gh?@5ugNkegZ`pYinG@5k=| zDWBS=9L4^mRFRHmm8NH^z3Z>#hp@Hu)xVYAr_2m5@(Jizj2jpi{^C!uUpMYKbrRg~1fl9|;#!nyf$tIS6cp`_XB4ZqCki|Qgq_?+{ zIdG`yZTOuQKrPe9Fcv$Onh4fqMwl5rn99#VBdEhkR}vW0`BzZA)mOj#Zo5=nQ^h1^ zg@Ty83NkzLp^W5&ENXeVazrQjezaxBP_N_4Z+GM24(2p&IU>A=jLcbMc>H@mt#$t3 z)jRK@c8`vmU#lp3_97MRMP9^p>_Uak5A$LZ!144G*^v5Lzv@(H_ZMO|Z2+Hxgu8YX_jXVkktvaj$ttt#ggw%Fvh$TyvvNbWpl zV*v)MuWvZqpH^pf+dk&%EPmcL4SOf5WIa#ZdlB+J!`D~Ua{I1F*e} zm-rivpAV6`x6)$QZ1D~@9j{^5&-`1-V*dc!ui{yG?O2I%;sBAAsUvU}HZ3gP)qg90 ze(kH@{x@1NT{1Qj#>phWTZLsMN&tZ+k(Y-Df(MEN{JqA{hV=eAW_f--g<`AVU4h~? zZx0bty1V!~ADKVLov(0BvTJ#5J1h7BB8rBi3}(M=4G3cu?WeEtl0fg4QzF8_ivbam zDB*kgO9>$oxrgYBy*FHplAAO6`vZRYB`ai_3r+tX)R|L@a9lRbA${LYJ z3WC3t%|7=%)W4N}Rs47A7y4g9vVD|~6=yVD0?+3~*WE&ntg50I)rZUN8uzgJSsi6s z{vx#N{BE>V8UU$Q z87{Fp>vj}dUsK|>yizh)S`zEH{dZL;w@bG4iG!1wT@cz$12%Kq4jJuPZ9@7?rQ(r%u1H?0UAvUFWCP@bYWeepzV6D=0r^P!EU) z!9w|RU;Rpvh(F6Np?#R^dS(bd2Wj~o8=+oO70C-6ii9L(*bgBbXbeOd)0*zjj~W-q zOmA|372C$GF7wyhM_Apd3_g$XYpLWi{l3Z?xGyB<%=>TdU$e$CGKY^=#0$H-D}|ju z9mBdt(fokJsT+!K0u=q5&5U3%3XMTkXi4HchcU_#{zm@*7}yTT%yrN4 zDV37@O4G2td4ok?DF7^ZDDDCNNcwVJn_TE_!n3=^?wys0)bRS=qn*QEX7V=oJC8@~ z*vlDmyM*?4mYeP)TG#t0Q=bKn#&0+|u-o5TW46b`bcLm@-tE;_H<`$KjWh@}^oucB zeS2Bk^7|*m?z+9az4&F2j!*ubY{9HP6JPTe#QwL`4i`km=}WKUB+uiMT)WyN&~g`> zUDgLw*zp$`+>Up3#OnLq*3XpP_1$z$itPz@5rh8#XwtErOJ;Q(Z*g{Hd;M#jK|W4? z>a@sph2QbP(K6~9=jklOk;95$0wN=ik{=ZGualU&&*CFyNhJOw_fb#$N%kiae-mAt zlz)ky)CS-9li-r@f)?xebs$Yt8~7yr^bw*V9FFSl?@shn3LQ+H>&sE zueARF5u1g*_r-7H?^yWvlwrdOe5s$_9?Gq7#Zh^we^nF0@%Jpi=k?rH*NW{A;!g5x zmqGsk6}W9nD#etT=m-w3Oz#I6T?L`oO6Fr|Q$NWn687cfpZk2%kBl7azbk|Di-3P0* ziyAEG#_DF~>UFg@ml1`U${VNG@SemG#E`Gh_4Xwnl8hcq6k&+8{x%&xZ z^D-qo#v>QJV_7(oW>;xZQDfp1m;+3xXi5|!h>4Bql>qSel_TW4Ii;1>mzEFZ!nYwK zVNw|ZJS{u<7xPmSsHc%pE~fWe$gzvXS%_wqFk?ahBpN(;`2ZsX21cii1xxxwX->{) z$lwpUxg`0L5g9~^EW0zRKk<#15Alxo6KQv>4WCufanHDF8`QF7 zH7x!H9Ms)uaCYntg_yF{@fyZH({dTCZ~g%e+0}UbJ&rfB?XmilJ*(r1*X=e8c5Aph zX4i6`uR32gn4K44ifG1SJp1pKHgN4WnP~NFN5<}~y!naQioP_AO-M}BkLf&XAkOQM-z3wv7Grlb&24>g zbP^bvCgyql)QCvlZ56l`;Ox@3F_Kz1l!`#=7_zAhXg9iCvM;=bp+4&wBzBTVB$8EC z%{s8CxFhYupaMa{pz~)|N>vU7s)Lg)MGB=XVx@?!Nyu2(id2xRd4N2*R`;pdxHE+j z(!8w9?8UidRAW*Gq$)?`8A7z23P{0c{{Tz`9zNhy3+|Og0b>M6rCMTmRwO8phG!H! zqh)V;nno0E0*{lw9x>k`^)8^^vOQ^>+jl$7%KjW3zQ1LqY^|C)-LG->m(X?H-bTX}zvgw^uFI6k_EuqJ>|Vr!C($|%9(|il ziqkRLU14pb*I@4$&b!59wXvVXYW;?odmiM;<8r#(xSiuI7VDkJc8)*&>gzvsv14(% zhNqV9Z~UspOck^nrf;$FSmSZO+vP5`EWyl0h1P^wY2(j)tKIsit8hIxs-Ozoa#2AN zC{f~LS7A}cWN&Q(`!yIVB&4Y%vW1h9S(znxYwiO};#zKLo7+@rQ9))c zz%?KOfaIiLtfrZLwvs?hQF#_b1KJ{@>N7XN&++jJ`C}c^O~82_!^U zU`TH-0s?Xw1r!8F#{qezhwQ_eC?jTmOmJ~c8uHMU5ibf@@ z-!eZG(zGTI^y$2VvX!U{lBg8iaB6p&Nbi|r6Ds-7b#N9Li0RJAv6**1z)K8rXqK<8_2%odDAe`)Y)(H<>zfWTzvLjfv|QzP;a9vqV~;&roGk^!uame?1kAjD+~-d+s&&=vczpW zCsD1HzME}-mb}GbY;h3BicAj0v+Q}^`PjZSd~w12Yt{pBj8LC!^6qF6AofR$U`D_O zpvxHKI+b;)IppwBRUW@ek^0Yrb0ZMN{{T|XWC_ZUI1U)n#uOk6QgI6d!k#EZK`+SQ zIB0+oic`okD%btWRhFbD&G;b}HXmU@OVXoqT!-`VKJ!Ydc%XbK(Txbes1-#x_Nbmn z0MLuu3`ic|H1L5+zi%HP>^N{*g1XalAn|0njTxXVeXHVQ#MymYyJfnEJ%W=N7B)z(Ar{LU#TeZ^M$>^HW%&DK`yu=2UcF`ZkwYPkFD^42^i z4+*w>pxVAWd~ntNIN;fpD6w>6d16%=mWKj~Sb=XUbM6olDg}I1e@BR>Pt?wQgYwAy zA12}ajRILAn8E=2oxpj14 zLa{O-G*m_+`-uEnpMyxCQSoBI$yfXGSzSL+PASNcHzf&SQilL9D-rOfN0GnuULKM$ z$xj+Qu<&5%5hdZ|viMS`=u}pxPH09H5E_VYAZUW%rz{XCmxiec2MB zH+hBD>^4ih1%@L^yvg=1Ivh_{Kl@T zGp7fiv`Exjie&^OYf4#5sbdnb3mXt41oC-)1#rAF0#67E@k$v&y*koHI21& zCPW5=Yr=UYGL{kfc>8Pzw!8)`&Q(~2qJhhh0|KHT(bQ*}WF$ur zq)@bxfL1o8w~>Ifmy>5FGe*=SDhT480WQB_SW~Gg6}TXQ;3J^OjwVS&kpO4%6!I&A zO+?QXOF!Q2eAZdug|6@Cg4l@Q7`!mNys$!${{TSg<}(<`oEC^zy0fwIMo5T0CP5mn z9x4w26_3iL95O~tGN_Qn%?9F>c^OG&U`THfN{YwY9DyJY_A(YF70pE(*R}eJ4=FrJ zMG6XV#4`m@4+}ujFEz&v6lR1&$ivNX!1nko3279N%Bx<`W9@Q6zYKg?(C}55MvuUW zLGS^e#Immx2ZK0%00F;67v_~fAh$866sV+oW|6=Vj|$0RqR-jjmm$^rAiRR@cFh{c zW2-E-!PfJ2cVJpsK`z04k=h{I?RHyd)G;+{yk&J;+n(Oe?Kiz$*?9P%-1dje?HH_; zz3sNwnTu7eV5p|)?4YuRf&T=`wr*I0rZ^v`0kJ04p`O}749_3I7A za53$>?pGHUzq#TxNH!k!*f)tjH-8xG43-${d}X)nS=aWmr3mCwt~p>pbaD#FR7VI`aR1-+00AD8m0QR31uP-Cw0!QTa;ra@zDMFMR%77@5D;kzDUI5pyC`42!B9w*? z&n}NfH)>Rm#x?O(P)G!*QlKa-z?J0|fed6{(4JBkLG8M$22zwyxlT%~K#?F)P zUO#pmlAuux^kM)=00uK&%dzlfZxREkqK*%b2ix*Si~;1vfpJ61BN%QaEUJW>bda=v z>QEp6NPc*4Fs$w!i||L3l%v7EQEBk#{{SMkZA&ksT;Mj_Y`dIfcTsE|Sb$n*^SVvU z-IW$%e0KAFicKzSO@+2SD&p2GHNCC$y1q_W+iSlzIec^y+hjM*GW#!p(01#=dxh^z zW)^FlwZ_qr+O=B;IIO?>US~UTahR-zS{dyo?0jw-49{uivd{Z#?3|rB+5Mx^hEJ3q zCo*x}cMr(&F*y$Y$~c4ML{(?l-y<+F+I+bAABKDn@<-ZuDK&58`v-P(kCvT7*i7gO zBQ%5tdSc!q%f>=ggC2Y&j)xux^wdgLknqVWr3MC-$T7xbsNw+3o>cHM58Ju}8BoBA zAo9xG08KP!%0=)I^2f!crY_16sDiJh zDmdL02iZZSLTc*z>wLPz4A6~B}|x@5?V z6~Aig8&_B*)YIAQTC?N#dmGa`Lw{LK10QI})3C?KAAD;} znZDh$+cLQB;KSHWlKBUXTz!W7VFS(BIExj$?z;<>&G)_DP8fHq%UAyZeA+V9nw9PB zmRe>e+0ztpFy-(U5n4xgZ-5%)657RWXO7EUMT?XF)PVYOLo zxQy0`sOK;9-BF25ey_QA7sqDlpX8epnS2&o8@6NHkU=Y4w1qrF{ld9duOGNSXJDYP z0lZ|T1Q>`_-nUELRx7^Qy4ZFbJ-2GttFQY#HLE>iJ!`pP)Kq@05qa2j{_Y8&BM&}od zw8Yxk;WL)``z%h;7RQvyYnRzFS(gF1Vm2I(sN0SyuQDBlHuEko38=}CwX^Kp7K6Ku z;=q9P{{VL9%9#F;e&Ek&s%x)SOq$swjA@6DTY%;;GVX-)Cy+Yut9!s$-dcQY`zSy#wwy(Eg@R_Z{Ny=b;QQ1J6#^Jg@qIsOQJX%00D+(vizCV#Kw4nCgSv-Er{uvv7FGdlRJ6;%&=BOhL}|o-;bx z9SUqDvSfA)dw=Fn2$y%oy5;g%+_bj+eTu$I{=K!eMeFNXJ39Zgw5mjY;oYxZd|#e%hQ5rg{a5bQB?Pa>yXhk&1>Xt!>(Lw8%`s4 z$GY-lRso|G;zcZF>Q-MMM+n~9hGjlKc*yaMiJ+?^AROc}R+u{uy~bT7t+vl$HhRZX zp2KKwCpp#Ca9KDq7IUXzrpq?JsN?e)%}cFxFZjzBtK;qZx@=_HUsu}NWmGh8tdB2JWJk;vva&D3+$(!|h-e{}Zvd2(}Ob<1lvNYWww8xBT7M-g61 zYT|Wxt(}%A*vz)dR=XRr^(~)WzO%*bml0`q+hOcGdre-$vQ8sR$z|+GZmrqxbJkZ1 z8C`-ro+gTyrSb#7GX(G;sa5?Wj%m-|!19okRX`|`flP$?NNQkQc!-ZGv?BY$!-%*A zKnLUn-}~Lx)3R*c*OS2Lm^*!EDY9hlPQi}E;IS_*$9(%vGhB$`H~rIIn%}%TGUl@# zgWFlV8FhRu-qCg*du7FDu~67+#ZK#~$FSmLOy^r{B9CVQ}GTUI*)3S7Js_Sp*?4(bT*Dh{n z&1sUncbck1ZZJj@lg0qLX3uenhdq*l`LcR?v$kqWDHipzH(9Hcio+dc40SF>>fZA0 zXStl~*6=-^sBP;7q>R0`q5ji^vze@OnJqKpo`11t*}6|1*NLg>mRknDe?D~=qB^G2 zRMwZU@|Fp#X(`lL&F?>r-fjDDul6h3EM6CX7|k25@>qrJn|W~d`!1iJycryZA03bF z4u-qcqxfU;{x03NZX9^{REOLOLo8){qa&C|ryr_0JjPPm`tu!}yV|cZkZqUPiAL>? zz+$hYT)tmq)9&-z9(G)X#C=Pc&Sh+6Qq*BC%!iVzfbhkM% zhsx(V&hx9gUm4n#azq$=V3t5cYyf2u(0?UR4I-2-ISUO!L8#Y~zjN3zjLpX3xZ-ro zZqKhnx8k-tJ%x) zDmGJ59w`SE@&eT^H;6KK_fTc;@m7}DE&D#$+ia))Gk+vBk=m>GI8i`1fSl9gsyJE`cdkO3t1n zKP)^z4vYZ=pSh7p0S7L4CF2$1w4{OeRq$%=j;F=~he@fRa0FiJ{cjTf3IV92S`-R~^(5>oenUmo~bd&Dgn7j>cbV z*)1ajlIn)kZ~9|?v%Ah^po2)azLI>Em;8f))g{>bM$dAauco)&vYS*lHrKs}*V{hr zi%g*FE6(l2-r;pTmQwErXWl(-Gz;&FzaTCCLTRkY!j&|HH4nI9!>aHMib^zm_Yy@O zr>o8}(C0GnLpB=IGoQ1y*04Fn)v^tT*R%{4R@T#@wfhkz!!QVeM2@H?A*i8hiZ)`r za;gn(didJ)-Db>f7%Wx%Q?B2u|9s z@gPWJDIg~Zr!Bf<@$hBh)OSoC=C;{;E9%*{2r#x&;V)AOCT`m!fa`sdTn61VW_HlF z{gY!&HtSN>a#}W@gX^5XR^xVT9DOM9jY?K5PX@iv8DvEX3dVQ{v#ZNY%{kTesj>on zYSk@q>pwr!>{d;EYV8UHxVPmb3p^$%MAJ)g20Ft302MTsIXSI0>^2JzT9(_8ZG!&* zH+!zHgZ;1c0Hc?7IpB($-g{ls`&0KhR`1WW+^B8TfJc`{Cl-sxud zyy_JU9I1Ka4&DGF#6Us?C+Q&9$=!Boa2B^%t0s!s_1Rf2=f`I3E-lUeZL#@0hA%&v zfwl`<*;^Mr)1=LJkG9xCA&#P*_@0f-) zAh;4v!P(R8?GO|6lvYwx$(k<;^YS5n@+t}dNFr0qJc=1T(D9!?ogW@#>dQTUuW(r?G`+Vu@f)-7*;_q} z9zNj#rB86W`(a#2Vo({#THC*HxGiSF_WBHMvbeFkw6lG3-vg#DM*#+_kU5OLvoB62 zqkWKh2?O*~5V8K?fU=Zce2N7?j;gRf4Sxgm&hfKi4yBvb9?w0EyvF8wPiWBhY-eR* zZWF{e3}cN}q#;O%8c`@h=gZn0r!cHs8ei2XT+Ek|+v?c-etTQju{y1}7PhcubGap( za>xXOHF2<7-EAJup_!SHVZYw=B;A5x{DF1rJc-Ch*$cZp6RS%jz+qliKmVnfwwO)l1T}6-1`3j2i2LZM1Sr% zYs-t(xVP+mg5wjjUE|+bj$2!dS4iQuiA;&XEIIP1+<@$$f816e19K%h`H07}O$`O$ z7xAJ9F`*PuR-j=JKg4YvauPApw=zEK`2?0;X+s0-ENt-js7?uDKw>MgdfRB(c1>PHoo{g#xwXky;;}bb{FM8A z(qeNPTpAu0EItvGlB5QAX+kSC4&2P#oFgLtxa}azyAOLySUygu5fml zzPI~km~ad#49LVu63Wt-mS(jQL8@Y-vbW>{Io0q5tK47jRj?l zcmW3t-rw0G^QMWBlk`yi2ixSy$3J0Fv1&pVr3=sN@{RZ^!oEr17A$T3#r1_}v1?~0 z8f^Wgt{N1!ymrQ8FEq0ayjZ*aipC2J<%V86eYy5qxG$u(Otw1LIZ)-UA=$8))Y+5c zz7cON!xaJiX%Gfx@^kiB`EeK=$e=fas{#c(D<5=`arvM#0vR_4yhcJ?fGPJY72CPzPzzRAh{+a{@Z(;E!` z02{l-;_-D?06O@o9$x8|o9x_*06i?GCG1YFvg`Z~cTd05B!>%cd3CO0GrNTN9-G2y zIpg9-fGXjMXbS}#k1A&ODL9No-c(#E?EO|<*HY0laNp0m*k7(sK$!=CPpfZ-+2nL3*;d31>_MgT3^~@bQjqz@_PKUNjSfV#}K)S3G=$` z<>t$f$y#j}meEVG=HrkEvig<=>phjxFEV?UM+>vbyV~k@5a#-{GCDkL%EmD}2dQ0w zdym;AqDt4edtnW~^?45l0p+vBwn(_1B; z34S-?rJfm*D9b7=j>w;CRZ&bq4;LfbWuO_4wO-$v$rM7oe3^+#i^LY=nJe{hA@Z*f zs?JbH<;ftKU+)=LR6G=QBRhtNW5wx{-8MfK(8Xh=++y;XO9@WPuGr_WAMMg(U2%B5 zGT!H83xAlkmEzp>{1!szV9Q%wT*HU7*le|Xm@nWO&YyyZRKZBV(UAE2Tvq{AiCTy- zuMk>S{{T?>H@|~0aC_ID)^Fxz>FzI@5o)_ zqOv_f-nz$QVpkc5*<#J=_n1Z6?d>LyBiUD3#nQU*;k!6YcD{RBRqQ;azsG!ZH+ii7 zdr!Z!?%hiM0q+gz?|5k9}X41_kz(fJE}HK%iBmte8G?VW}TjAvHgU)O6i^*DP8H?<{VPvnh+ zz}K@n-JZ19*Sjr+CFooOo zRE!_wWk4wpEM#UD z?#g$qeoY^1oI$nOhmzj+TE1MAThgRg+D_kZzhGqGw9)yu=`^ewy|P(La*>v36jX5| zH(V{9_M>lmhSV`qUIvolc)h&dS@D**OFOJ2xZJFJjGa^@$nP<;$k!S}<9AJ0V$Nmr zTCM{%iOu!Sy)GMJ>Y#@UBbBKf`%_G@x;$v@9M;mrCXP%@V%{I><%wk-frV^+!ZBHd zE+gmOgoU<(!^YAuLIBPw<}N(gvJj!Wm_$ zYgD?|toL5V&2AT6YuC74GY_-u7PDMnE-bq{ZOe4Vm5`^x4!_&KBC?$Y<< z9h8|ZC-~sn24kohn6xTcS6F`w(T!mBRa_VXc z2m(-}$O;Ht1mwhOHw|6&ij}asJ@CAbbB`5dZ}PbdiD1_~`%jHKbawekU&u=rm3wLx zMG``lXQ=#YCi@{Fw9HMl)-x}uVYVDyy8CaLoffR_UfL5bA6&M{B)8J^j-4z!UP+$b z->q@E4HH_i-iCWy)iabYG{6|tk^&{TNSM5yc?dsrF_wvvy0enr z5&c;KhD!r!lZx{jED_sH`(SvtHjf$vW#w6>VaQlq(JIB9P(#CU!baXoIZ+Vf#Fl8) z53+bL^0CL`Nf~P0H~Sq%e7vu;NtzK{pW z?gAZ;feEvH3J$^>*zT)`nbAkEMiF}p`cBIcA7o%|I`>`b4wA{^u6Ky6wVAr3Q;T$c)35b- zqw9Pu`MrMoCz#Z)^4A+6mczO-Svy#ML0Q_3C%&N9meXW?uP3*Evt%K`YZf@mUZ>La z{PtTVay4vy{lpo)avjfa)}iYelS#JTbM4w$tUPh%Yk$vUbgi^n<3=-TgBibL_MAf7 z;6IBzm%D^5!Y>K{B)X|A7}5fys<$j!lsIE#g%OomnxQXm7%lEp~? zfgAEeC;*W`5?JTK+;zrZZHgO+%k)!8ce(++)X9R;TAjGrInwyKdsN});S|K zGUF72M}}x^%xeQAr3yp?08}bd5#*o(&R&w#a*?QG5_rVUfJ5b3EBj0xrO(J5CCRz& zEw&^*v$;H%TNdH8THoPpFwti{hRkB7nq(OpYiO|-kV|Q?tTtHBj+u#9EZmA_0=$I+ zfQDK^Ia0jGz&)k7f8g4J>ee`{Zoz4fx4aQtMRAHuW;1KqHp`h#Y2>%X#E?#$bMl*-N~Qi`OCQ^k34K!QP0T&NtcJS}tL zWdZ6dW%5{u6C$WHGna(9kV(vOu?~e>f#dC#3FlI(faxG!T8cA_zCPD2Yq$Iw>Aku3 zZ{sd7a=!rkn*!IpPx4<)gB92vxz^C#{MO#>p8MqPV?USF{&vTRwcNf@Mc56me5KVe zsoZ}cu-jZVJ=1~4+huzW&nuM6_QjROwkvTK)sfzy$lYc4JT`XQ4$)6#2I+O@srL>_ z+O_O1e)+N&+eZ0m72{ZI_AK&Zb}g2}ABx@a?}A%<{KktGrH4B}&ghmf+*|(uv+m8h zLnAlwUm!9A9|^%IWP@gqr*Ey_Y7rVwh%N`G9Yg@g;2RdRtJ?7A3&AOLjV`m~@ zAqW*@W-+qVPvm1oNjp;5;d8c=g4Y_n8(gwnPRD9|rPLXXt5{X*tzzQmC6K?0#@_cI zh|507rr2(|TW!x1u*H92fw^v>5#@Gi?iLX22?yL4F)OTZ1ZN=?yEcoA{r%m%{`&s_ zTQn;;y0BZ3bEcSu#f-L+*{a#F5xOL*@uY#33X+9@xBV*=U@)Mr5n@t%GqT~(BA;KE zV0ePzGgxf=KHS$b*Nv=mnIyRw&}3A*o66`468r*Z7@TZEmr%!HK%$6`+WZ}8WctMtV zy_n$5WwpC|eWv2dTRGm|=MyZr%j?`;Ge*JZaQ8SokH_tausP}Ejxfv?m0X1X08Wef zD-e{buF@YONTpE0`6#NPQZNURCQ|A=1Zqs2%!h>o#YdfZmZgZszUxgui5Kt4x0%}i z03~CNcHFh)n%4f>_L3GdVO6234;0H%a zlK%i3JsUlL{{S>PDXLH7E2E=k{xkYCDE>6ML5=V6kJ553U-+c!ON8t{;qWu zVq|CZAkvEWk>q(ifecE^?b9;5B+5)gMyVlN`2>r8j5mLkYnRx;O1*dB?gRsY$F85svU}{k&BykELr1#9GY&|xRhxh_fqC(z18n>3b4Vl zSxuT5$IZhnt%S=XWnr_o*Y8st?AR%;HEY{k)-84W33G8x9C2(I^|Q!NXRK`YyRH%C zB=|^`8b6<(fC~%bJa{e*`xBJiGi&?~P1P+g~MmQe98^ zGVBaYnrHG|)_GRZd)u!28X1w*p+aaYEqkA(d9N-^h-3<&5y_`dAdxGUj7ML{heP5! zE3G=>6M1{q`VKDVA=TSvKHl!~`YzGgaB|_cTyJ1vvfDOK7rtC}g|6d@$Kd9;)-cA( z>uSnbUTt}-hTWC!F2l64)c*jBTojnDr(oVuBX}yZ(U6rwMFu>`o@eZUuaJ>Eib5Q+ zcvZSPETe#oOM9*2TgjC@x-8K!cbb7{=|57k1yxW2G6?K? z*jA+s)<{RYTaiu1eo0s%=(V1*T6O zXPmcfLG^qFyZ-=^VDPxyE^6ZwuWpz;-Hroa#A?>s)*}UHkviTR8++FJ%WPLSayY4F zJiO5|%WV;oMqx%vSZhv%z04&Fe__tFcXvSyj1{7q8ROtANZ$BbR6|e0h*f<2{{U28 zM!R0+a~o}>QemKy{?jiE8&cZeU1ly~S+M^AdwovhBUZMS*z0|Q?Oz>#AetLHpWDrk z$6stMWrvp~HZK`zg|KLN62=(A{{VL*kifDKjU1rYe`K1+PQDEbe&-*bk#d>5b?afV zIZN!OWBvCrbCHTXx@HqyX!7?r_xBQ6S+dV>b94Uy@vJTIIeYzQErn}4IX0V&jJ7(x zsJ3o{m}0Au81WKNi^iHROn+)Y`ju3aPZ|Tty`_z%j2ewXgDirg2ZQ3lNClX46r5K) z)PTr$t=kFK*gp07Z@c`DztesnD<1I2$Mo#kbx={i=Kb#sHk#j{x9F!*e4%V*Q~80>AP)1_;dGxfGw z-s2tEc`RnVvtRZmLOC*aR(Qw3cQyw4Id@D>^OyM%wn;Q!7_=4ZE}#w-Q;Ez&iku*& ze`y+5YE$B}DtM~^6#dnQkK9p1%&|JuY_CB_n-vTXny?Js=On@$`(6~Wg`hx4pNeJ- zp5mDI4MQ2&c$T-!YFVwK+8fwoNV6Cpj~h{GnTna2IWiSdKb1Ag#vo;6kUuQ)BSCVd zcOJdB*zPhp+^(9?Z8jOGvY2b9*e^CAoUBf(Z35Y97~M}Fs@TUYcwVI@b9`J~M9gl4 zMo`wyk8$k96Ibn{m6lNBainevO7iO zR-;b?K24WqFi8u%2;ai?qdF<6yIf^2@@)(<5$Uc~oA(cbc}x zTHIkWdn}oZrJf!>sOuQI>l*x_1glKWL`wc%G*2rS1S%4H$%OY&_BE4TW$ulw_w3@c zOUvv}`)giYVC9O$NhAw>X^)#-xmoh^&u1!GzCm!8iFa*mBCut+xVE>GCvhZrtnQxq zE}gLUH!c;F`5}w+RGw@hg{Vc%dwh>*5YZ5P2{IvJ?S%|jAJD4r98^j;Ak=I;!9*F{ zjz0sB?;hvbep~LJ7&`YA*EtT=w@1QD67{~TpyP4Y*qpBEuWH*K%W;I?4!O2Pv6 zZQ513Xt_SQys){-Y`L56y}7=~L5ajoYmL#hy?WC!xt6W7sBrlFPD?lYJFc72r1=Nj z(vZ&KO$B&qPX=M{7YBuK!1%K?k&0ACDv!z{6%mS(%ockY1tbEq8EkI14i|3McAAnO zvc?sRYD)$pHjs&Y*_BEt@@Dr>(833L@VZVPQ&~(JoHZ!R7 ztZPdn7}Pi`cM*4K1k)pwll26-Y;21)vd?2>EsU17Yb0SpkL)>1SuVy(z()3}@EcHW zQq|>}85~Fn03!K?QmT>A0M~4rC)mzP!rtOr$UVDTo3y!{O6Kn?XN42&S#7r3=TX^j z?w3MqD7Eaaqoph>y7s~A{<6b$%q~D>k*0S6k3XqcD)QWvZWD$9#~rb!&%JATT~+ow z4M!n#`1i$JU*BxFfv+7~SG~wfuXIkyWeh(X_1*`@{U{8l#(No4EAg{$!_Swn&S{fd zO=)vH{{Xth$C8sLwB1|kSnj{Z^;VNMi-7X+Xi0Yag-$%T4eJBfwhOp+X5zBxotYxb zt@e&JTeJHESFJC$^)A7ZXbGty@+J=&cvZ>qu2_X%x-XD)YW_;QjqsR`pw%bqt#47& zGFe-VQw5RjD}9$?&+Av6!D(d{39Yf+Tdr+c8yl=1FSl{JRG4new#nY-J4xr-E;G4U zueZI&FTUzJQ|X%qH(N=%R?D|f=GUn;zPH3`oik}?i`nqAU#9!Smt zt1u-8lNgvcuvmyfSF}8QR4T^LOk;vS`L|iSXYIRy+h$K>yt9%#J+anpHeJ5Q7alO> zv$r-|eEm(h0Cc}3iY6`Fr!%W)009w0dEg4)Ng>jr5+)MOd~^dJF43gm&}EZnD+CDq}Ju zd!96`{&8|@NmvlfE3k04*IRa90+5qzF4`EL{qA$KIsc(IOX&1+vDdb;{ z`cOLeUuv4W%Qu|@^)q--WmIrUV;Lqv9YCt=@t!#G5j#l1&yMZY7!B@OdLLwt*}8XT z%g?NJ4xgpZDtVV~2a%&IDI}VV8uM8bYxwu9XQj$~b>481V|s&rYn>yhlU}|+#+|%r zT|iOGb}sp8*I0cQK${VFE#;m%+J3~x+-Egz&eeBm9R#bnb@mT?j>}`-I+$vkW*d3N z$O}m#GgI2AsYd{h)EUv17L@mv%l`l{_oC}JI*toX-?dDvT20nZ9a&5cOKXwxnOUx1 z69vxEn!Ls8Ih;1g%AG}SOpFNhl>)g4;_8wL|{{Ud-cSv&^ zrJffHVcoX-Mo4Y9xC^_kp^GZLabb|kj=#uPl3bS!|nM zU?h6>Qx{o{fpTz}Z~FfGF{q{W{(CE$#%Hrv(cfY*kjZP%EbOdf`w!A;X%H*>w@jkWT-K9+fynjOWMg|O$*tN#R7B36Kq&E5 z3U+MNcnnWlS3e{CD`t zzkH~ae-su?7H{~q-f>ZFzZ$+sbzF8m$CJS4f8;w2s&(&c{cg(#+4<}U>KJgF#_QSG zO)F88W7=D6wTlt8wu_|vgRs8FV|ya!cZZ|$c&vmskYl#pfD{@$MmXk@KN^nJq^yr`+~p?9-+$(kLD~5|MtsJF zebrH7b5?sDo#ZxoXg_|M)ut;kai!hwAG$Hit7I}&(zdef*K^6hB=DCC0y;fQ`S`6E z7Ev+rGq{GDSz{qTRFa73u##8vVz%;08JKt?FOgNZsejUVnTtHBI8bACXYndUAXN@o z$n&)m+%$Fjffxl@?k=L0oujs>th}nycAYX3`-Wlquo^hMduwF2U+lSC#%PqzP8dh`Z_{5O?qsy;&ZEQLXKi*H zT&AMeoz8m}+f>Tg-PmLMryG`&Ri7ekAiuKA>^Y9R*)w==$Vp7uFBtSwAeq6{{V|O9ks9tLsB^5o+K~JnAp@_ zAQ8g>%P%Ioe=T!$1Q`2G)+-AZ7Pi><<`LXU(f1q1nQ^Gghs#>*cAGWSR+g6K@=#e^ zO<{C4TgxpP*Bxzzv>}$HASaOF7?bWF6E6{oqhMxO=kjN$K@}M*6FWd=lrxECxO8uF zonwvSj!zYd!C6{3{S9yFAb8Q@mBD{_zibEuqJ`GT$-ZFiMD;qSv>weU-ta|4(viyuD->z%_wo~$ilx# zGBc{HBP%%}Cm;v=JcIz%;7ZBQC8?j90f|Xf4AUPgi-RE#rdW(#6=RJS#L0W`;uB7m&@|>bG}t>o+-h_MNi!2(ED$tlXG%N71rZm@6y%JIMKFyXwr%we_B6 z)uCFENiQUdH;r*O9dE3&IV~?U*tyv@tXD|scs$mNfx=C(BpY>}w|QrRj{$)Ainf;> zciCOtg~sIeUx_=0?QPe6&)t1BE(7@^?5ttdf0H{4=&*mthf-w*m+}*=dn#hmLVJ_q zhgNr>?my!9eA8yw>zIBjznjY%^1J&l2y-s7^R*)p4+TWZ-NzV6)i zk#F&D;`Yh%?`PKa{AQzx6MmItCAcr2ZR}Xv zqQV@bUtHZv+P-DFlKXPN%hnkT?dSeSHTJ)0l7`MN{DkZp)>8$8{nzhem4ct#FN}8# zwG_m+{)XiEMx2s{BP@)iB=+7p7D|d#thZu1l2vHh@n(Wcxk7&C5TZ#YSCUw*m?hM}AM-yDdFPkD87f~XJU?}3{S^1JWTY@S%{lH7Pl1Ajn&Q$Vw;Kv{e38^^dQVN0h zmw>zi7DiR@b}9g+0jg2OOCtn{+Lc*{x#wIGQbQqkDjstfNkmGX>XenqcKr%LX_i9J zKb1!6>UaU2O-BAq?7mztft}H3vKbq0fSdPPH+aPrrg57WZ;{m#7d%$CdCVyP0BP5* zYZ;c?Hi@(gtUhhBdmc{a!85j4d1c8-Bt0jWlSj<7ZW-B}$fbl}*UsLdz3KkV>&%8X zZi?@#uCY1Yj~8{-w)&TP}_WsFb zw&pXPL(~b3!2Cbd^ZNe)Wqek($LFw7MF!9^=DFjmquQ?7JQ8F9tJM`3H`<#93xy?bxkx*(k0puHHY;5>aP4mG#(vl0cfbtycFC*i_{~Qe-UU6nMk^%6 zyqKQ@ABhB(h_qFIleNNF&y=~k5FNK$xtik7QnG^LYZxNClH#vom(OdzvWp#u-Lm+R zkFy%4Mp$BTz9I}x_Z9xhBBCPA`h%86oHiQbQ<0kvT>VlBDibaVtcT%OQ+}{lg`<*xNFPK?{rHbtSdI zjip<8l4YJdj5d`XM%ghD#}siaWBW|j(M&fiRn%r55iPV{R+1~U!dBo$@q(<43&~Pl z2x)^-FeclcALJ@k9NGtZre@Ojn(5?V%f2+cN*G5 z5XeJuYKx^}V8UivMYz-T{Jqxiq;1(;E~E0BV(U$JulnlK3yY*$ER5K!R=3jy~EP-+HdJ`Ca02IwZ*lC<`r?XuFsyn)v@;T z+4f$^h&Zh_8@YBH3vF*>&gE_L`t1GhacN`M{aM)m044H&8-Iy!9ejN z+vcQRYsdwU#8;Qz6*5QuiT* zw98sq1C)jw)!ry!Bg|}BeFv@WGed!fxbv=XW#`G`ig#x6L1QbN2!spSxBfMoYkRpvsb;YBpeiX2D-EYh@isDn=3o3%hk_9 ziI!jdY{z`^!SUB5)=|izs(11Pl7)k-(W#*VR>lV|=q`;S|D--`bLARrgL zCEu9$x9HCMl+E$eI@ zv#nxrouRJbH*J66j@y#yPUqx;Wa69_rBlQ*yfxb28#>dx2V;K}HdhO{d@k?qvRszQ zvTH3DVx&UjZ-VMrJHt1Pj^5{a?K_J^S+QnRCQxYky>hV- zc%kFr%5XtuEO}44kC9!+oP#Yk`-r*6J|^}It&N@J)@do5tgdr*5a{c$?F+NREGsP0 zw-Rlb?|L&X8?pV6^7&Y2soa+rDDD&zi^1gPcz8z zAVW(xFDpt5{{W)Oiv>Nyta41!+TBWR);VFduVyx8lJ?d~r=4MiONfXl(mL0Y_{k$9 zvw0HX1Wd6F$5FK3FuhN;UFLBenW)8;o<+2d8LlIO{@WvCd$!{6;}?gzv9Q>(S6T6t z-Hv-96u70on;X`%J^s6_%5Z(5FN#;6opa#I?g;_N9~%)&uFkE=EBd74pDt<@;)(X@ zfM9@9u~Em9MH33l2Wi-{nfSDl0AQ2eT87r zte{JMdfo`&x0&1a5Uc+Hblk8{mbHauM|*~Oxn>JHB(p!b19L1^#wKYR+(8*ijv)iA z5-UQ_98g}h+uhnT8(S;e<`}!YQY@D-QMw{CG?UeeB(b=T+DLDf;LUjiK+Vk=nJyme z32o;yu@Q-0AnY#0upbX|#agEga~fmr3fb$Ey=8Ul+chg3Blm4R7WSo96Sp!kv{0yg zgGl8b7LmX{58IfzbX>ijM&A7P4zqi!#K|TZvDfKppkh)i-cB5TLoIupwX?**COY3k zxsu#B=Qp%)5*IH>}LE6G)26cj(njm#IF zuY}Ly^l3XQYD|WUt{QI<3SiDOn%DzZ@H$sr7; zE<&kNq=O>x3`K#65T-N{qM-;)CzAzIp%f_`l_h8p$ARH)3)f+5l5M)3p63o;S$x_t z&s`X<{{Z8Mh}>Pvc?;UsKPeWEb&|uTojynQ*;{-cS8RJ-wgU%_)$#N4r`!EfBCAW` zC|h{lgrREccDWNbu+1`3+p#v0OL;Up+}CWHH1pJ5a}q7ap=)*$F}XnWQ<{+xXa-_=6hT_r_PjQOgSW_JK2&!6R#<&| zSnXZc6}&)jyDh@@9o|Ppi}=#$$u&)EFfJzWC5f+bn8t;vR3lE_K&eo$1t19PjsXB4 zZ^D9kE3XwIsH6<%$C6N$Rq|OBt1P~4?K%!ek0e#UkTEz%(soQew!aR|W2>zDPE$Iz zmP1<@=o_yyS;9%p-dp41(KZ{5E-$8anKG9wcqFer2Ze$(GKk|BA7&&)Q6JnJ#+p`k zk>u62Hl1b0MLI!=x3&?)BLNg2N$rHi5FoD+&*1Q0c>8`|-sXX33v7uU#v<0}Ug&9U z9o{1)1ceRc~yLUT|=&GR*xfX zoxoj3Sq*$!#<2;L{mG(KyfH~6<|nndK;l_rlg`FQM3f8$0(_*_S_~JlW3_ur2?eBH zAO#+|Y*aZoZ*2}StWWMGNu*&UCxl=_dEPf7=3hT)xg;R)U6)$M?s&^QrdO--4SMc3 zUa`h(Etb|cTuxgXpNi_n;wh(Fi`#Pz_BQ%han?}at*{pV0PekvWc&Ds2@qwFH=pJR z2wU?=fv;(+hk8@xQppvh9li7OtGe8F4`X(}%v{%4-)j9)hso^Nn|SBP?iTg5e2lZV zwD^bc!zo#~KmnqhG%$2@>sLEX7y1L@peSZ~?(`_zu z);NnUr_{0|JK(chz%SO#8vN*kf^~@Y&;<3wR;+RxDaX>2DyD=}8jG zGTN=CROPg3Bb_Cc1NcVvjQ0iPYlxLS04}QXB zTI(eXv{@V5Yv7cOWwe6}Ne@KJ{4iNpAw<4Jls3~A0NhqLNlYvZMlvYE@LhzE@_!2~bfTzi13HJzi zVIg4@!=0b3vANuxzfEKElNpjIk&4MQtke_k9P-L0tA_HYk(Nt~&I3w-#>};N7=x3R zD3Zcx&ZUA(eV_RtP(PuX>)cG1DJ>_CUv8{oWR7C>WejN@(0?TGmPuw`7^GVFNu;<& z9`i(-4_0Y=RIl;pQ9_*uriQ?4_+3uj7_jjnO7UF#XO2_fW)9v%$L$i111}!okqlt6 zLKL_N!BmO3g_p{tb)z6jps#c zX-tr;*2rhPl6fJFq!IaeaU^W+=v1SYo+#ZUBLozzS;Hck)wuu>97QTX z$zCzJ5f*5q_XWW5A7Bx_;P_WQ$}FT(%HAx(N{ECDFN(0m~<>f4j6ttt1qs6+pR*@L9Lpn&!Byxi^64Eh9n6<2tO)@o|v);TKeWo%yI2PU? z^Kv|fLdiw0YZxOIm$QlEkij*a%YGp-+(N99qoYJ3j#Z5n<%N%mq-<3q@TT)X$BKeP z5xl{|gdi&U4h=feOdPuMRaF_?-nS(T`GH5futFX}wStt zN-C8uiwSY=!x%N^Aq^2UTgha;_bMXqL?ofpmRo|N2^Q5ZUfxKqZrQG_HCgT?!)tbS z_NoGp^P8AV%O!!4GMds@LmkRnmXRF99}gfOb712(-TP1#XB*co-}B|SEMQt z*vKQ9J+B5*Q^lQtRVZo@j2#<+3YA!yin9P@J}K>`bL8*JBr4Lf%^FiH2|cna%fgEO zww0=O{Y*d+y0t49)yV+LtblkQ{kD;ZkTIHx7DbU6K!scg*_638EQOLLi^tssa7z>1}HnvA?uzyAOr%N#{yk~z)Kl$9i^THT9Ryks%lEO5QRNQA2- zvQ2IypT_P?HZFVV^{8!(R@R@lKj-&$>vtA&PKN3`y<+|&XofdysS+e2b8i8*ca|oe zDUXpTr~pD7768aygH&ktl>+zQ#_$62|O$J45e5- zG=?M&rbdLRHU-4L=OtGvu&@Jf#0 z$UoI)f@gCfS)l+WI57yBio&IoBdf?nlnbK9pc!}t%8FmO1NHdJB0v?A7LB7~%x5#)D~SR$*7Kwj-Nx9(k0##qQ;%=N_+oKVY!YfX zM6H#`Z9EapZqc?cS-ja!J4&&~ zrJgPvdP0IY{{ZMjw&CQHTCccp(7PkVhf^0HM;_M>A0z(x^ejT;rp~1otW2-&F+v2UUk}8fo1u}*f)`Hxl0iH*6JC9Bz8gRiy9TWT`NExv_nkz4AP5F**48Em7P zkwOppFUK{Tz%E)mwxc!$d|p+1xS>l~-KV;^irfpY2;yNNhA84$5UiGjc&vp_x{Qj? z5RIPfq^PFo(nhTbl2$Exf=Di-;!kt-+>#OP?N=RZbAB#0Ys+-P>u@a4W7-*-HFNYr zfKmjFr}OshU@q0b2mX#4duP5})}Yxd{f7&4XK^aZ(}$K9c99m=MvWqr%4Cj6Qqdg> zxM6QJb2>~WNz}z0cR^ZbE|W_e4|;otdF6Kf&ArUB!z9w)Y55gcGxmvWhuB>6skWy}YR92aDno=I0}$!cyx= zoA-7a+X>~<{{a3vEVYlv+^p96-c3w8mBfvb)h_=4Z;I9KVYbC+f+=OxEG$Hh%dKm{NEzCZuk00;pB0tP<-{{X}NVb%*k-15pMr^h z{{T`qXo!LQabjcJ;t6ysSj0V>M5T3 zp-9B4c|4FQl{A40Xs2G&5{ay0UEO86Nzbx^k7{wRqK{ONpLgnh^v#%r;3_`(nN@;2#V-1CYak0$WY+I2AZ={0g%mU z5;oqCGP5(1m@}D@9>9UUJspZLP>Om~kp_c>=N7PHP^7pgL=#J5j^dmrr~pWXK|NBu zdMEHuk5l(1I_%J1LMW4OvxR9<#P-PmoJ$iSN7HN)pRPc5X6U;DAh|js)$=i zb1Y%lg%ud0X|Q>PO+wVgIJV@~iCUgw9o8y{0l;wy7ZDbOd301MAU_kv`CNr`Q%^CB zK&i7+eJ>-mWgvrXH4L>4#XT_=)8-ax3+p0-#CrH6NHM$7F)Y$PNIYKdry-%#Q){Wv z*45xWPq&vueUIE9^3hND>NDoiPwhX~K$r5T5fk>j;%KZ?4HZ*KB-9@(f%5?>shdP2 zPd1#j(psSy9f)#E(qL-Bk$;vc>4@=b?oUiGO)Ih3q94^TOi-u^MiB1_h;GzAsd}l1 zsC?Aph%zBlncUm4W*f!p11~U!VD}rB3Ilukcx^(USdCdVU#==0E6vG(QBPuzv66xy zwjD#2t&0hrh}GG$&uJwk0KQY$eH1|(ijtOrT?EoWa6w`X;II2%x*&7F;j88Pz+BSi z({TC3L|yX%m{{L514g4CNd=RZ3)FbXb{7oZxUxK+VRkuA)M}=n?JM0;PdFUJJ4lR9 z+m{I=11{9Y0>Th-vrtzP5TZPjc5Jyaacsw!PEL%sp2L2_Z)$oqC^(;MiH|YZoP0+| zEio!AvsF6|X&Oar04sY?V`u5MMNVOebP{T5m&D~2b-;-hDYS)>J*yw2T1eGyFQgft7rF*y8b~2=hZl=Bs6|E^W2OnjZ;Mh5wGWKYq*;7j)6!x!WGkj#2I8NnJ$2ZfIglPQ z8rX?8`!|&$b$JWVa9Md_uQr{hQS36*92*KX@&k)$w0~{;I~24JxBH5IuOvVRZf7iA z%c#zqsHztbzPH;E%mQJXnMM`U^Ha7on+mI!K#3rS{+l|nwY~J2BBxDRK$uX zdDYCq@kohZ_TR(-S%2JSJ$o!co8Y)&)R(&P1PON_9JGhan)|R{;jeW;k8F^qnjw36 z$vrSA5?m6*dTh03<EfJnlPU&lkiWJpslN7=u7^77tHjh0G;+&vw*Ltd| zsTI{`X+~s@=)rO-6bebG)mBg`dU;{qs5i})kS%gADvo_14A86yQul#o<(5yYCov*- zr)2W(=wOdBveb>sAaZb7Ga5;~JvBHj5rwJ5jQ1uoDV_R&eL>R6gom;VHBh8X-~A)Z z*bt_Wz(@-{ndfqc6zU(#5&d2}R5T86BBMoORZ>{|Mg|CS{{Y;nfByj1fgknq;cyI~3B@%PF%}s|)f~}ikvM-UO``B(6A9gER}f%l zs9Zsg7wMvoSc&eu?hEPB6P70OU5I=vN1x6#^LYe{YN%32GnD0qa3NnP76&M%pyaU* zsrzaG6w?<4>8mte;jv$W%SnaCBh@j2ld^dzsLaS-qK7V$#=3x1o_1mgiwCMuXG59F z%}fha4q;gsAKvy2h;sooP-@6$!v6p(i>x*S1k{$g=uH|-MU?>LV0D6GqZHqt;n=QC zOz-seVv23<7M;jZL;%(HpKJB#92J=V08M_COk({3&HH-lqO0jG+hN#2S$_MLIfMD~ z+KTtn@*UYza(T4o{v6No*SJ*!{+f!bBZF1%0^BtLC#G)Ey&g>@y-%96I;*HQW8^Ff zo?QfHpbBaPih3DK>PIxqtWmjG=-0G_8hE@Y@QSCqqfSEU@B~?quEO$x>RL~Ft z!E29I%;tzylBfy5JE~qUKs(@#fOw`qxDmAe*8c!gMNw5#k?e|#5vw($6>=QTW*xtx z$;oSlRUEXFbJ((SIaO3&ElvLbr%*Xa<{47P**6rGT;SCjK_F8uWgtf(F`vjt<7Qw_$QF{o623ytX#H>NPR$7_%_I`<~ zk+-OzzM-8`{?Y1sIY$*&ST>(2ubHr>A0G8H+I?3IXJIZo-2u%X>Ka0FPW6w)iIf_|Q2)X-$)u_OU1L&vpI_SF+zKz&YSMj5NE{0EX1 zR9WvfYX(%&K@$R(n0SAwt}C5TVjObmH^+;xEUY00RWJp?q`kyleZg{SFlsov0;tqP zWG#$`Y?0;wYp0YNnq(@jlgG@eqY7xEqn9zAQgW%UnL3S^VXRQ37Y2KgBe8PnypV@G zoXtsi`darcXEv70SJ>UEhbrSf7%4q6une{VU!Hj8MKHL z(<;4DukLE6n@=$O;59-d(@jI=%IAbq)W6IvRDGW;52r|K>LZ7kNB*BB#}!2U^+K8h zfJrd=l!|GqmPJ5{KobJ03qaFVB+^F1Fo!SgfTq09^(losuxFW}TZjrb!fA_Vr-rJm zIY|KC=yMJEPpTxUe%F%kP;x}Wz%ms5&ppL7Q}rI2jWqKsFHJdwTPGED7N(b`F;!41 ztWzxDur(VwV9uIE>XE3hC5q+JYp&r!#%gPPu%qUp zqHU$cS-)^Hf~W1i*Nw;yB~33A3Dj&0kyzo&TRn|P5WYy#MA$}9)+>YApA zUs9+Ita}t?J=*sukjfoI3Pq$2)F~1HhVF;cpz#qC{6-j_;T1g-Q&U2YVc;|Vll-<{ z+M2qat0@sb0f|IDT7QW1Huz!e{-+|NMOV^Ww!$zda=+Xu6jf(7kkk|XbdQt!so}f? zIkcB8!(AomBFR9ELNyAq#sQx(x|WH?;E&vM&k-4UbXqE^54xX}PSX`d6~=FggioSs zjJE6OxpWXo=5qx!kLUxAt&8dMz;e`wkOPXkjugNZ^(v|93^`!_;$fc$KOo+R+5Nm~ z<`GSGSaO~!NA&XK@ADIDwLGe2NC0j4rcSwSVK3OJ0K@=MU@{DQz zw*DpMRV;4>-BCtHNj zl{AS{P4|;&*nqG8pC}lQ-OQ<>P83o&kMb>I`ZY$LVIWrlRW(x+O;uf0EJaqztJMIu zU!2zL02WhJvl9Z+4(h78b=OTr>dTlmQ#t`_N6S}|roKl!*F{0vrf@HD2F0eF%?gke zTP<%#C=u=MhhlRV5!*D^Nim;L^qYY_gX7RO830dWH5r(ld%AN|_7wfM+fr+;veeQ$ z$u?6+ja3)`fIoHqR&&ArJ(TfhAb%#@v-7CU%l`n}ql%C9e(FEg2HijQq`eh_)Vu{? zW(J#yW-T;mtOfN0f+c7h1&Br1zpUXN zsiBObL@4kPPkt?L-z~ zuX+N|bsPJU&?%~BOEHjRu{}0SUHaM7HAnh<$3m=Fc9c=}9J;>pgUsaSA5xJ~rlUcD zG?5pl=5O51Y8>yf`-=H=Rs~dMZg(!KOhKZjrl^cdNpXGeXDC^=yDdz9^0H^cPb^LV z#-iYvUnNZy)H$5G!8CIz2A9k8QB^19u9y-Wf(wJ7QOgFZsv??c5j9j`02h=`F{=BH zXac%{`EKP>e}yOKtN#E}yZ}$l8RBzRL-ARb@8b)WN~Bm{I}PISSJ(RtysFGi66II3 z{{U*Q_zl#f$>-8&tV|yfVFtn=xmAlRr@XOL(|BasO;tbz{{U_F;F?|n-eJ7mNU#_W znxsDSoe@aky1MPC3uve1q@SLyFO_l3_Un48kzwEZDqm=fo5Bcoov0hsLN%HLhc77O zdgfGPmN)?H2GW|9NpJDdNTT9wCvlbN@@b1iJ=2ahJ*qWY<%>eVJj!Z_Ah|ePDfq4l z33;ZlK-l$sJCiKp2e-5%UoDHA&^Fuu0GiQ!k*215ZwAyVu2IFYq+enc&}yZF4~Vy7 ztCy0RNEqd+mz_5``GnqUHF19_b}ZD-#{=bYKs3l7s_Y$smkimJYF8ZU-7c%fZK zF*1avcd=QF3vpkD1H~{;)Gb5$eEJn&$FQT)hKXqZ0Ji4;0F&*jJY2e?ke}bmwv+s+ z95qabil_b0kU5;XKBY8)Q&c&W{YI{<#Tn}+s@^7Uo3 z1n1B4+P<}CHGGp#GcgUA1N7^v_NtnJ1h!x^TcG~{yM6d>X&>UF_j2i|e9dP!m>>k` zp~;DXG{cBOEl99>G+ht2=l#Vw%nK63`;7vsXx>S^kgt(YG?502i|MLIY|wo`Ur+96 z6$=cir~4|MJ_gXae9lE#`C_U`%?jxNzRHFoI4T&95*{~ zP-+8}Nd6C$S8aC9eb@VMHm9u9Q07!m&CKS~MOkjAmsqBXb}#-<{n!dkG3D~Bn~(iY zWjDY5K3R!8L^Cg^NF$X=cm5#SO|{kGfJgni{iN{Anau^wW=5JS{x(%#?v*V}+@|I6 z^2y@mRq~u^KeDVw@K@IRa{mCzlk@V;)sG5?4OiKun=^L_oxyOy<%J{FXXcpLKXqP% z9~8m@%0!395{KM<=gToq^8V~FnM?!(tb)u}*2Uv)%rywYAPD(+tdyV|4Y*>XWAya(RGe z`evdB^&gjWu{#wXOuGox{Hp!Owpc8ZdJMRZI^12-=*7r}+tQyTcVF_WaIa6w^;Of@&qHqysW+0@Qh2sB(FA z9G})?d4}xYw(41ZDSd8TbbqRm^(fmKN~@X6KPLrb$Le!P71jX5Fk|Y4B%HLEkqRjn z#&xywnP9*8zxK2KF%EAuPn%cnrkbsz`B(eoI47iY{@$#$4#F*_q5G*egW|u3G!7~s z{w`ngrw^K&TtyHfPNh{}FoAiH0`2zSBu6U9JQy8^&j=$-Y9#G{hhPYEIm~LXSUpZM zxenzNO;I3g2__Kn-c~_4oD3okHx-@tSb?f1Sg6^rV$3%u8}LriQzK$8T}J|)!&o)7 zKXQy;w0slgee6g~9+ua+49ysCyj+~?w*(&0zs6XI?YupRQO%~Rxo$~++kRH@JA!&D zG?=)a!aT(73Z7jQk{%>U@xe7Vc|i;mY(x~vlmonKKg18yda3N%4B-h674--Yv1JB! zBh)|BGp1kKeGivJ)HrHAhiLN)<$P(6$GJ%M6~jU7VcyC4zxhX*^zB(`3;a08nwgQU2%4`;J{4ij92EZ8g*dbk$un z(rC*uRU||jNq>+KLjM5c{{Y)*JibJ#sj~Aq{{Y=>9JBmiHGjOf4@3dke#QR)clTp0 zK!1*tI$m|uEG-IYJ57n;0e~jl0G&qwfTwLW5yaxr%aFLhxc$zBWM!vWYWJq3M%&Kc zO$ReD8}_455oxqHB582pqKP89kMRTt+2WW`Y5xFItkjkQZ(@Z(IEZP8sQ#(JisAse z5P~9GEx@kyD0a7bL|bW1OoSa~F)~5`U2yo|fEL+fEJuZ!7y+`xDm<`G%3ymFQW*ql zgy8**h^c`FL%K+f4vhw6L-vOear}!Cdd%k5Hnyi4k}+^+KvkgAj1`zxLG1lJ$RefPZbc!p$$y&C3~icJnHQ8!5K4g*(V! z{y5Lf6Xw*={@>fn8y>65s+vjWS4eVYMJ8ZGhm=Vy(R&f6?L|j7%xVFy09+bu!Q#bz zZ@M|FEjj!K38@U)8f6NMW(mUYMB;)&31dX9MAg&-zAis93ivxqYnrIMh!(xSy9&2u{o@dJi1s>%!B>EcD ztD@ho)ZtvFnz+Tr2Lhb21`kXd04#havfFmIV4_&T-rm_&;u8tp%m&9zR8Xdwbq)0S zhlJbwPocKyr}k!w4;lXehac{^Z#_Sw_S72cxL7Z!%9?Q{4rkxgIknl#rT&}U{JEzH zQ?(9X+}BMF0m`I+X@+4vs_y1-kSIDP%Pdk(j54w643<6fOYjmMXwso0Yy@akX!p< z-i35Rp*EAxu*_h90G*a6Gi8!=N-a6Lz`cAp@cqQm%z503tpZcxd~x0_pvKhtk8;ZE`s(Z|kj`js)}pX{bw zs&+>DipVO-NwS#KcAIuAw9_0$W3{9ZukIsj7@o;2ys!WXmVRjzjC_R7;Ii@u`l|A$ z)%Gmal1=B{WBOcuRw$aIuZntmy?j<-2T-+mpiz`NirSu@62em0Mc3M@pcoQOH^%$+ zs4;D2h08IuH6@6V_3}(VTzZO{n&GI8gs=LQcBt1;-c!1tN9lc~2C^n)MFRTkJT&;+ z3jYAO{{V5So^@h5WyqxLJY3WL%}?}|^uErL1cgnM0d*9>0jLWb?{G8i^4ZaMr6DtiWM0SrFma%S;{$;(?P0CDluoa!HXgG{{U}5<3T_KXkSP896FHs(OLkU|Rcj z-nea^!G^dlDT4xE<^C{~Y-uc@79JWShiz{xF%rWZzuJVV&h$LOwv%-VkEh%G3-7N zTsz)CYu<$v$aQAUG;qTedGS%x6b1eQ#PuL9O?n%9Q@R3{E?VP@)rG<+nv22iWd*zr z)X*o&{{VtuR#Ck568`{$@ent!nf4=BCM_{&7wo+?1e0+1Nea1yk}R=D;-jN6wn0K@ zn!^C1SJW5Va(CRbRS39pxIsH*zS5qq+;CA4D2k*Tzs*2SXIqdrEn>Js5qx{o4x%Cv z0xx39(76XJ-WVu5czy!)5Ee|~&t~%^X`+^5#qU7UxnTo2uP87%Lc(^0NBmcWOP9>HYWGjrX|i1S-D&yRYB^c*f-v@7d^0hKSb=p49<1M z(65+W$dea0k+K9{qzMpmnx>C++8)*P&h5HyZ#9;YF72f=#LU_LpH%ng6&8fG#XymS z4`!~MFu>i7&vIc6z6d_xE?KG!Wwoy!Y72zN1VFYz*w}nUt2J;pUDQ-mbTVY>Tm9}R z(aT~$dF$AmmJcV1+k$s%F$2`5q`Sbp4!0>fggcR-+7w}}%P_t=;D<0<1O37WwrP;6 zm__y*{t542PKTmNMcI@i@-U` z;^Pu;Cl$w0wVxaDy5wmfnw-jDTrL9i)~JN?7oSf)q#r~jZ@k4k&DN%N1~9kRxn`y3 z+jFeNgk0cp!t0gi%q?V|n-6GAWv^<@H;<7{ElhSJ%Qi9IAbUoKI_y~~q*P6X<+%=5 zFMts3KyA9~@L6-!A1l41lHSI>OTM;7&9s06zJN5NU1QWKy>V`;JCzHfe>(w0y^1xc5AOCae!Ja zWg`=Rnv)CtR%kiaODz*{?{XkT%y3Mj1rDVhKf6U1Ad&lCB@*R?16XWdSDP5Zh=2HU|PdLV$G?Dz@BlS*wS6U*n~Qv%kp#_ZBicd_s-4PxMB%X!qcq z(8~+P35jShHzxtjm~7SyiegNS6pr7RodT_3xO-%{k79>XZSO8tPEmx3zX5Q^u{e9- z+;KRssmud0n@HSR^eMVq^klx=Y)pSd=z}V?1we1HM~L?iRP`ClTay)-feq=*Hm5Fw zKnW-)V7kEovY)j)Zj-Y6Qus1i(MKex>RMV1+lR0Z=)6ck};v}=OO2GVW)R%)a# z?qm%X>kn0ynVn{Z0Q^>Z$zdF%!_wVL#QWBH%wED zOmJCFxGd5ZCmmEeF5D%k5tZj3R0%IDa9k;mYJ^k%iwW(->je7hQxm$~o~ZKZ4BfE1 zS33~`WKUZ~sAeFMcZxQXZ^Zyhw~s~o8L6@aU9kXTqb!7z=#8?+U)4B|llbpUOe9m^ z)jiELuVK<7LT=14-}m zO-wp*(&a>Jf&$1w_fKkYZaM(D#-`-*v8;m3&*qrE(de?B#{2rG%1k{a-ky@hJyeDk z$dkD<_Nj!_?<>_p#m6#sz09iQQdyzu0g*xh#kUVz%3>Y+6o9~cCOREvS>(;Hu9hMf z+Wrf|*cBTS)oh9am}73G3W#HORHUbOmZuezp-Z62`xey&mSNZppSYt(DYG*Ie78vp ztfzGL>dGaJ-r)@ot0Ep4jqp0MddMCPeh-f2o!(L+FD=vTU*rtsfezAuFJ+LLf#L>RC*k5bJzyad9fInnX>g# zn3M@~J3DYO2unx^2` zFTBg#>b!{H1P5v&P}gb{iEA~26cEoR6HGP1KWZ*@)@T|7hOuQgbw=k%Fg00l#}b3y ze34Lm8_w{JPR%oB(Spc6b0B1CwEQL(YHjy3i34znOv%S$j5Z+UiIy7PcLYYpc^A7- z>5W&yQ34Jz5t~4$RPcrMT;l6MlSKk1!NHcZQ7z%s9Mepw!(wv?MBIT6+RthLL7DZ% z6vc=zBJpTtpO?iuVMwqyiWHL_XXKdskQZo-5fnob7Xc=zgDOThQ??;e319?Gz}c{O z*nxc0fwk=O!5T&1yNX!<02YH{fQVtR5-&J@iO$2>W(u91Q9^0~J!=HR`4QZlmTBV1 z;vj_}u$z%6`dE!d06=4U;B!d<>K`k+&j}!DF}Tm%T^22@)Ayn#-O1`ZtS@?Y1Re6g z+2Wi-3ovCB#khH)RV0IDqj*dN%x!y#HXHCw)f-bZT7!9uvkBYww{weBf_Ij+%2Z^vhjw>W}vkViDZGf}QK@(N0Fols%OyG42sv!Uc z{7&)4&KU+=L7sMQzK}72ngB>Lc3r*mP9Vg0V4JE^42l`!QIMNsQShY#e{srj%>|TL z@9L_iqS7sXDWCz6os&045Sp|LXJv?HFLonXJ?PR2V~Q0{rWBKVEEA7rQ7lpeutr3~wjNH8pp3hxJ)LS+S-JeM%J&T+d;@%>XBCUY@59E4~E)o_8iTNA=MQ_p&t`W$M1IVm3XPqD-~#KNXUbm8>3ix&Y2S z*6mClmm5XodF?>gKB=IbTdY^pXj9^C+vJTm5ZA_O_N<2q+pl_hVrq9X?!wF#TJMFC zS)K4;m?F^FyptZE3paY=D~JQFn(bLy-O34=$v=8ah{2X(B5kz+L`QzoQ<-*x*jaZ~44<_MYJo8((RajLjWvy}C%*?xRwHH~ z6hVlQ?DMf4#@I}XVI#N2LD`cN>Y@oz3?zvWQJXB3xyHx4;+Pc(n*t=AZ$_wm6fSfc zY#-u-34W?C6vwe-AEL=jP6i-r*o7pAnT4x0ZvhB6jN2Z~sxIeP+@KRnWvPiym~~L; z=W8gI!x@;DF*SHths-u`1OwI|Y7kKxw0NDpwh$fq6OXG0Fs(@|~ z2_ix4c_%HkI4PY?+EyrhR$_prSXhZB*O7bJGO>gs1_M{N*e|I`6-nakO~>~Q$VMeZ zrxt4k!#pA4o=s(?jL&}5=4tI&TWR#y6v{dr^;F0kE!eY-Mcs+Je`rxEUk^K-9+u`v;EP<8g&Dh@r zYo?&b=8SJ>KzE_a<%eMGCBG7#m=P2%@hmy() z%~23g_$Rlj*U_;ZT~kd#!*ASBAi~XW(dK}z+!F_+ZNCdanl)8ZnG8NDiGL-Mm6@qK zQ6{flgIFjM81VpReQyB)MK?Syw{7b)ME9lvMhLt~vhRupv1DF);My{eg3VtD8o>Y{ z#rCM{wL_~0DeO&Ei2y_@s+pVYp5dKlw73i3qm;Q?|i%ce^ z;F;r%+Q~T+P(4#-x@m{8hejG9q0}@*9I;GRP*I|uET%1ez*+#dPXL8?_cxWu3QJ!P z6e>78e9>SWMoqJB)M_c%NJ!pd=LzE_OIsZcJUt42sTpOi7>OOp`_oi+ryUnZpxp?b zh1jgFO^Jjs*p3z>TNsH_WE0dD@lNkaGWN_JfqyWSnfvqm7c_EB(ZSTJc2bA%!|T3n>C8bTN|pk zLy{Gl+#Cq@hUKu(&8LCBq>zU|B$MkB$rcbr5bh z;8WE+AiI2_QD=l+?**2r31W4UXFB0-^m&5GxQN#EoJQcYQ!*SHxR}~L zC`MYgIExe2x$MLXN@x>i+;BzUWI>U=zA4`rVPedER%&3-I{Bh`P^bwX97Y?EqnS9& zHo|b2Y!T(!0(%N@pRyL~9f*=ez0xVq!9P^d($uav;cvk}ia`CN<=<&5gtv)CzG42s zedl0N0KN#C0dZve6T~JZ;+ogD(M0h`b~|pG5ICQYq75Je@3k6;GH-yr{1buz-wn%z zIFlaz>70fTY;X&XVy+&<^+NL2Z&83RhhiYx26u(#?TMxhqQhg0u|}Fi1+xd@JQEQc z?<+J+{{T;oh*eCj^u;j{eVvQx(jg~!2AkiyJ&2Zh&#)X}v6fLyg8i*zHcdH$mSZp< zxiv-lsF6ENJCs-t=^km*lnrBLH{xyzGqYl5S!r_|)f=uGmkmZb+J#)g2N3{)Fwq1H zuTZRSq^1y}iZOxmPuaZ?V>6DW14~pU4Ws&Mrwji8aukac zJ5g&jDY~hSju7~m_FRosyOiWd!3w`|`A3=ZObkr?%8*P41u&(cajSViQ6qi79~1_5 z$^b0$Ts0l#(HSWs9k!x!nsSK)0xpb-ZXo*$d=r|4WdfWKR}g@esi+g3kW5c^s^XuT z1Uaz&w;`!OR5-gXn91A1`qK+}Hn9Sj51Zk{duw%s&ncNEi6Hck_` znMN^W9fTnZEck^L=79o1-(5--8~bAh!v3$AB1XQczz>C(timk@#7p2%s+I{z;;Cj82o}Rw6kfIvl5c${W*!|snp+^e#_CV%PDeNBs0ere#z`WF)*7vZiTm|6Sm=keyH6!(dMEwGaw76 zrh^TT^J`XmWu^hJ_~>``ZAAP&Di+&y>$|Z<<8{7>8iYS?0_4Y4}(N{E2g37G3*+My68zM&EF*=faah-*Dyiv|Y4uYQ14=1Ohw z@=i6}n$dQLC6>(-3S&-F!X*i;JT6n8ipXuW-nc`w&+P)|we4vj1)bX7n#P{nR%+~( zvGh)0@?{OX)0OMhHe!ViUfj0`{0*t9lsi*hh`n{m#W3fZ^+M%dVnLC(89wxIL z_H0D6z4qgV>sC+&GL-dXNwzKwdo1%@XDEX`F(1>n!8w%gB}Jl`DD_MP>^l(^zyl0v zzgaBC0LA@wC#G5oieWEjC6=TOB!C1M%k^2R4`;=2f$+IDUf1$Qnt*8-NfFA40v5oS zmbMZ$6vs16nxfyuyf1cNO^yYhw`ypH*Iwk~z90?zdh*97d{R7Eh*#XzXCyLy3rsq%#&5GI(Y9ircg&Db#3XjXVdk2A1p z6g`uL7>h!7vh=_p(nKdt2UT(Q5!c{>w#)XT%wYEa02jSyt|!1CMV`&wW_=8y)v*R7 zNintsW-K=PDY|~KAb>G8qH0fPif*vnrtJ8v(`yB%S*NHC%3Wc`X@G)buGNr9MKJ3I z8XRed{yGo;+5iXv0|Ev=0RI5@q(?-$#e#=e&F@k6G3vGb#r?tm0Q!J${-mSRiib)b z>lF^1R63=Pyu;bJ>g~>$^@!)D{RsYTkL3o_>82l?AM&l*W7LF5P$5sFKf;ee^uws^ zqBUHeqIXO3+r7;$QS+CiFPf2h>%p#c0MMXhJ zLuepC8H8F(So?!0dkCm^^da~}e>co_sq`4hIE_9Z4R$uV_@L`B+L(k*GEq@dQRuke z(qf^~VWKI5he^|!Cr$x3!=V2FgI)Q7{iBk&W8k8DAfu)Pt|0pt3H8v$X6444uGjRr z3aOaawE#mFh}OlB8TTfpNvhKNT}eSdL9{sOFi;91Vz_3aq0oH+#bvGt5~6k{8%_adzFH!l$M8`d>xOz6 zM$U`qeJ`RY09Y!6ye*7MG#Ip#l?2bB^frbG^hG*xG1M#jHWM=SPJNzArD40_rK%2} zly__K+?c@QhnDUO$_1HCmNynA|4PD2~QdJBub zKLwVj7$RMsdikSVtODRPIaI#WZ>OwmNoZnmbT z4Q95ZS(%m?)#Z9(>|s8`v>2?_#59e*n^V}CF5H0PM=u2i8;%-Ec23QrE zn;=Hn9b}DFK~ywjr1EJ5)lr*f7jC_+EQCcs+8+6B^KoL!R}Ll{YS|^0sNyxBci?dR z%1z15y$r@)KMs@AG2e%xfMI!8oBf+q9cJakU2eO3R#UttAEwokkqb^2DvpDIj@uI* zr^$Hi)@$lj06tOYw5QNfSSZ+u4SB}`vk1Jf^-omImwZ-EX5-+Aek(AEG!K3Z!!hYHR?o4G5US z6buC-Kzwht8cyd?{0={v4lw92wE#W9*IInA+wk*D>~1=Kpb;JtP8M4sng7+ zyS(%m>5Ac)lFcFu5sDQXC9TNCW6%_oT1nXgb-8AyFWT#h58?4gxUMN%j)!(mCR3)c z=rz(HCPjDMl5#BY`)6iX1Fg^&U=w_?*Sp+>b>`l>vWHC`hT1%|T z9R^HO6zU0z1qh|&{-O55n`JJ+88G*Ehx7|^Z{a}I)W67Ixe7{8^U2?bOL1r$&QCv zY7jHJtK7hh3gRFJ;ii!0ro6+4R(DW@<&KC30{;L+03_bwqlNuKoEY>RSb-kM)NBPc zA2nsk{xsNdHVeY#(@AKTJ4E}H9Kk0HFa{5_MSUJt^RA<8qw_DT&fGQEd_x4{2Jx=F z>n(o+q!qKpK{-uNYKnH~!7)InO)*SVIxV4*7otv*nP8Yt#Xg52{{W?*Gr$Pr7j#Xo z#`gj)(rIX?nOHF=`$Zzt0(+5ao)a)FWTy97OFJMW2D=9D%(3ai-blSMv!RsB!=%*A z_haawTwGH~+;mha4p{gVny;A83>IsMJDt4H2H1=#knm8AOY3vD4Rznp%TkO*hu1(9 zt}P5Dq4YkM(Zrl4QVKvQs3>$|qMnigQHpo%(H)7DH2m3EH6V~P)L_Et%sVb!JiBJh zL)*pkRT&v4LFNsGwrgr077f;ZWrsn!FYh_8@#wLLQ|(hxAvHax(K*u9M0cm8zP619 zgJ@q%DK!QFv1Sh)cdx6?iMs7|$DrkXIwfh>Uewb#S+?Zoc(b=%iYeKwXTcH&C z27>B3XQLevQ_$-s2h{^hkU$`i+h=iu+t3`yFfCLwRA{OOll%c-)j^32QK(XKjuB(A zZIqkAS$3uXl}YBnS_H^zEc8a2Y>Z=5AKF@ekW~SPmr$g3ZA@qFexbGlW&7;X{T7+H zC*~FwQZ&BP8TWsfDAV#3(ZekfEU63u1M6Q>a(Sb`aG zLjYc*fg;P%Jh0Ea&G#`zs>_oz`Hmlf=B9o>A8DA&$Klg@%O<0So6fM_pOy*!qvdJZ zvsDv$*2L6Ly&T1YEx9==3O?r$>JYvo;NW-~hLVrpPCMP;CCiCkXo z9T3ssp^9xGl;eueP;l%(A;7(lagsE|35}k&=w!fbz89Pa?Y$6xF~jg?A=A)zA63K; zLI9N*ca;Gf&q;)8xF@W_?PuVl*{_#whNcP5hNNrcp1#5An0wR{X?}wyBQT5%<7hYS zf)&*^ZMy}CXOu)eaqdQwVf_=;-Q{!J`MJIRAa*a5b|?Ya(|XS9R$gBS@AzvZ^?XwZ zCZ;Ow5FN1it}^%uCIP+e^k%U~pxPZlMG)KB(5O6`Dk=Jus<9FQ@L8%ZO}>~ZrU|JB za4FfgpGRD0vtLC4zu$lfjhbVn&56|5cb$!G$?JO{%+D;>7%Fm?20Ji1Ac}8n-v0jp zkGJ73ly(L3g~LefAAq?)m9(Z$V9?2g)=CCrf*_D;0rI|BTIQ>579msYXc0KhYN!zN zl-ahS%%FlQAXE&DJBaU291MPM(-oPX4M_0)Jg6482jUNT{f8hB?TQZ_7Cpu_)Jt& zQlUQ`4!Bbl!xynI9*Zgc69wX(Hi!Mr-Im-%%u@tJ%Pt_|+4)6AG}ImGGQb1jVu!j# zLqvVm`VH(K@`EVof@An3R8$l~!%Kw98euJbR3@Hjlv9liER%bU)=tegMZy4vQXxn{+mep?aq}`ln5zDdwKJiIW*fgL^>Oo0eP)Z#w)6VfcJ~ zoD=?Yif)VaGt|Ihg@F(b{(!|~1RKu4uLtF z9^~d%$ZNGYmG*Y8Fqo+LC=S`}-l5fNoeUHE2(A#rqRSOW?x~!|h0Xq$Od!nX=7B9# zUHl;_rVK>mt@bBoEamm)>4JR_PE*vPveO-i2aU=p$&K&c zojK??Ny2co@zu-`qL`aoxsw_7Q2H2Xk7|a7()J$ob+Ly+8*SU#!9cz@MsiP0)gK&~zFxwnG-TdIOgeX(H>* zv6D0t)L+;`Oj8MHeHoY&0>l$-f?2LRQx`FJKQwQ%e9fo>2eoDyK(do%j{bP36E<8d z9i3A;eK#ql35J&<&C!yMhgG5cR(UcO+C7z)87zo?DP>7_v z7#sSf1ETsHLz!No3}xHteAXBHm?6Y$F=OO`q!VZL8o!VAG$J+>DKAdYb{|X%Zb^v` zmIaL_JW>bu5KGfy;sAmiRBaVTpa<;+(hg^gkfMpc3^p`_G$(&lucfrUhD}I?#?vP| zq@c2iTkc}24X?KJa{-i6LsVkK=BPb`G($$oBhCaGn1LFFd#R8u`&{N0rsfx5^#BW( z4U|Lz7}=W|pb_QJa?xM`j0_9R9fhoteySNVhjKb2q4y7PihhJsP)KAcsx0gbsej{k zt)O*E+9M>v2GSutXCZ^!izp_$++!^fP)rEl2l%I}4Sb4(?AN^z3(7J;j;RKSB3(VE z14J}t6+3|$FcJ;of2vYXd{$Fr4W8sNii&LvVYI_tT~o@X-UndwdoGfNsp>9zr*z0Y zqq_|bP^zgrmGo&An4M>~L1!-ryFRz#jb<DEFA+`VHaq#2k(>} zlZp$FBlAGOnraXXTA0MRED&l%B$g-mv

vktWz$7ZjYcMz1F_QYGZkCA`I>sz;uO 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�}T6FG`B`HD!{x9MgtmFxv7o>z! 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 - - - - - - - 2023-04-24T16:58:39.940540 - image/svg+xml - - - Matplotlib v3.5.1, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/nanobind/docs/images/sizes.svg b/src/nanobind/docs/images/sizes.svg deleted file mode 100644 index 4ad568b..0000000 --- a/src/nanobind/docs/images/sizes.svg +++ /dev/null @@ -1,1909 +0,0 @@ - - - - - - - - 2023-04-24T16:53:14.348990 - image/svg+xml - - - Matplotlib v3.5.1, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/nanobind/docs/images/times.svg b/src/nanobind/docs/images/times.svg deleted file mode 100644 index 686dfe5..0000000 --- a/src/nanobind/docs/images/times.svg +++ /dev/null @@ -1,1859 +0,0 @@ - - - - - - - - 2023-04-24T16:53:10.667274 - image/svg+xml - - - Matplotlib v3.5.1, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/nanobind/docs/images/wrapper-dark.svg b/src/nanobind/docs/images/wrapper-dark.svg deleted file mode 100644 index 4ca0ee0..0000000 --- a/src/nanobind/docs/images/wrapper-dark.svg +++ /dev/null @@ -1,102 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/nanobind/docs/images/wrapper-light.svg b/src/nanobind/docs/images/wrapper-light.svg deleted file mode 100644 index 62f73c9..0000000 --- a/src/nanobind/docs/images/wrapper-light.svg +++ /dev/null @@ -1,92 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/nanobind/docs/index.rst b/src/nanobind/docs/index.rst deleted file mode 100644 index 53a7dc4..0000000 --- a/src/nanobind/docs/index.rst +++ /dev/null @@ -1,147 +0,0 @@ -Introduction -============ - -.. only:: not latex - - .. image:: https://github.com/wjakob/nanobind/raw/master/docs/images/logo.jpg - :width: 800 - :align: center - :alt: nanobind logo - :class: only-light - - .. image:: https://rgl.s3.eu-central-1.amazonaws.com/media/uploads/wjakob/2023/03/28/nanobind_logo_dark.png - :width: 800 - :align: center - :alt: nanobind logo - :class: only-dark - -.. only:: latex - - .. image:: https://github.com/wjakob/nanobind/raw/master/docs/images/logo.jpg - :width: 800 - :align: center - :alt: nanobind logo - -*nanobind* is a small binding library that exposes C++ types in Python and vice -versa. It is reminiscent of `Boost.Python -`_ and `pybind11 -`_ and uses near-identical syntax. In -contrast to these existing tools, nanobind is *more efficient*: bindings -compile in a shorter amount of time, produce smaller binaries, and have better -runtime performance. - -More concretely, :ref:`benchmarks ` show up to **~4× faster** -compile time, **~5× smaller** binaries, and **~10× lower** 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). - -.. only:: not latex - - Documentation formats - --------------------- - - You are reading the HTML version of the documentation. An alternative `PDF - version `__ is - also available. - - Dependencies - ------------ - -.. only:: latex - - .. rubric:: Dependencies - -nanobinds depends on - -- **Python 3.8+** or **PyPy 7.3.10+** (the *3.8* and *3.9* 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. - -.. only:: not latex - - How to cite this project? - ------------------------- - -.. only:: latex - - .. rubric:: How to cite this project? - -Please use the following BibTeX template to cite nanobind in scientific -discourse: - -.. code-block:: bibtex - - @misc{nanobind, - author = {Wenzel Jakob}, - year = {2022}, - note = {https://github.com/wjakob/nanobind}, - title = {nanobind: tiny and efficient C++/Python bindings} - } - - -The nanobind logo was designed by `AndoTwin Studio -`__. High-resolution version are available `here -`__ -(light) and `here -`__ -(dark). - -.. only:: not latex - - Table of contents - ----------------- - -.. toctree:: - :maxdepth: 1 - - changelog - why - benchmark - porting - faq - -.. toctree:: - :caption: Basics - :maxdepth: 1 - - installing - building - basics - bazel - meson - -.. toctree:: - :caption: Intermediate - :maxdepth: 1 - - exchanging - ownership - functions - classes - exceptions - ndarray_index - refleaks - packaging - typing - utilities - -.. toctree:: - :caption: Advanced - :maxdepth: 1 - - free_threaded - ownership_adv - lowlevel - -.. toctree:: - :caption: API Reference - :maxdepth: 1 - - api_core - api_extra - api_cmake - api_bazel diff --git a/src/nanobind/docs/installing.rst b/src/nanobind/docs/installing.rst deleted file mode 100644 index 7f5c7dd..0000000 --- a/src/nanobind/docs/installing.rst +++ /dev/null @@ -1,48 +0,0 @@ -.. _installing: - -Installing the library -###################### - -The *nanobind* project is hosted at `wjakob/nanobind on GitHub -`_. To use the library in your own -projects, it is usually easiest to install it using one of the following three -methods: - -Install via Pip (recommended) -============================== - -Run the following command in your terminal to install a package containing both -C++ and CMake source code needed to compile extension modules. - -.. code-block:: bash - - python -m pip install nanobind - -Install via Conda -================= - -The following alternative installs an equivalent package through Conda. It is -provided for users that develop Conda-based extensions with a build-time -dependency on *nanobind*, in which case the PyPI package cannot be used. - -.. code-block:: bash - - conda install -c conda-forge nanobind - -Install as a Git submodule -========================== - -If you prefer not to involve external package managers, and if your project -uses the Git control system, you may also directly reference *nanobind* as a -`Git submodule `_. In the -main directory of your repository, run the following commands: - -.. code-block:: bash - - git submodule add https://github.com/wjakob/nanobind ext/nanobind - git submodule update --init --recursive - -This assumes you are placing your dependencies in ``ext/``. - -The :ref:`next section ` will explain how to set up a basic build -system that you can use to build your first extension module. diff --git a/src/nanobind/docs/lowlevel.rst b/src/nanobind/docs/lowlevel.rst deleted file mode 100644 index fb2c035..0000000 --- a/src/nanobind/docs/lowlevel.rst +++ /dev/null @@ -1,323 +0,0 @@ -.. _lowlevel: - -.. cpp:namespace:: nanobind - -Low-level interface -=================== - -nanobind exposes a low-level interface to provide fine-grained control over -the sequence of steps that instantiates a Python object wrapping a C++ -instance. This is useful when writing generic binding code that manipulates -nanobind-based objects of various types. - -Given a previous :cpp:class:`nb::class_\<...\> ` binding declaration, -the :cpp:func:`nb::type\() ` template function can be used to look up -the Python type object associated with a C++ class named ``MyClass``. - -.. code-block:: cpp - - nb::handle py_type = nb::type(); - -In the case of failure, this line will return a ``nullptr`` pointer, which -can be checked via ``py_type.is_valid()``. We can verify that the type -lookup succeeded, and that the returned instance indeed represents a -nanobind-owned type (via :cpp:func:`nb::type_check() `, which is -redundant in this case): - -.. code-block:: cpp - - assert(py_type.is_valid() && nb::type_check(py_type)); - -nanobind knows the size, alignment, and C++ RTTI ``std::type_info`` record of -all bound types. They can be queried on the fly via :cpp:func:`nb::type_size() -`, :cpp:func:`nb::type_align() `, and -:cpp:func:`nb::type_info() ` in situations where this is useful. - -.. code-block:: cpp - - assert(nb::type_size(py_type) == sizeof(MyClass) && - nb::type_align(py_type) == alignof(MyClass) && - nb::type_info(py_type) == typeid(MyClass)); - -Given a type object representing a C++ type, we can create an uninitialized -instance via :cpp:func:`nb::inst_alloc() `. This is an ordinary -Python object that can, however, not (yet) be passed to bound C++ functions -to prevent undefined behavior. It must first be initialized. - -.. code-block:: cpp - - nb::object py_inst = nb::inst_alloc(py_type); - -We can confirm via :cpp:func:`nb::inst_check() ` that this newly -created instance is managed by nanobind, that it has the correct type in -Python. Calling :cpp:func:`nb::inst_ready() ` reveals that the -*ready* flag of the instance is set to ``false`` (i.e., it is still -uninitialized). - -.. code-block:: cpp - - assert(nb::inst_check(py_inst) && - py_inst.type().is(py_type) && - !nb::inst_ready(py_inst)); - -For simple *plain old data* (POD) types, the :cpp:func:`nb::inst_zero() -` function can be used to *zero-initialize* the object and mark it -as ready. - -.. code-block:: cpp - - nb::inst_zero(py_inst); - assert(nb::inst_ready(py_inst)); - -We can destruct this default instance via :cpp:func:`nb::inst_destruct() -` and convert it back to non-ready status. This memory region -can then be reinitialized once more. - -.. code-block:: cpp - - nb::inst_destruct(py_inst); - assert(!nb::inst_ready(py_inst)); - -What follows is a more interesting example, where we use a lesser-known feature -of C++ (the "`placement new `_" -operator) to construct an instance *in-place* into the memory region allocated -by nanobind. - -.. code-block:: cpp - - // Get a C++ pointer to the uninitialized instance data - MyClass *ptr = nb::inst_ptr(py_inst); - - // Perform an in-place construction of the C++ object at address 'ptr' - new (ptr) MyClass(/* constructor arguments go here */); - -Following this constructor call, we must inform nanobind that the instance -object is now fully constructed via :cpp:func:`nb::inst_mark_ready() -`. When its reference count reaches zero, nanobind will then -automatically call the in-place destructor (``MyClass::~MyClass``). - -.. code-block:: cpp - - nb::inst_mark_ready(py_inst); - assert(nb::inst_ready(py_inst)); - -Let’s destroy this instance once more manually (which will, again, call -the C++ destructor and mark the Python object as non-ready). - -.. code-block:: cpp - - nb::inst_destruct(py_inst); - -Another useful feature is that nanobind can copy- or move-construct ``py_inst`` -from another instance of the same type via :cpp:func:`nb::inst_copy() -` and :cpp:func:`nb::inst_move() `. These functions call -the C++ copy or move constructor and transition ``py_inst`` back to ``ready`` -status. This is equivalent to calling an in-place version of these constructors -followed by a call to :cpp:func:`nb::inst_mark_ready() ` but -compiles to more compact code (the :cpp:class:`nb::class_\ ` -declaration had already created bindings for both constructors, and this simply -calls those bindings). - -.. code-block:: cpp - - if (copy_instance) - nb::inst_copy(/* dst = */ py_inst, /* src = */ some_other_instance); - else - nb::inst_move(/* dst = */ py_inst, /* src = */ some_other_instance); - -Both functions assume that the destination object is uninitialized. Two -alternative versions :cpp:func:`nb::inst_replace_copy() ` -and :cpp:func:`nb::inst_replace_move() ` destruct an -initialized instance and replace it with the contents of another by either -copying or moving. - -.. code-block:: cpp - - if (copy_instance) - nb::inst_replace_copy(/* dst = */ py_inst, /* src = */ some_other_instance); - else - nb::inst_replace_move(/* dst = */ py_inst, /* src = */ some_other_instance); - -Note that these functions are all *unsafe* in the sense that they do not -verify that their input arguments are valid. This is done for -performance reasons, and such checks (if needed) are therefore the -responsibility of the caller. Functions labeled ``nb::type_*`` should -only be called with nanobind type objects, and functions labeled -``nb::inst_*`` should only be called with nanobind instance objects. - -The functions :cpp:func:`nb::type_check() ` and -:cpp:func:`nb::inst_check() ` are exceptions to this rule: -they accept any Python object and test whether something is a nanobind type or -instance object. - -Two further functions :cpp:func:`nb::type_name() ` and -:cpp:func:`nb::inst_name() ` determine the type name associated with -a type or instance thereof. These also accept non-nanobind types and instances. - -Even lower-level interface --------------------------- - -Every nanobind object has two important flags that control its behavior: - -1. ``ready``: is the object fully constructed? If set to ``false``, - nanobind will raise an exception when the object is passed to a bound - C++ function. - -2. ``destruct``: Should nanobind call the C++ destructor when the - instance is garbage collected? - -The functions :cpp:func:`nb::inst_zero() `, -:cpp:func:`nb::inst_mark_ready() `, :cpp:func:`nb::inst_move() -`, and :cpp:func:`nb::inst_copy() ` set both of these -flags to ``true``, and :cpp:func:`nb::inst_destruct() ` sets -both of them to ``false``. - -In rare situations, the destructor should *not* be invoked when the instance is -garbage collected, for example when working with a nanobind instance -representing a field of a parent instance created using the -:cpp:enumerator:`nb::rv_policy::reference_internal -` return value policy. The library therefore -exposes two more functions :cpp:func:`nb::inst_state() ` and -:cpp:func:`nb::inst_set_state() ` that can be used to access -them individually. - -Referencing existing instances ------------------------------- - -The above examples used the function :cpp:func:`nb::inst_alloc() ` -to allocate a Python object along with space to hold a C++ instance associated -with the binding ``py_type``. - -.. code-block:: cpp - - nb::object py_inst = nb::inst_alloc(py_type); - - // Next, perform a C++ in-place construction into the - // address given by nb::inst_ptr(py_inst) - ... omitted, see the previous examples ... - -What if the C++ instance already exists? nanobind also supports this case via -the :cpp:func:`nb::inst_reference() ` and -:cpp:func:`nb::inst_take_ownership() ` functions—in this -case, the Python object references the existing memory region, which is -potentially (slightly) less efficient due to the need for an extra indirection. - -.. code-block:: cpp - - MyClass *inst = new MyClass(); - - // Transfer ownership of 'inst' to Python (which will use a delete - // expression to free it when the Python instance is garbage collected) - nb::object py_inst = nb::inst_take_ownership(py_type, inst); - - // We can also wrap C++ instances that should not be destructed since - // they represent offsets into another data structure. In this case, - // the optional 'parent' parameter ensures that 'py_inst' remains alive - // while 'py_subinst' exists to prevent undefined behavior. - nb::object py_subinst = nb::inst_reference( - py_field_type, &inst->field, /* parent = */ py_inst); - -.. _supplement: - -Supplemental type data ----------------------- - -nanobind can stash supplemental data *inside* the type object of bound types. -This involves the :cpp:class:`nb::supplement\() ` class binding -annotation to reserve space and :cpp:func:`nb::type_supplement\() -` to access the reserved memory region. - -An example use of this fairly advanced feature are libraries that register -large numbers of different types (e.g. flavors of tensors). A single -generically implemented function can then query the supplemental data block to -handle each tensor type slightly differently. - -Here is what this might look like in an implementation: - -.. code-block:: cpp - - struct MyTensorMetadata { - bool stored_on_gpu; - // .. - // should be a POD (plain old data) type - }; - - // Register a new type MyTensor, and reserve space for sizeof(MyTensorMedadata) - nb::class_ cls(m, "MyTensor", nb::supplement()) - - /// Mutable reference to 'MyTensorMedadata' portion in Python type object - MyTensorMedadata &supplement = nb::type_supplement(cls); - supplement.stored_on_gpu = true; - -The :cpp:class:`nb::supplement\() ` annotation implicitly also -passes :cpp:class:`nb::is_final() ` to ensure that type objects with -supplemental data cannot be subclassed in Python. - -nanobind requires that the specified type ``T`` be trivially default -constructible. It zero-initializes the supplement when the type is first -created but does not perform any further custom initialization or destruction. -You can fill the supplement with different contents following the type -creation, e.g., using the placement new operator. - -The contents of the supplemental data are not directly visible to Python's -cyclic garbage collector, which creates challenges if you want to reference -Python objects. The recommended workaround is to store the Python objects -as attributes of the type object (in its ``__dict__``) and store a borrowed -``PyObject*`` reference in the supplemental data. If you use an attribute -name that begins with the symbol ``@``, then nanobind will prevent Python -code from rebinding or deleting the attribute after it has been set, making -the borrowed reference reasonably safe. - -.. _typeslots: - -Customizing type creation -========================= - -nanobind exposes a low-level interface to install custom *type slots* -(``PyType_Slot`` in the `CPython API -`_) in newly -constructed types. This provides an escape hatch to realize features that were -not foreseen in the design of this library. - -To use this feature, specify the :cpp:class:`nb::type_slots() ` -annotation when creating the type. - -.. code-block:: cpp - - nb::class_(m, "MyClass", nb::type_slots(slots)); - -Here, ``slots`` should refer to an array of function pointers that are tagged -with a corresponding slot identifier. For example, here is an example -function that overrides the addition operator so that it behaves like a -multiplication. - -.. code-block:: cpp - - PyObject *myclass_tp_add(PyObject *a, PyObject *b) { - return PyNumber_Multiply(a, b); - } - - PyType_Slot slots[] = { - { Py_nb_add, (void *) myclass_tp_add }, - { 0, nullptr } - }; - -The ``slots`` array specified in the previous -:cpp:class:`nb::class_\() ` declaration references the -function ``myclass_tp_add`` and is followed by a mandatory null terminator. -Information on type slots can be found in the CPython documentation sections -covering `type objects `_ and -`type construction `_. - -This example is contrived because it could have been accomplished using -builtin features: - -.. code-block:: cpp - - nb::class_(m, "MyClass") - .def("__add__", - [](const MyClass &a, const MyClass &b) { return a * b; }, - nb::is_operator()) - -The documentation section on :ref:`reference leaks ` discusses -another important use case of type slots. diff --git a/src/nanobind/docs/meson.rst b/src/nanobind/docs/meson.rst deleted file mode 100644 index bd21a03..0000000 --- a/src/nanobind/docs/meson.rst +++ /dev/null @@ -1,116 +0,0 @@ -.. _meson: - -Building extensions using Meson -=============================== - -If you prefer the Meson build system to CMake, you can build extensions using -the `Meson WrapDB `__ package. - -.. note:: - - This package is a community contribution maintained by - `Will Ayd `__, please report issues - directly in the - `Meson WrapDB `__ repository. - -.. _meson-setup: - -Adding nanobind to your Meson project -------------------------------------- - -To use Meson as the build generator in your Python project, you will want to -install the -`meson-python `__ -project as a build dependency. To do so, simply add the following to your -pyproject.toml file: - -.. code-block:: toml - - [project] - name = "my_project_name" - dynamic = ['version'] - - [build-system] - requires = ['meson-python'] - - build-backend = 'mesonpy' - -In your project root, you will also want to create the subprojects folder -that Meson can install into. Then you will need to install the wrap packages -for both nanobind and robin-map: - -.. code-block:: sh - - mkdir -p subprojects - meson wrap install robin-map - meson wrap install nanobind - -The ``meson.build`` definition in your project root should look like: - -.. code-block:: meson - - project( - 'my_project_name', - 'cpp', - version: '0.0.1', - meson_version: '>=1.0.0', - default_options: ['cpp_std=c++17', 'b_ndebug=if-release'], - ) - - py = import('python').find_installation() - nanobind_dep = dependency('nanobind') - py.extension_module( - 'my_module_name', - sources: ['path_to_module.cpp'], - dependencies: [nanobind_dep], - install: true, - ) - -With this configuration, you may then call: - -.. code-block:: sh - - meson setup --buildtype release builddir - meson compile -C builddir - -to compile the extension in the ``builddir`` folder. - -Alternatively, if you don't care to have a local build folder, you can use -the Python build frontend of your choosing to install the package as an -editable install. With pip, this would look like: - -.. code-block:: sh - - python -m pip install -e . - -.. _meson-stable-abi: - -Building against the stable ABI -------------------------------- - -As in nanobind's CMake config, you can build bindings targeting Python's -stable ABI, starting from version 3.12. To do this, specify the target -version using the ``limited_api`` argument in your configuration. For example, -to build extensions against the CPython 3.12 stable ABI, use: - -.. code-block:: meson - - project( - 'my_project_name', - 'cpp', - version: '0.0.1', - meson_version: '>=1.3.0', - default_options: ['cpp_std=c++17', 'b_ndebug=if-release'], - ) - - py = import('python').find_installation() - nanobind_dep = dependency('nanobind') - py.extension_module( - 'my_module_name', - sources: ['path_to_module.cpp'], - dependencies: [nanobind_dep], - install: true, - limited_api: '3.12', - ) - -as your ``meson.build`` file. diff --git a/src/nanobind/docs/microbenchmark.ipynb b/src/nanobind/docs/microbenchmark.ipynb deleted file mode 100644 index 30a434a..0000000 --- a/src/nanobind/docs/microbenchmark.ipynb +++ /dev/null @@ -1,447 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "83b016f7-695a-43b7-8baf-4bd0d435a0c8", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "import random\n", - "import subprocess\n", - "import itertools\n", - "from collections import defaultdict\n", - "import importlib.machinery\n", - "import os\n", - "import time\n", - "import cython\n", - "from matplotlib.patches import Rectangle\n", - " \n", - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "\n", - "import matplotlib as mpl\n", - "mpl.rcParams['hatch.linewidth'] = 5.0 \n", - "cycle = [x['color'] for x in mpl.rcParams['axes.prop_cycle']]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "705868b4-c013-4fc2-a4c3-bdaca6c90d05", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "experiment_labels = []\n", - "sizes = defaultdict(lambda: [])\n", - "times = defaultdict(lambda: [])\n", - "\n", - "from sysconfig import get_paths as gp\n", - "suffix = importlib.machinery.EXTENSION_SUFFIXES[0]\n", - "\n", - "# Path to pybind11 git repository\n", - "pybind11_path = '/home/wjakob/pybind11/include'\n", - "\n", - "# Path to pybind11 git repository (smartholder branch)\n", - "pybind11_sh_path = '/home/wjakob/pybind11_sh/include'\n", - "\n", - "# Path to boost (in this case, assumed to be installed by the OS)\n", - "boost_path = '/usr/include/boost'\n", - "\n", - "cmd_base = ['clang++', '-march=native', '-shared', '-rpath', '..', '-std=c++17', '-I', '../include', '-I', gp()['include'],\n", - " '-Wno-deprecated-declarations', '-fPIC', f'-L{boost_path}/stage/lib', '-L..', '-fno-stack-protector',\n", - " '-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT']\n", - "\n", - "def gen_file(name, func, libs=('cython', 'boost', 'pybind11', 'pybind11_sh', 'nanobind')):\n", - " for i, lib in enumerate(libs): \n", - " for opt_mode, opt_flags in {'debug' : ['-O0', '-g3'], 'opt' : ['-Os', '-g0']}.items():\n", - " if lib != 'cython':\n", - " fname = name + '_' + lib + '.cpp'\n", - " else:\n", - " fname = name + '_' + lib + '_' + opt_mode + '.pyx'\n", - " \n", - " with open(fname, 'w') as f:\n", - " if lib == 'boost':\n", - " f.write(f'#include \\n')\n", - " f.write(f'namespace py = boost::python;\\n\\n')\n", - " f.write(f'BOOST_PYTHON_MODULE({name}_{lib}_{opt_mode}) {{\\n')\n", - " elif lib == 'nanobind':\n", - " f.write(f'#include \\n\\n')\n", - " f.write(f'namespace py = nanobind;\\n\\n')\n", - " f.write(f'NB_MODULE({name}_{lib}_{opt_mode}, m) {{\\n')\n", - " elif lib.startswith('pybind11'):\n", - " f.write(f'#include \\n\\n')\n", - " f.write(f'namespace py = pybind11;\\n\\n')\n", - " f.write(f'PYBIND11_MODULE({name}_{lib}_{opt_mode}, m) {{\\n')\n", - " elif lib == 'cython':\n", - " f.write(f'from libc.stdint cimport uint16_t, int32_t, uint32_t, int64_t, uint64_t\\n')\n", - "\n", - " func(f, lib)\n", - " if lib != 'cython':\n", - " f.write(f'}}\\n')\n", - "\n", - " fname_out = name + '_' + lib + '_' + opt_mode + suffix\n", - " cmd = cmd_base + opt_flags + [name + '_' + lib + '.cpp', '-o', fname_out]\n", - " if lib == 'nanobind':\n", - " cmd += ['-lnanobind']\n", - " elif lib == 'boost':\n", - " cmd += ['-I', boost_path, '-lboost_python310']\n", - " elif lib == 'pybind11':\n", - " cmd += ['-I', pybind11_path]\n", - " elif lib == 'pybind11_sh':\n", - " cmd += ['-I', pybind11_sh_path]\n", - " \n", - " print(' '.join(cmd))\n", - " time_list = []\n", - " for l in range(5):\n", - " time_before = time.perf_counter()\n", - " if lib == 'cython':\n", - " subprocess.check_call(['cython3', '-3', '--cplus', fname, '-o', name + '_' + lib + '.cpp'])\n", - " subprocess.check_call(cmd)\n", - " time_after = time.perf_counter()\n", - " time_list.append(time_after-time_before)\n", - " time_list.sort()\n", - " \n", - " if opt_mode != 'debug':\n", - " subprocess.check_call(['strip', fname_out])\n", - " if i == 0:\n", - " experiment_labels.append(name + ' [' + opt_mode + ']')\n", - " sizes[lib].append(os.path.getsize(fname_out) / (1024 * 1024))\n", - " times[lib].append(time_list[len(time_list)//2])\n", - "\n", - "\n", - " \n", - "def gen_func(f, lib):\n", - " types = [ 'uint16_t', 'int32_t', 'uint32_t', 'int64_t', 'uint64_t', 'float' ]\n", - " if lib == 'boost':\n", - " prefix = 'py::'\n", - " else:\n", - " prefix = 'm.'\n", - " for i, t in enumerate(itertools.permutations(types)):\n", - " args = f'{t[0]} a, {t[1]} b, {t[2]} c, {t[3]} d, {t[4]} e, {t[5]} f'\n", - " if lib != 'cython':\n", - " f.write(' %sdef(\"test_%04i\", +[](%s) { return a+b+c+d+e+f; });\\n' % (prefix, i, args))\n", - " else:\n", - " f.write('cpdef float test_%04i(%s):\\n return a+b+c+d+e+f\\n\\n' % (i, args))\n", - "\n", - "\n", - "def gen_class(f, lib):\n", - " types = [ 'uint16_t', 'int32_t', 'uint32_t', 'int64_t', 'uint64_t', 'float' ]\n", - "\n", - " for i, t in enumerate(itertools.permutations(types)):\n", - " if lib == 'boost':\n", - " prefix = ''\n", - " postfix = f', py::init<{t[0]}, {t[1]}, {t[2]}, {t[3]}, {t[4]}, {t[4]}>()'\n", - " func_prefix = 'py::def'\n", - "\n", - " else:\n", - " prefix = 'm, '\n", - " postfix = ''\n", - " func_prefix = 'm.def'\n", - "\n", - " if lib != 'cython':\n", - " f.write(f' struct Struct{i} {{\\n')\n", - " f.write(f' {t[0]} a; {t[1]} b; {t[2]} c; {t[3]} d; {t[4]} e; {t[5]} f;\\n')\n", - " f.write(f' Struct{i}({t[0]} a, {t[1]} b, {t[2]} c, {t[3]} d, {t[4]} e, {t[5]} f) : a(a), b(b), c(c), d(d), e(e), f(f) {{ }}\\n')\n", - " f.write(f' float sum() const {{ return a+b+c+d+e+f; }}\\n')\n", - " f.write(f' }};\\n')\n", - " else:\n", - " f.write(f'cdef class Struct{i}:\\n')\n", - " f.write(f' cdef {t[0]} a\\n')\n", - " f.write(f' cdef {t[1]} b\\n')\n", - " f.write(f' cdef {t[2]} c\\n')\n", - " f.write(f' cdef {t[3]} d\\n')\n", - " f.write(f' cdef {t[4]} e\\n')\n", - " f.write(f' cdef {t[5]} f\\n\\n')\n", - " f.write(f' def __cinit__(self, {t[0]} a, {t[1]} b, {t[2]} c, {t[3]} d, {t[4]} e, {t[5]} f):\\n')\n", - " f.write(f' self.a = a\\n')\n", - " f.write(f' self.b = b\\n')\n", - " f.write(f' self.c = c\\n')\n", - " f.write(f' self.d = d\\n')\n", - " f.write(f' self.e = e\\n')\n", - " f.write(f' self.f = f\\n\\n')\n", - " f.write(f' cpdef float sum(self):\\n')\n", - " f.write(f' return self.a+self.b+self.c+self.d+self.e+self.f\\n\\n')\n", - " continue\n", - "\n", - " f.write(f' py::class_({prefix}\\\"Struct{i}\\\"{postfix})\\n')\n", - " \n", - " if lib != 'boost':\n", - " f.write(f' .def(py::init<{t[0]}, {t[1]}, {t[2]}, {t[3]}, {t[4]}, {t[5]}>())\\n')\n", - " f.write(f' .def(\"sum\", &Struct{i}::sum);\\n\\n')\n", - " \n", - " if i > 250:\n", - " break;\n", - " \n", - " \n", - "gen_file('func', gen_func)\n", - "gen_file('class', gen_class)\n", - "experiment_labels = ['func [debug]', 'func [opt]', 'class [debug]', 'class [opt]']\n", - "\n", - "print(experiment_labels)\n", - "print(dict(sizes))\n", - "print(dict(times))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c67eac20-ffb5-4c58-ba08-64e7404a54a9", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "\n", - "plot_colors = {\n", - " 'boost': cycle[1],\n", - " 'pybind11': cycle[3],\n", - " 'pybind11_sh': cycle[5],\n", - " 'cython' : cycle[4],\n", - " 'nanobind': cycle[0]\n", - "}\n", - "plot_labels = {\n", - " 'boost' : 'Boost.Python',\n", - " 'pybind11' : 'pybind11',\n", - " 'pybind11_sh' : 'pybind11 + smart_holder',\n", - " 'cython' : 'Cython',\n", - " 'nanobind' : 'nanobind'\n", - "}\n", - "\n", - "def bars(data, ylim_scale = 1, figsize_scale = 1, width_scale=1.0, debug_shift=0.1):\n", - " ylim = 0\n", - " for n, d in data.items():\n", - " if len(d) == 0:\n", - " continue\n", - " ylim = max(max(d), ylim)\n", - " ylim *= ylim_scale * 1.3\n", - "\n", - " def adj(ann):\n", - " for i, a in enumerate(ann):\n", - " if a.xy[1] > ylim*.9:\n", - " a.xy = (a.xy[0], ylim * 0.8)\n", - " if i%2 == 1:\n", - " a.set_color('white')\n", - "\n", - " fig, ax = plt.subplots(figsize=[11.25*figsize_scale, 3*figsize_scale])\n", - " width = 1.0/(len(data) + 1)*width_scale\n", - " x = np.arange(4)\n", - "\n", - " result = []\n", - " for i, n in enumerate(plot_labels):\n", - " d = data[n]\n", - " if len(d) == 0:\n", - " continue\n", - "\n", - " col = plot_colors[n]\n", - " if col != 'None':\n", - " kwargs = { 'edgecolor': 'black', 'color': col }\n", - " else:\n", - " kwargs = {'edgecolor': 'white', 'hatch' : '/', 'color':cycle[7]}\n", - " \n", - " bar = ax.bar(x+width*(i -(len(data)-1)/2), d, width, label=plot_labels[n], align='center', **kwargs)\n", - " result.append(bar)\n", - " \n", - " ax.add_patch(Rectangle((-0.65+debug_shift, -1), 1, 28, facecolor='white', alpha=.8, edgecolor='None'))\n", - " ax.add_patch(Rectangle((1.4+debug_shift, -1), 1, 25, facecolor='white', alpha=.8, edgecolor='None'))\n", - "\n", - " for i, n in enumerate(plot_labels):\n", - " d = data[n]\n", - " if len(d) == 0:\n", - " continue\n", - "\n", - " bar = result[i]\n", - " if n == 'nanobind':\n", - " adj(ax.bar_label(bar, fmt='%.2f'))\n", - " else:\n", - " improvement = np.array(d) / np.array(data['nanobind'])\n", - " improvement = ['%.2f\\n(x%.1f)' % (d[i], v) for i, v in enumerate(improvement)]\n", - " adj(ax.bar_label(bar, labels=improvement, padding=3))\n", - " \n", - " ax.set_ylim(0, ylim)\n", - " ax.set_xticks(x, experiment_labels)\n", - " return fig, ax\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "60cb243a-e94d-4dd0-af67-91b7c5007571", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig, ax = bars(times, ylim_scale=0.93, figsize_scale=1.1, width_scale=1)\n", - "ax.set_ylabel('Time (seconds)')\n", - "ax.set_title('Compilation time')\n", - "ax.set_xlim(-0.45,3.45)\n", - "\n", - "ax.legend(loc='upper left')\n", - "\n", - "fig.tight_layout()\n", - "plt.savefig('times.png', facecolor='white', dpi=200, bbox_inches='tight', pad_inches = 0)\n", - "plt.savefig('times.svg', facecolor='white', bbox_inches='tight', pad_inches = 0)\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b23e02b4-6e98-49a3-a60f-14142757af71", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig, ax = bars(sizes, ylim_scale=.085, figsize_scale=1.1)\n", - "ax.set_ylabel('Size (MiB)')\n", - "ax.set_title('Binary size')\n", - "ax.set_xlim(-0.45,3.45)\n", - "ax.legend(loc='lower left')\n", - "\n", - "fig.tight_layout()\n", - "plt.savefig('sizes.png', facecolor='white', dpi=200, bbox_inches='tight', pad_inches = 0)\n", - "plt.savefig('sizes.svg', facecolor='white', bbox_inches='tight', pad_inches = 0)\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d3f3b5d2-74d6-415c-ad3d-b8fb0f37eddb", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "import cppyy\n", - "if not hasattr(cppyy.gbl, 'test_0000'):\n", - " cppyy.include('cppyy.h')\n", - "\n", - "plot_colors = {\n", - " 'boost': cycle[1],\n", - " 'cython': cycle[4],\n", - " 'pybind11': cycle[3],\n", - " 'cppyy' : cycle[8],\n", - " 'python': 'None',\n", - " 'nanobind': cycle[0]\n", - "}\n", - "\n", - "plot_labels = {\n", - " 'boost' : 'Boost.Python',\n", - " 'pybind11' : 'pybind11', \n", - " 'cppyy' : 'cppyy',\n", - " 'cython' : 'Cython',\n", - " 'nanobind' : 'nanobind',\n", - " 'python' : 'Python'\n", - "}\n", - "\n", - "class native_module:\n", - " @staticmethod\n", - " def test_0000(a, b, c, d, e, f):\n", - " return a + b + c + d +e + f\n", - "\n", - " \n", - " class Struct0:\n", - " def __init__(self, a, b, c, d, e, f):\n", - " self.a = a\n", - " self.b = b\n", - " self.c = c\n", - " self.d = d\n", - " self.e = e\n", - " self.f = f\n", - "\n", - " def sum(self):\n", - " return self.a + self.b + self.c + self.e + self.f\n", - " \n", - "\n", - "rtimes = defaultdict(lambda: [])\n", - "for name in ['func', 'class']:\n", - " its = 10000000 if name == 'func' else 2500000\n", - " for lib in plot_labels:\n", - " for mode in ['debug', 'opt']:\n", - " if lib == 'cppyy':\n", - " m = cppyy.gbl\n", - " elif lib == 'nanobind_sh':\n", - " continue # Performance identical, not an interesting data point\n", - " elif lib == 'python':\n", - " m = native_module\n", - " else:\n", - " m = importlib.import_module(f'{name}_{lib}_{mode}')\n", - " \n", - " time_list = []\n", - " for i in range(5):\n", - " time_before = time.perf_counter()\n", - " if name == 'func':\n", - " func = m.test_0000\n", - " for i in range(its):\n", - " func(1,2,3,4,5,6)\n", - " elif name == 'class':\n", - " cls = m.Struct0\n", - " sum_member = cls.sum\n", - " for i in range(its):\n", - " sum_member(cls(1,2,3,4,5,6))\n", - "\n", - " time_after = time.perf_counter()\n", - " time_list.append(time_after-time_before)\n", - " time_list.sort()\n", - "\n", - " rtimes[lib].append(time_list[len(time_list)//2])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5ad743ce-33c4-4dd1-8a0f-2ccbcf02aa1c", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig, ax = bars(rtimes, ylim_scale=.188, figsize_scale=1.25, width_scale=1, debug_shift=.1)\n", - "ax.set_ylabel('Time (seconds)')\n", - "ax.set_title('Runtime performance')\n", - "ax.set_xlim(-0.45,3.45)\n", - "ax.legend()\n", - "fig.tight_layout()\n", - "plt.savefig('perf.png', facecolor='white', dpi=200, bbox_inches='tight', pad_inches = 0)\n", - "plt.savefig('perf.svg', facecolor='white', bbox_inches='tight', pad_inches = 0)\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2f54fb73-d150-48c0-862f-57abdbce9875", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.6" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/src/nanobind/docs/ndarray.rst b/src/nanobind/docs/ndarray.rst deleted file mode 100644 index 50581d2..0000000 --- a/src/nanobind/docs/ndarray.rst +++ /dev/null @@ -1,714 +0,0 @@ -.. cpp:namespace:: nanobind - -.. _ndarray_class: - -The ``nb::ndarray<..>`` class -============================= - -nanobind can exchange n-dimensional arrays (henceforth "**nd-arrays**") with -popular array programming frameworks including `NumPy `__, -`PyTorch `__, `TensorFlow `__, -`JAX `__, and `CuPy `_. It -supports *zero-copy* exchange using two protocols: - -- The classic `buffer - protocol `__. - -- `DLPack `__, a - GPU-compatible generalization of the buffer protocol. - -nanobind knows how to talk to each framework and takes care of all the -nitty-gritty details. - -To use this feature, you must add the include directive - -.. code-block:: cpp - - #include - -to your code. Following this, you can bind functions with -:cpp:class:`nb::ndarray\<...\> `-typed parameters and return values. - -Array input arguments ---------------------- - -A function that accepts an :cpp:class:`nb::ndarray\<\> `-typed parameter -(i.e., *without* template parameters) can be called with *any* writable array -from any framework regardless of the device on which it is stored. The -following example binding declaration uses this functionality to inspect the -properties of an arbitrary input array: - -.. code-block:: cpp - - m.def("inspect", [](const nb::ndarray<>& a) { - printf("Array data pointer : %p\n", a.data()); - printf("Array dimension : %zu\n", a.ndim()); - for (size_t i = 0; i < a.ndim(); ++i) { - printf("Array dimension [%zu] : %zu\n", i, a.shape(i)); - printf("Array stride [%zu] : %zd\n", i, a.stride(i)); - } - printf("Device ID = %u (cpu=%i, cuda=%i)\n", a.device_id(), - int(a.device_type() == nb::device::cpu::value), - int(a.device_type() == nb::device::cuda::value) - ); - printf("Array dtype: int16=%i, uint32=%i, float32=%i\n", - a.dtype() == nb::dtype(), - a.dtype() == nb::dtype(), - a.dtype() == nb::dtype() - ); - }); - -Below is an example of what this function does when called with a NumPy -array: - -.. code-block:: pycon - - >>> my_module.inspect(np.array([[1,2,3], [3,4,5]], dtype=np.float32)) - Array data pointer : 0x1c30f60 - Array dimension : 2 - Array dimension [0] : 2 - Array stride [0] : 3 - Array dimension [1] : 3 - Array stride [1] : 1 - Device ID = 0 (cpu=1, cuda=0) - Array dtype: int16=0, uint32=0, float32=1 - -Array constraints ------------------ - -In practice, it can often be useful to *constrain* what kinds of arrays -constitute valid inputs to a function. For example, a function expecting CPU -storage would likely crash if given a pointer to GPU memory, and nanobind -should therefore prevent such undefined behavior. The -:cpp:class:`nb::ndarray\<...\> ` class accepts template arguments to -specify such constraints. For example the binding below guarantees that the -implementation can only be called with CPU-resident arrays with shape (·,·,3) -containing 8-bit unsigned integers. - -.. code-block:: cpp - - using RGBImage = nb::ndarray, nb::device::cpu>; - - m.def("process", [](RGBImage data) { - // Double brightness of the MxNx3 RGB image - for (size_t y = 0; y < data.shape(0); ++y) - for (size_t x = 0; x < data.shape(1); ++x) - for (size_t ch = 0; ch < 3; ++ch) - data(y, x, ch) = (uint8_t) std::min(255, data(y, x, ch) * 2); - }); - -The above example also demonstrates the use of :cpp:func:`operator() -`, which provides direct read/write access to the array -contents assuming that they are reachable through the CPU's -virtual address space. - -.. _ndarray-constraints-1: - -Overview -^^^^^^^^ - -Overall, the following kinds of constraints are available: - -- **Data type**: a type annotation like ``float``, ``uint8_t``, etc., constrain the - numerical representation of the nd-array. Complex arrays (i.e., - ``std::complex`` or ``std::complex``) are also supported. - -- **Constant arrays**: further annotating the data type with ``const`` makes it possible to call - the function with constant arrays that do not permit write access. Without - the annotation, calling the binding would fail with a ``TypeError``. - - You can alternatively accept constant arrays of *any type* by not specifying - a data type at all and instead passing the :cpp:class:`nb::ro ` annotation. - -- **Shape**: The :cpp:class:`nb::shape ` annotation (as in ``nb::shape<-1, 3>``) - simultaneously constrains the number of array dimensions and the size per - dimension. A value of ``-1`` leaves the size of the associated dimension - unconstrained. - - :cpp:class:`nb::ndim\ ` is shorter when only the dimension - should be constrained. For example, ``nb::ndim<3>`` is equivalent to - ``nb::shape<-1, -1, -1>``. - -- **Device tags**: annotations like :cpp:class:`nb::device::cpu ` - or :cpp:class:`nb::device::cuda ` constrain the source device - and address space. - -- **Memory order**: two ordering tags :cpp:class:`nb::c_contig ` and - :cpp:class:`nb::f_contig ` enforce contiguous storage in either - C or Fortran style. - - In the case of matrices, C-contiguous implies row-major and F-contiguous - implies column-major storage. Without this tag, arbitrary non-contiguous - representations (e.g. produced by slicing operations) and other unusual - layouts are permitted. - - This tag is mainly useful when your code directly accesses the array contents - via :cpp:func:`nb::ndarray\<...\>::data() `, while assuming a - particular layout. - - A third order tag named :cpp:class:`nb::any_contig ` accepts - *both* ``F``- and ``C``-contiguous arrays while rejecting non-contiguous - ones. - -Type signatures -^^^^^^^^^^^^^^^ - -nanobind displays array constraints in docstrings and error messages. For -example, suppose that we now call the ``process()`` function with an invalid -input. This produces the following error message: - -.. code-block:: pycon - - >>> my_module.process(np.zeros(1)) - - TypeError: process(): incompatible function arguments. The following argument types are supported: - 1. process(arg: ndarray[dtype=uint8, shape=(*, *, 3), device='cpu'], /) -> None - - Invoked with types: numpy.ndarray - -Note that these type annotations are intended for humans–they will not -currently work with automatic type checking tools like `MyPy -`__ (which at least for the time being -don’t provide a portable or sufficiently flexible annotation of n-dimensional -arrays). - -Overload resolution -^^^^^^^^^^^^^^^^^^^ - -A function binding can declare multiple overloads with different nd-array -constraints (e.g., a CPU and a GPU implementation), in which case nanobind will -call the first matching overload. When no perfect match can be found, nanobind -will try each overload once more while performing basic implicit conversions: -it will convert strided arrays into C- or F-contiguous arrays (if requested) -and perform type conversion. This, e.g., makes it possible to call a function -expecting a ``float32`` array with ``float64`` data. Implicit conversions -create temporary nd-arrays containing a copy of the data, which can be -undesirable. To suppress them, add an -:cpp:func:`nb::arg("my_array_arg").noconvert() ` or -:cpp:func:`"my_array_arg"_a.noconvert() ` argument annotation. - -Passing arrays within C++ code ------------------------------- - -You can think of the :cpp:class:`nb::ndarray ` class as a -reference-counted pointer resembling ``std::shared_ptr`` that can be freely -moved or copied. This means that there isn't a big difference between a -function taking -``ndarray`` by value versus taking a constant reference ``const ndarray &`` -(i.e., the former does not create an additional copy of the underlying data). - -Copies of the :cpp:class:`nb::ndarray ` wrapper will point to the same -underlying buffer and increase the reference count until they go out of scope. -You may call freely call :cpp:class:`nb::ndarray\<...\> ` methods from -multithreaded code even when the `GIL -`__ is not held, for -example to examine the layout of an array and access the underlying storage. - -There are two exceptions to this: creating a *new* nd-array object from C++ -(discussed :ref:`later `) and casting it to Python via -the :cpp:func:`ndarray::cast` function both involve Python API calls that require -that the GIL is held. - -.. _returning-ndarrays: - -Returning arrays from C++ to Python ------------------------------------ - -Passing an nd-array across the C++ → Python language barrier is a two-step -process: - -1. Creating an :cpp:class:`nb::ndarray\<...\> ` instance, which - only stores *metadata*, e.g.: - - - Where is the data located in memory? (pointer address and device) - - What is its type and shape? - - Who owns this data? - - An actual Python object is not yet constructed at this stage. - -2. Converting the :cpp:class:`nb::ndarray\<...\> ` into a - Python object of the desired type (e.g. ``numpy.ndarray``). - -Normally, step 1 is your responsibility, while step 2 is taken care of by the -binding layer. To understand this separation, let's look at an example. The -``.view()`` function binding below creates a 4×4 column-major NumPy array view -into a ``Matrix4f`` instance. - -.. _matrix4f-example: - -.. code-block:: cpp - - struct Matrix4f { float m[4][4] { }; }; - - using Array = nb::ndarray, nb::f_contig>; - - nb::class_(m, "Matrix4f") - .def(nb::init<>()) - .def("view", - [](Matrix4f &m){ return Array(data); }, - nb::rv_policy::reference_internal); - -In this case: - -- step 1 is the ``Array(data)`` call in the lambda function. - -- step 2 occurs outside of the lambda function when the nd-array - :cpp:class:`nb::ndarray\<...\> ` :ref:`type caster ` - constructs a NumPy array from the metadata. - -Data *ownership* is an important aspect of this two-step process: because the -NumPy array points directly into the storage of another object, nanobind must -keep the ``Matrix4f`` instance alive as long as the NumPy array exists, which -the :cpp:enumerator:`reference_internal ` return -value policy signals to nanobind. More generally, wrapping an existing memory -region without copying requires that that this memory region remains valid -throughout the lifetime of the created array (more on this point :ref:`shortly -`). - -Recall the discussion of the :ref:`nd-array constraint ` -template parameters. For the return path, you will generally want to add a -*framework* template parameter to the nd-array parameters that indicates the -desired Python type. - -- :cpp:class:`nb::numpy `: create a ``numpy.ndarray``. -- :cpp:class:`nb::pytorch `: create a ``torch.Tensor``. -- :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``. -- No framework annotation. In this case, nanobind will create a raw Python - ``dltensor`` `capsule `__ - representing the `DLPack `__ metadata. - -This annotation also affects the auto-generated docstring of the function, -which in this case becomes: - -.. code-block:: python - - view(self) -> numpy.ndarray[float32, shape=(4, 4), order='F'] - -Note that the framework annotation only plays a role when passing arrays from -C++ to Python. It does not constrain the reverse direction (for example, a -PyTorch array would still be accepted by a function taking the ``Array`` alias -defined above as input. For this reason, you may want to add a -:cpp:class:`nb::device::cpu ` device annotation). - -Dynamic array configurations -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The previous example was rather simple because all the array configuration was fully known -at compile time and specified via the :cpp:class:`nb::ndarray\<...\> ` -template parameters. In general, there are often dynamic aspects of the -configuration that must be explicitly passed to the constructor. Its signature -(with some simplifications) is given below. See the -:ref:`ndarray::ndarray() ` documentation for a more detailed specification -and another variant of the constructor. - -.. code-block:: cpp - - ndarray(void *data, - std::initializer_list shape = { }, - handle owner = { }, - std::initializer_list strides = { }, - dlpack::dtype dtype = ..., - int device_type = ..., - int device_id = 0, - char order = ...) { .. } - -The parameters have the following role: - -- ``data``: CPU/GPU/.. memory address of the data. -- ``shape``: number of dimensions and size along each axis. -- ``owner``: a Python object owning the storage, which must be - kept alive while the array object exists. -- ``strides``: specifies the data layout in memory. You only need to specify - this parameter if it has a non-standard ``order`` (e.g., if it is non-contiguous). - Note that the ``strides`` count elements, not bytes. -- ``dtype`` data type (floating point, signed/unsigned integer), bit depth. -- ``device_type`` and ``device_id``: device type and number, e.g., for multi-GPU setups. -- ``order``: coefficient memory order. Default: ``'C'`` (C-style) ordering, - specify ``'F'`` for Fortran-style ordering. - -The parameters generally have inferred defaults based on the array's -compile-time template parameters. Passing them explicitly overrides these -defaults with information available at runtime. - -.. _ndarray-ownership: - -Data ownership -^^^^^^^^^^^^^^ - -Let's look at a fancier example that uses the constructor arguments explained -above to return a dynamically sized 2D array. This example also shows another -mechanism to express *data ownership*: - -.. code-block:: cpp - - m.def("create_2d", - [](size_t rows, size_t cols) { - // Allocate a memory region an initialize it - float *data = new float[rows * cols]; - for (size_t i = 0; i < rows * cols; ++i) - data[i] = (float) i; - - // Delete 'data' when the 'owner' capsule expires - nb::capsule owner(data, [](void *p) noexcept { - delete[] (float *) p; - }); - - return nb::ndarray>( - /* data = */ data, - /* shape = */ { rows, cols }, - /* owner = */ owner - ); - }); - -The ``owner`` parameter should specify a Python object, whose continued -existence keeps the underlying memory region alive. Nanobind will temporarily -increase the ``owner`` reference count in the :cpp:func:`ndarray::ndarray()` -constructor and then decrease it again when the created NumPy array expires. - -The above example binding returns a *new* memory region that should be deleted -when it is no longer in use. This is done by creating a -:cpp:class:`nb::capsule`, an opaque pointer with a destructor callback that -runs at this point and takes care of cleaning things up. - -If there is already an existing Python object, whose existence guarantees that -it is safe to access the provided storage region, then you may alternatively -pass this object as the ``owner``---nanobind will make sure that this object -isn't deleted as long as the created array exists. If the owner is a C++ object -with an associated Python instance, you may use :cpp:func:`nb::find() ` -to look up the associated Python object. When binding methods, you can use the -:cpp:enumerator:`reference_internal ` return -value policy to specify the implicit ``self`` argument as the ``owner`` upon -return, which was done in the earlier ``Matrix4f`` :ref:`example -`. - -.. warning:: - - If you do not specify an owner and use a return value policy like - :cpp:enumerator:`rv_policy::reference` (see also the the section on - :ref:`nd-array return value policies `), nanobind will assume - that the array storage **remains valid forever**. - - This is one of the most frequent issues reported on the nanobind GitHub - repository: users forget to think about data ownership and run into data - corruption. - - If there isn't anything keeping the array storage alive, it will likely be - released and reused at some point, while stale arrays still point to the - associated memory region (i.e., a classic "use-after-free" bug). - -In more advanced situations, it may be helpful to have a capsule that manages -the lifetime of data structures containing *multiple* storage regions. The same -capsule can be referenced from different nd-arrays and will call the deleter -when all of them have expired: - -.. code-block:: cpp - - m.def("return_multiple", []() { - struct Temp { - std::vector vec_1; - std::vector vec_2; - }; - - Temp *temp = new Temp(); - temp->vec_1 = std::move(...); - temp->vec_2 = std::move(...); - - nb::capsule deleter(temp, [](void *p) noexcept { - delete (Temp *) p; - }); - - size_t size_1 = temp->vec_1.size(); - size_t size_2 = temp->vec_2.size(); - - return std::make_pair( - nb::ndarray(temp->vec_1.data(), { size_1 }, deleter), - nb::ndarray(temp->vec_2.data(), { size_2 }, deleter) - ); - }); - -.. _ndarray_rvp: - -Return value policies -^^^^^^^^^^^^^^^^^^^^^ - -Function bindings that return nd-arrays can specify return value policy -annotations to determine whether or not a copy should be made. They are -interpreted as follows: - -- The default :cpp:enumerator:`rv_policy::automatic` and - :cpp:enumerator:`rv_policy::automatic_reference` policies cause the array to - be copied when it has no owner and when it is not already associated with a - Python object. - -- The policy :cpp:enumerator:`rv_policy::reference` references an existing - memory region and never copies. - -- :cpp:enumerator:`rv_policy::copy` always copies. - -- :cpp:enumerator:`rv_policy::none` refuses the cast unless the array is - already associated with an existing Python object (e.g. a NumPy array), in - which case that object is returned. - -- :cpp:enumerator:`rv_policy::reference_internal` retroactively sets the - nd-array's ``owner`` field to a method's ``self`` argument. It fails with an - error if there is already a different owner. - -- :cpp:enumerator:`rv_policy::move` is unsupported and demoted to - :cpp:enumerator:`rv_policy::copy`. - -.. _ndarray-temporaries: - -Returning temporaries -^^^^^^^^^^^^^^^^^^^^^ - -Returning nd-arrays from temporaries (e.g. stack-allocated memory) requires -extra precautions. - -.. code-block:: cpp - :emphasize-lines: 4,5 - - using Vector3f = nb::ndarray>; - m.def("return_vec3", []{ - float data[] { 1, 2, 3 }; - // !!! BAD don't do this !!! - return Vector3f(data); - }); - -Recall the discussion at the :ref:`beginning ` of this -subsection. The :cpp:class:`nb::ndarray\<...\> ` constructor only -creates *metadata* describing this array, with the actual array creation -happening *after* of the function call. That isn't safe in this case because -``data`` is a temporary on the stack that is no longer valid once the function -has returned. To fix this, we could use the :cpp:func:`nb::cast() ` -method to *force* the array creation in the body of the function: - -.. code-block:: cpp - :emphasize-lines: 4,5 - - using Vector3f = nb::ndarray>; - m.def("return_vec3", []{ - float data[] { 1, 2, 3 }; - // OK. - return nb::cast(Vector3f(data)); - }); - -While safe, one unfortunate aspect of this change is that the function now has -a rather non-informative docstring ``return_vec3() -> object``, which is a -consequence of :cpp:func:`nb::cast() ` returning a generic -:cpp:class:`nb::object `. - -To fix this, you can use the nd-array :cpp:func:`.cast() ` -method, which is like :cpp:func:`nb::cast() ` except that it preserves -the type signature: - -.. code-block:: cpp - :emphasize-lines: 4,5 - - using Vector3f = nb::ndarray>; - m.def("return_vec3", []{ - float data[] { 1, 2, 3 }; - // Perfect. - return Vector3f(data).cast(); - }); - -.. _ndarray-nonstandard: - -Nonstandard arithmetic types ----------------------------- - -Low or extended-precision arithmetic types (e.g., ``int128``, ``float16``, -``bfloat16``) are sometimes used but don't have standardized C++ equivalents. -If you wish to exchange arrays based on such types, you must register a partial -overload of ``nanobind::detail::dtype_traits`` to inform nanobind about it. - -You are expressively allowed to create partial overloads of this class despite -it being in the ``nanobind::detail`` namespace. - -For example, the following snippet makes ``__fp16`` (half-precision type on -``aarch64``) available by providing - -1. ``value``, a DLPack ``nanobind::dlpack::dtype`` type descriptor, and -2. ``name``, a type name for use in docstrings and error messages. - -.. code-block:: cpp - - namespace nanobind::detail { - template <> struct dtype_traits<__fp16> { - static constexpr dlpack::dtype value { - (uint8_t) dlpack::dtype_code::Float, // type code - 16, // size in bits - 1 // lanes (simd), usually set to 1 - }; - static constexpr auto name = const_name("float16"); - }; - } - -.. _ndarray-views: - -Fast array views ----------------- - -The following advice applies to performance-sensitive CPU code that reads and -writes arrays using loops that invoke :cpp:func:`nb::ndarray\<...\>::operator() -`. It does not apply to GPU arrays because they are -usually not accessed in this way. - -Consider the following snippet, which fills a 2D array with data: - -.. code-block:: cpp - - void fill(nb::ndarray, nb::c_contig, nb::device::cpu> arg) { - for (size_t i = 0; i < arg.shape(0); ++i) - for (size_t j = 0; j < arg.shape(1); ++j) - arg(i, j) = /* ... */; - } - -While functional, this code is not perfect. The problem is that to compute the -address of an entry, ``operator()`` accesses the DLPack array descriptor. This -indirection can break certain compiler optimizations. - -nanobind provides the method :cpp:func:`ndarray\<...\>::view() ` -to fix this. It creates a tiny data structure that provides all information -needed to access the array contents, and which can be held within CPU -registers. All relevant compile-time information (:cpp:class:`nb::ndim `, -:cpp:class:`nb::shape `, :cpp:class:`nb::c_contig `, -:cpp:class:`nb::f_contig `) is materialized in this view, which -enables constant propagation, auto-vectorization, and loop unrolling. - -An improved version of the example using such a view is shown below: - -.. code-block:: cpp - - void fill(nb::ndarray, nb::c_contig, nb::device::cpu> arg) { - auto v = arg.view(); // <-- new! - - for (size_t i = 0; i < v.shape(0); ++i) // Important; use 'v' instead of 'arg' everywhere in loop - for (size_t j = 0; j < v.shape(1); ++j) - v(i, j) = /* ... */; - } - -Note that the view performs no reference counting. You may not store it in a way -that exceeds the lifetime of the original array. - -When using OpenMP to parallelize expensive array operations, pass the -``firstprivate(view_1, view_2, ...)`` so that each worker thread can copy the -view into its register file. - -.. code-block:: cpp - - auto v = arg.view(); - #pragma omp parallel for schedule(static) firstprivate(v) - for (...) { /* parallel loop */ } - -.. _ndarray-runtime-specialization: - -Specializing views at runtime -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -As mentioned earlier, element access via ``operator()`` only works when both -the array's scalar type and its dimension are specified within the type (i.e., -when they are known at compile time); the same is also true for array views. -However, sometimes, it is useful that a function can be called with different -array types. - -You may use the :cpp:func:`ndarray\<...\>::view() ` method to -create *specialized* views if a run-time check determines that it is safe to -do so. For example, the function below accepts contiguous CPU arrays and -performs a loop over a specialized 2D ``float`` view when the array is of -this type. - -.. code-block:: cpp - - void fill(nb::ndarray arg) { - if (arg.dtype() == nb::dtype() && arg.ndim() == 2) { - auto v = arg.view>(); // <-- new! - - for (size_t i = 0; i < v.shape(0); ++i) { - for (size_t j = 0; j < v.shape(1); ++j) { - v(i, j) = /* ... */; - } - } - } else { /* ... */ } - } - -Array libraries ---------------- - -The Python `array API standard `__ -defines a common interface and interchange protocol for nd-array libraries. In particular, to -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: - -.. 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); - }); - -Returning a raw :cpp:class:`nb::ndarray ` without framework annotation -will produce a DLPack capsule, which is what the interface expects. - -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 --------------------------- - -Why does my returned nd-array contain corrupt data? -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If your nd-array bindings lead to undefined behavior (data corruption or -crashes), then this is usually an ownership issue. Please review the section on -:ref:`data ownership ` for details. - -Why does nanobind not accept my NumPy array? -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -When binding a function that takes an ``nb::ndarray`` as input, nanobind -will by default require that array to be writable. This means that the function -cannot be called using NumPy arrays that are marked as constant. - -If you wish your function to be callable with constant input, either change the -parameter to ``nb::ndarray`` (if the array is parameterized by -type), or write ``nb::ndarray`` to accept a read-only array of any -type. - -Limitations related to ``dtypes`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. _dtype_restrictions: - -Libraries like `NumPy `__ support arrays with flexible -internal representations (*dtypes*), including - -- Floating point and integer arrays with various bit depths - -- Null-terminated strings - -- Arbitrary Python objects - -- Heterogeneous data structures composed of multiple fields - -nanobind's :cpp:class:`nb::ndarray\<...\> ` is based on the `DLPack -`__ array exchange protocol, which causes it to -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/src/nanobind/docs/ndarray_index.rst b/src/nanobind/docs/ndarray_index.rst deleted file mode 100644 index 74106d4..0000000 --- a/src/nanobind/docs/ndarray_index.rst +++ /dev/null @@ -1,13 +0,0 @@ -.. _ndarrays: - -N-dimensional arrays -==================== - -nanobind provides two alternative interfaces to exchange array data between -Python and C++. - -.. toctree:: - :maxdepth: 1 - - ndarray - eigen diff --git a/src/nanobind/docs/ownership.rst b/src/nanobind/docs/ownership.rst deleted file mode 100644 index c2ca16c..0000000 --- a/src/nanobind/docs/ownership.rst +++ /dev/null @@ -1,400 +0,0 @@ -.. _ownership: - -.. cpp:namespace:: nanobind - -Object ownership -================ - -Python and C++ don't manage the lifetime and storage of objects in the same -way. Consequently, two questions arise whenever an object crosses the language -barrier: - -- Who actually *owns* this object? C++? Python? Both?! - -- Can we safely determine when it is no longer needed? - -This is important: we *must* exclude the possibility that Python destroys an -object that is still being used by C++ (or vice versa). - -The :ref:`previous section ` introduced three ways of exchanging -information between C++ and Python: :ref:`type casters `, -:ref:`bindings `, and :ref:`wrappers `. -It is specifically -:ref:`bindings ` for which these two questions must be answered. - -.. _ownership_problem: - -A problematic example ---------------------- - -Consider the following problematic example to see what can go wrong: - -.. code-block:: cpp - - #include - namespace nb = nanobind; - - struct Data { }; - Data data; // Data global variable & function returning a pointer to it - Data *get_data() { return &data; } - - NB_MODULE(my_ext, m) { - nb::class_(m, "Data"); - - // KABOOM, calling this function will crash the Python interpreter - m.def("get_data", &get_data); - } - -The bound function ``my_ext.get_data()`` returns a Python object of type -``my_ext.Data`` that wraps the pointer ``&data`` and takes ownership of it. - -When Python eventually garbage collects the object, nanobind will try to free -the (non-heap-allocated) C++ instance via ``operator delete``, causing a -segmentation fault. - -To avoid this problem, we can - -1. **Provide more information**: the problem was that nanobind *incorrectly* - transferred ownership of a C++ instance to the Python side. To fix this, we - can add a :ref:`return value policy ` annotation that clarifies - what to do with the return value. - -2. **Make ownership transfer explicit**: C++ types passed via :ref:`unique - pointers ` (``std::unique_ptr``) make the ownership transfer - explicit in the type system, which would have revealed the problem in this - example. - -3. **Switch to shared ownership**: C++ types passed via :ref:`shared pointers - ` (``std::shared_ptr``), or which use :ref:`intrusive - reference counting ` can be shared by C++ and Python. The - whole issue disappears because ownership transfer is no longer needed. - -The remainder of this section goes through each of these options. - -.. _rvp: - -Return value policies ---------------------- - -nanobind provides several *return value policy* annotations that can be -passed to :func:`module_::def`, :func:`class_::def`, and :func:`cpp_function`. -The default policy is :cpp:enumerator:`rv_policy::automatic`, which is usually -a reasonable default (but not in this case!). - -In the :ref:`problematic example `, the policy -:cpp:enumerator:`rv_policy::reference` should have been specified explicitly so -that the global instance is only *referenced* without any implied transfer of -ownership, i.e.: - -.. code-block:: cpp - - m.def("get_data", &get_data, nb::rv_policy::reference); - -On the other hand, this is not the right policy for many other situations, -where ignoring ownership could lead to resource leaks. As a developer using -this library, it is important that you familiarize yourself with the different -options below. In particular, the following policies are available: - -- :cpp:enumerator:`rv_policy::take_ownership`: - Create a thin Python object wrapper around the returned C++ instance without - making a copy and transfer ownership to Python. When the - Python wrapper is eventually garbage collected, nanobind will call the C++ - ``delete`` operator to free the C++ instance. - - In the example below, a function uses this policy to transfer ownership of a - heap-allocated C++ instance to Python: - - .. code-block:: cpp - - m.def("make_data", []{ return new Data(); }, nb::rv_policy::take_ownership); - - The return value policy declaration could actually have been omitted here - because :cpp:enumerator:`take_ownership ` is the - default for *pointer return values* (see :cpp:enumerator:`automatic - `). - -- :cpp:enumerator:`rv_policy::copy`: - Copy-construct a new Python object from the C++ instance. The copy will be - owned by Python, while C++ retains ownership of the original. - - In the example below, a function uses this policy to return a reference to a - C++ instance. The owner and lifetime of such a reference may not be clear, so - the safest route is to make a copy. - - .. code-block:: cpp - - struct A { - B &b() { /* .. unknown code .. */ } - }; - - nb::class_(m, "A") - .def("b", &A::b, nb::rv_policy::copy); - - The return value policy declaration could actually have been omitted here - because :cpp:enumerator:`copy ` is the default for *lvalue - reference* return values (see :cpp:enumerator:`automatic - `). - -- :cpp:enumerator:`rv_policy::move`: - Move-construct a new Python object from the C++ instance. The new object will - be owned by Python, while C++ retains ownership of the original (whose - contents were likely invalidated by the move operation). - - In the example below, a function uses this policy to return a C++ instance by - value. The :cpp:enumerator:`copy ` operation mentioned above - would also be safe to use, but move construction has the potential of being - significantly more efficient. - - .. code-block:: cpp - - struct A { - B b() { return B(...); } - }; - - nb::class_(m, "A") - .def("b", &A::b, nb::rv_policy::move); - - The return value policy declaration could actually have been omitted here - because :cpp:enumerator:`move ` is the default for *functions - that return by value* (see :cpp:enumerator:`automatic - `). - -- :cpp:enumerator:`rv_policy::reference`: - Create a thin Python object wrapper around the returned C++ instance without - making a copy, but *do not transfer ownership to Python*. nanobind will never - call the C++ ``delete`` operator, even when the wrapper expires. - The C++ side is responsible for destructing the C++ instance. - - This return value policy is *dangerous* and should be used cautiously. - Undefined behavior will ensue when the C++ side deletes the instance while it - is still being used by Python. If you need to use this policy, combine it with - a :cpp:struct:`keep_alive` function binding annotation to manage the lifetime. - Or use the simple and safe :cpp:enumerator:`reference_internal - ` alternative described next. - - Below is an example use of this return value policy to reference a - global variable that does not need ownership and lifetime management. - - .. code-block:: cpp - - Data data; // This is a global variable - - m.def("get_data", []{ return &data; }, nb::rv_policy::reference) - -- :cpp:enumerator:`rv_policy::reference_internal`: A policy for *methods* that - expose an internal field. The lifetime of the field must match that of the - parent object. - - The policy resembles :cpp:enumerator:`reference ` in - that it creates creates a thin Python object wrapper around the returned C++ - field without making a copy, and without transferring ownership to Python. - - Furthermore, it ensures that the instance owning the field (implicit - ``this``/``self`` argument) cannot be garbage collected while an object - representing the field is alive. - - The example below uses this policy to implement a *getter* that permits - mutable access to an internal field. - - .. code-block:: cpp - - struct MyClass { - public: - MyField &field() { return m_field; } - - private: - MyField m_field; - }; - - nb::class_(m, "MyClass") - .def("field", &MyClass::field, nb::rv_policy::reference_internal); - - More advanced variations of this scheme are also possible using combinations - of :cpp:enumerator:`reference ` and the - :cpp:struct:`keep_alive` function binding annotation. - -- :cpp:enumerator:`rv_policy::none`: This is the most conservative policy: it - simply refuses the cast unless the C++ instance already has a corresponding - Python object, in which case the question of ownership becomes moot. - -- :cpp:enumerator:`rv_policy::automatic`: This is the default return value - policy, which falls back to :cpp:enumerator:`take_ownership - ` when the return value is a pointer, - :cpp:enumerator:`move ` when it is a rvalue reference, and - :cpp:enumerator:`copy ` when it is a lvalue reference. - -- :cpp:enumerator:`rv_policy::automatic_reference`: This policy matches - :cpp:enumerator:`automatic ` but falls back to - :cpp:enumerator:`reference ` when the return value is a - pointer. It is the default for function arguments when calling Python - functions from C++ code via :cpp:func:`detail::api::operator()`. You probably - won't need to use this policy in your own code. - -.. _unique_ptr: - -Unique pointers ---------------- - -Passing a STL unique pointer embodies an ownership transfer---a return value -policy annotation is therefore not needed. To bind functions that receive or -return ``std::unique_ptr<..>``, add the extra include directive - -.. code-block:: cpp - - #include - -.. note:: - - While this this header file technically contains a :ref:`type caster - `, it is *not* affected by their usual limitations (mandatory - copy/conversion, inability to mutate function arguments). - -**Example**: The following example binds two functions that create and consume -instances of a C++ type ``Data`` via unique pointers. - -.. code-block:: cpp - - #include - - namespace nb = nanobind; - - NB_MODULE(my_ext, m) { - struct Data { }; - nb::class_(m, "Data"); - m.def("create", []() { return std::make_unique(); }); - m.def("consume", [](std::unique_ptr x) { /* no-op */ }); - } - -Calling a function taking a unique pointer from Python invalidates the passed -Python object. nanobind will refuse further use of it: - -.. code-block:: pycon - :emphasize-lines: 8,9 - - Python 3.11.1 (main, Dec 23 2022, 09:28:24) [Clang 14.0.0 (clang-1400.0.29.202)] on darwin - Type "help", "copyright", "credits" or "license" for more information. - >>> import my_ext - - >>> x = my_ext.create() - >>> my_ext.consume(x) - - >>> my_ext.consume(x) - :1: RuntimeWarning: nanobind: attempted to access an uninitialized instance of type 'my_ext.Data'! - - TypeError: consume(): incompatible function arguments. The following argument types are supported: - 1. consume(arg: my_ext.Data, /) -> None - - Invoked with types: my_ext.Data - -We strongly recommend that you replace all use of ``std::unique_ptr`` by -``std::unique_ptr>`` in your code. Without the latter type -declaration, which references a custom nanobind-provided deleter -:cpp:class:`nb::deleter\ `, nanobind cannot transfer ownership of -objects constructed using :cpp:class:`nb::init\<...\> ` to C++ and will -refuse to do so with an error message. Further detail on this special case can -be found in the *advanced* :ref:`section ` on object ownership. - -.. _shared_ownership: - -Shared ownership ----------------- - -In a *shared ownership* model, an object can have multiple owners that each -register their claim by holding a *reference*. The system keeps track of the -total number of references and destroys the object once the count reaches zero. -Passing such an object in a function call shares ownership between the caller -and callee. nanobind makes this behavior seamless so that everything works -regardless of whether caller/callee are written in C++ or Python. - -.. _shared_ptr: - -Shared pointers -^^^^^^^^^^^^^^^ - -STL shared pointers (``std::shared_ptr``) allocate a separate control block to -keep track of the reference count, which makes them very general but also slightly -less efficient than other alternatives. - -nanobind's support for shared pointers requires an extra include directive: - -.. code-block:: cpp - - #include - -.. note:: - - While this this header file technically contains a :ref:`type caster - `, it is *not* affected by their usual limitations (mandatory - copy/conversion, inability to mutate function arguments). - -You don't need to specify a return value policy annotation when a function -returns a shared pointer. - -nanobind's implementation of ``std::shared_ptr`` support typically -allocates a new ``shared_ptr`` control block each time a Python object -must be converted to ``std::shared_ptr``. The new ``shared_ptr`` -"owns" a reference to the Python object, and its deleter drops that -reference. This has the advantage that the Python portion of the -object will be kept alive by its C++-side references (which is -important when implementing C++ virtual methods in Python), but it can -be inefficient when passing the same object back and forth between -Python and C++ many times, and it means that the ``use_count()`` -method of ``std::shared_ptr`` will return a value that does not -capture all uses. Some of these problems can be mitigated by modifying -``T`` so that it inherits from ``std::enable_shared_from_this``. -See the :ref:`advanced section ` on object ownership -for more details on the implementation. - -nanobind has limited support for objects that inherit from -``std::enable_shared_from_this`` to allow safe conversion of raw -pointers to shared pointers. The safest way to deal with these objects -is to always use ``std::make_shared(...)`` when constructing them in C++, -and always pass them across the Python/C++ boundary wrapped in an explicit -``std::shared_ptr``. If you do this, then there shouldn't be any -surprises. If you will be passing raw ``T*`` pointers around, then -read the :ref:`advanced section on object ownership ` -for additional caveats. - -.. _intrusive_intro: - -Intrusive reference counting -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Intrusive reference counting is the most flexible and efficient way of handling -shared ownership. The main downside is that you must adapt the base class of -your object hierarchy to the needs of nanobind. - -The core idea is to define base class (e.g. ``Object``) common to all bound -types requiring shared ownership. That class contains a builtin atomic counter -(e.g., ``m_ref_count``) and a Python object pointer (e.g., ``m_py_object``). - -.. code-block:: cpp - - class Object { - ... - private: - mutable std::atomic m_ref_count { 0 }; - PyObject *m_py_object = nullptr; - }; - -The core idea is that such ``Object`` instances can either be managed by C++ or -Python. In the former case, the ``m_ref_count`` field keeps track of the number -of outstanding references. In the latter case, reference counting is handled by -Python, and the ``m_ref_count`` field remains unused. - -This is actually little wasteful---nanobind therefore ships with a more -efficient reference counter sample implementation that supports both use cases -while requiring only ``sizeof(void*)`` bytes of storage: - -.. code-block:: cpp - - #include - - class Object { - ... - private: - intrusive_counter m_ref_count; - }; - -Please read the dedicated :ref:`section on intrusive reference counting -` for more details on how to set this up. diff --git a/src/nanobind/docs/ownership_adv.rst b/src/nanobind/docs/ownership_adv.rst deleted file mode 100644 index bede697..0000000 --- a/src/nanobind/docs/ownership_adv.rst +++ /dev/null @@ -1,398 +0,0 @@ -.. _ownership_adv: - -.. cpp:namespace:: nanobind - -Object ownership, continued -=========================== - -This section covers intrusive reference counting as an alternative to shared -pointers, and it explains the nitty-gritty details of how shared and unique -pointer conversion is implemented in nanobind. - -.. _intrusive: - -Intrusive reference counting ----------------------------- - -nanobind provides a custom intrusive reference counting solution that -completely solves the issue of shared C++/Python object ownership, while -avoiding the overheads and complexities of traditional C++ shared pointers -(``std::shared_ptr``). - -The main limitation is that it requires adapting the base class of an object -hierarchy according to the needs of nanobind, which may not always be possible. - -Motivation -^^^^^^^^^^ - -Consider the following simple class with intrusive reference counting: - -.. code-block:: cpp - - class Object { - public: - void inc_ref() const noexcept { ++m_ref_count; } - - void dec_ref() const noexcept { - if (--m_ref_count == 0) - delete this; - } - - private: - mutable std::atomic m_ref_count { 0 }; - }; - -It contains an atomic counter that stores the number of references. When the -counter reaches zero, the object deallocates itself. Easy and efficient. - -The advantage of over ``std::shared_ptr`` is that no separate control block -must be allocated. Technical band-aids like ``std::enable_shared_from_this`` -can also be avoided, since the reference count is always found in the object -itself. - -However, one issue that tends to arise when a type like ``Object`` is -wrapped using nanobind is that there are now *two* separate reference counts -referring to the same object: one in Python’s ``PyObject``, and one in -``Object``. This can lead to a problematic reference cycle: - -- Python’s ``PyObject`` needs to keep the ``Object`` instance alive so that it - can be safely passed to C++ functions. - -- The C++ ``Object`` may in turn need to keep the ``PyObject`` alive. This - is the case when a subclass uses *trampolines* (:c:macro:`NB_TRAMPOLINE`, - :c:macro:`NB_OVERRIDE`) to catch C++ virtual function calls and - potentially dispatch them to an overridden implementation in Python. In - this case, the C++ instance needs to be able to perform a function call on - its own Python object identity, which requires a reference. - -The source of the problem is that there are *two* separate counters that try -to reason about the reference count of *one* instance, which leads to an -uncollectable inter-language reference cycle. - -The solution -^^^^^^^^^^^^ -We can solve the problem by using just one counter: - -- if an instance lives purely on the C++ side, the ``m_ref_count`` - field is used to reason about the number of references. - -- The first time that an instance is exposed to Python (by being - created from Python, or by being returned from a bound C++ function), - lifetime management switches over to Python. - -The file `nanobind/intrusive/counter.h -`_ -includes an official sample implementation of this functionality. It contains an extra optimization to pack *either* -a reference counter or a pointer to a ``PyObject*`` into a single -``sizeof(void*)``-sized field. - -The most basic interface, :cpp:class:`intrusive_counter` represents an atomic -counter that can be increased (via :cpp:func:`intrusive_counter::inc_ref()`) or -decreased (via :cpp:func:`intrusive_counter::dec_ref()`). When the counter -reaches zero, the object should be deleted, which ``dec_ref()`` indicates by -returning ``true``. - -In addition to this simple counting mechanism, ownership of the object can also -be transferred to Python (via :cpp:func:`intrusive_counter::set_self_py()`). In -this case, subsequent calls to ``inc_ref()`` and ``dec_ref()`` modify the -reference count of the underlying Python object. - -To incorporate intrusive reference counting into your own project, you would -usually add an :cpp:class:`intrusive_counter`-typed member to the base class of an object -hierarchy and expose it as follows: - -.. code-block:: cpp - - #include - - class Object { - public: - void inc_ref() noexcept { m_ref_count.inc_ref(); } - bool dec_ref() noexcept { return m_ref_count.dec_ref(); } - - // Important: must declare virtual destructor - virtual ~Object() = default; - - void set_self_py(PyObject *self) noexcept { - m_ref_count.set_self_py(self); - } - - private: - nb::intrusive_counter m_ref_count; - }; - - // Convenience function for increasing the reference count of an instance - inline void inc_ref(Object *o) noexcept { - if (o) - o->inc_ref(); - } - - // Convenience function for decreasing the reference count of an instance - // and potentially deleting it when the count reaches zero - inline void dec_ref(Object *o) noexcept { - if (o && o->dec_ref()) - delete o; - } - -Alternatively, you could also inherit from :cpp:class:`intrusive_base`, which -obviates the need for all of the above declarations: - -.. code-block:: cpp - - class Object : public nb::intrusive_base { - public: - // ... - }; - -The main change in the bindings is that the base class must specify a -:cpp:class:`nb::intrusive_ptr ` annotation to inform an instance -that lifetime management has been taken over by Python. This annotation is -automatically inherited by all subclasses. In the linked example, this is done -via the ``Object::set_self_py()`` method that we can now call from the class -binding annotation: - -.. code-block:: cpp - - nb::class_( - m, "Object", - nb::intrusive_ptr( - [](Object *o, PyObject *po) noexcept { o->set_self_py(po); })); - -Also, somewhere in your binding initialization code, you must register Python -reference counting hooks with the intrusive reference counter class. This -allows its implementation of the code in ``nanobind/intrusive/counter.h`` to -*not* depend on Python (this means that it can be used in projects where Python -bindings are an optional component). - -.. code-block:: cpp - - nb::intrusive_init( - [](PyObject *o) noexcept { - nb::gil_scoped_acquire guard; - Py_INCREF(o); - }, - [](PyObject *o) noexcept { - nb::gil_scoped_acquire guard; - Py_DECREF(o); - }); - -These ``counter.h`` include file references several functions that must be -compiled somewhere inside the project, which can be accomplished by including -the following file from a single ``.cpp`` file. - -.. code-block:: cpp - - #include - -Having to call :cpp:func:`inc_ref()` and :cpp:func:`dec_ref()` many times to -perform manual reference counting in project code can quickly become tedious. -Nanobind also ships with a :cpp:class:`ref\ ` RAII helper class to -help with this. - -.. code-block:: cpp - - #include - - void foo() { - /// Assignment to ref automatically increases the object's reference count - ref x = new MyObject(); - - // ref can be used like a normal pointer - x->func(); - - } // <-- ref::~ref() calls dec_ref(), which deletes the now-unreferenced instance - -When the file ``nanobind/intrusive/ref.h`` is included following -``nanobind/nanobind.h``, it also exposes a custom type caster to bind functions -taking or returning ``ref``-typed values. - -That's it. If you use this approach, any potential issues involving shared -pointers, return value policies, reference leaks with trampolines, etc., can -be avoided from the beginning. - -.. _shared_ptr_adv: - -Shared pointers, continued --------------------------- - -The following continues the :ref:`discussion of shared pointers ` -in the introductory section on object ownership and provides detail on how -shared pointer conversion is *implemented* by nanobind. - -When the user calls a C++ function taking an argument of type -``std::shared_ptr`` from Python, ownership of that object must be -shared between C++ to Python. nanobind does this by increasing the reference -count of the ``PyObject`` and then creating a ``std::shared_ptr`` with a new -control block containing a custom deleter that will in turn reduce the Python -reference count upon destruction of the shared pointer. - -When a C++ function returns a ``std::shared_ptr``, nanobind -checks if the instance already has a ``PyObject`` counterpart -(nothing needs to be done in this case). Otherwise, it indicates -shared ownership by creating a temporary ``std::shared_ptr`` on -the heap that will be destructed when the ``PyObject`` is garbage -collected. - -The approach in nanobind was chosen following on discussions with `Ralf -Grosse-Kunstleve `_; it is unusual in that multiple -``shared_ptr`` control blocks are potentially allocated for the same object, -which means that ``std::shared_ptr::use_count()`` generally won’t show the -true global reference count. - -.. _enable_shared_from_this: - -enable_shared_from_this -^^^^^^^^^^^^^^^^^^^^^^^ - -The C++ standard library class ``std::enable_shared_from_this`` -allows an object that inherits from it to locate an existing -``std::shared_ptr`` that manages that object. nanobind supports -types that inherit from ``enable_shared_from_this``, with some caveats -described in this section. - -Background (not nanobind-specific): Suppose a type ``ST`` inherits -from ``std::enable_shared_from_this``. When a raw pointer ``ST -*obj`` or ``std::unique_ptr obj`` is wrapped in a shared pointer -using a constructor of the form ``std::shared_ptr(obj, ...)``, a -reference to the new ``shared_ptr``\'s control block is saved (as -``std::weak_ptr``) inside the object. This allows new -``shared_ptr``\s that share ownership with the existing one to be -obtained for the same object using ``obj->shared_from_this()`` or -``obj->weak_from_this()``. - -nanobind's support for ``std::enable_shared_from_this`` consists of three -behaviors: - -* If a raw pointer ``ST *obj`` is returned from C++ to Python, and - there already exists an associated ``std::shared_ptr`` which - ``obj->shared_from_this()`` can locate, then nanobind will produce a - Python instance that shares ownership with it. The behavior is - identical to what would happen if the C++ code did ``return - obj->shared_from_this();`` (returning an explicit - ``std::shared_ptr`` to Python) rather than ``return obj;``. - The return value policy has limited effect in this case; you will get - shared ownership on the Python side regardless of whether you used - `rv_policy::take_ownership` or `rv_policy::reference`. - (`rv_policy::copy` and `rv_policy::move` will still create a new - object that has no ongoing relationship to the returned pointer.) - - * Note that this behavior occurs only if such a ``std::shared_ptr`` - already exists! If not, then nanobind behaves as it would without - ``enable_shared_from_this``: a raw pointer will transfer exclusive - ownership to Python by default, or will create a non-owning reference - if you use `rv_policy::reference`. - -* If a Python object is passed to C++ as ``std::shared_ptr obj``, - and there already exists an associated ``std::shared_ptr`` which - ``obj->shared_from_this()`` can locate, then nanobind will produce a - ``std::shared_ptr`` that shares ownership with it: an additional - reference to the same control block, rather than a new control block - (as would occur without ``enable_shared_from_this``). This improves - performance and makes the result of ``shared_ptr::use_count()`` more - accurate. - -* If a Python object is passed to C++ as ``std::shared_ptr obj``, and - there is no associated ``std::shared_ptr`` that - ``obj->shared_from_this()`` can locate, then nanobind will produce - a ``std::shared_ptr`` as usual (with a new control block whose deleter - drops a Python object reference), *and* will do so in a way that enables - future calls to ``obj->shared_from_this()`` to find it as long - as any ``shared_ptr`` that shares this control block is still alive on - the C++ side. - - (Once all of the ``std::shared_ptr``\s that share this control block - have been destroyed, the underlying PyObject reference being - managed by the ``shared_ptr`` deleter will be dropped, - and ``shared_from_this()`` will stop working. It can be reenabled by - passing the Python object back to C++ as ``std::shared_ptr`` once more, - which will create another control block.) - -Bindings for a class that supports ``enable_shared_from_this`` will be -slightly larger than bindings for a class that doesn't, as nanobind -must produce type-specific code to implement the above behaviors. - -.. warning:: The ``shared_from_this()`` method will only work when there - is actually a ``std::shared_ptr`` managing the object. A nanobind - instance constructed from Python will not have an associated - ``std::shared_ptr`` yet, so ``shared_from_this()`` will throw an - exception if you pass such an instance to C++ using a reference or - raw pointer. ``shared_from_this()`` will only work when there exists - a corresponding live ``std::shared_ptr`` on the C++ side. - - The only situation where nanobind will create the first - ``std::shared_ptr`` for an object (thus enabling - ``shared_from_this()``), even with ``enable_shared_from_this``, is - when a Python instance is passed to C++ as the explicit type - ``std::shared_ptr``. If you don't do this, or if no such - ``std::shared_ptr`` is still alive, then ``shared_from_this()`` will - throw an exception. It also works to create the ``std::shared_ptr`` - on the C++ side, such as by using a factory function which always - uses ``std::make_shared(...)`` to construct the object, and - returns the resulting ``std::shared_ptr`` to Python. - -If you need to enable ``shared_from_this`` immediately upon -regular Python-side object construction (i.e., ``SomeType(*args)`` -rather than ``SomeType.some_fn(*args)``), you can bind a C++ function -that returns ``std::shared_ptr`` as your class's ``__new__`` method. -See the documentation on :ref:`customizing object creation `. - -.. warning:: C++ code that receives a raw pointer ``T *obj`` *must not* - assume that it has exclusive ownership of ``obj``, or even that - ``obj`` is allocated on the C++ heap (via ``operator new``); - ``obj`` might instead be a subobject of a nanobind instance - allocated from Python. This applies even if ``T`` supports - ``shared_from_this()`` and there is no associated - ``std::shared_ptr``. Lack of a ``shared_ptr`` does *not* imply - exclusive ownership; it just means there's no way to share ownership - with whoever the current owner is. - -.. _unique_ptr_adv: - -Unique pointers ---------------- - -The following continues the :ref:`discussion of unique pointers ` -in the introductory section on object ownership and provides detail on how -unique pointer conversion is *implemented* by nanobind. - -Whereas ``std::shared_ptr<..>`` could abstract over details concerning -storage and the deletion mechanism, this is not possible in the simpler -``std::unique_ptr<..>``, which means that some of those details leak into -the type signature. - -When the user calls a C++ function taking an argument of type ``std::unique_ptr`` from Python, ownership of that object must be transferred from C++ to Python. - -- When ``Deleter`` is ``std::default_delete`` (i.e., the default - when no ``Deleter`` is specified), this ownership transfer is only - possible when the instance was originally created by a *new expression* - within C++ and nanobind has taken over ownership (i.e., it was created by - a function returning a raw pointer ``T *value`` with - :cpp:enumerator:`rv_policy::take_ownership`, or a function returning a - ``std::unique_ptr``). This limitation exists because the ``Deleter`` - will execute the statement ``delete value`` when the unique pointer - expires, causing undefined behavior when the object was allocated within - Python (the problem here is that nanobind uses the Python memory allocator - and furthermore co-locates Python and C++ object storage. A *delete - expression* cannot be used in such a case). nanobind detects this, refuses - unsafe conversions with a ``TypeError`` and emits a separate warning. - -- To enable ownership transfer under all conditions, nanobind - provides a custom ``Deleter`` named :cpp:class:`nb::deleter\ - ` that uses reference counting to keep the underlying - ``PyObject`` alive during the lifetime of the unique pointer. Following - this route requires changing function signatures so that they use - ``std::unique_ptr>`` instead of ``std::unique_ptr``. - This custom deleter supports ownership by both C++ and Python and can be - used in all situations. - -In both cases, a Python object may continue to exist after ownership was -transferred to C++ side. nanobind marks this object as *invalid*: any -operations involving it will fail with a ``TypeError``. Reverse ownership -transfer at a later point will make it usable again. - -Binding functions that return a ``std::unique_ptr`` always -works: nanobind will then acquire or reacquire ownership of the object. - -Deleters other than ``std::default_delete`` or ``nb::deleter`` are -*not supported*. diff --git a/src/nanobind/docs/packaging.rst b/src/nanobind/docs/packaging.rst deleted file mode 100644 index 95d949d..0000000 --- a/src/nanobind/docs/packaging.rst +++ /dev/null @@ -1,332 +0,0 @@ -.. _packaging: - -Packaging -========= - -A Python *wheel* is a self-contained binary file that bundles Python code and -extension libraries along with metadata such as versioned package dependencies. -Wheels are easy to download and install, and they are the recommended mechanism -for distributing extensions created using nanobind. - -This section walks through the recommended sequence of steps to build wheels -and optionally automate this process to simultaneously target many platforms -(Linux, Windows, macOS) and processors (i386, x86_64, arm64) using the `GitHub -Actions `__ CI service. - -Note that all of the recommended practices have already been implemented in the -`nanobind_example repository `_, -which is a minimal C++ project with nanobind-based bindings. You may therefore -prefer to clone this repository and modify its contents. - -Step 1: Overview ----------------- - -The example project has a simple directory structure: - -.. code-block:: text - - ├── README.md - ├── CMakeLists.txt - ├── pyproject.toml - └── src/ - ├── my_ext.cpp - └── my_ext/ - └── __init__.py - -The file ``CMakeLists.txt`` contains the C++-specifics part of the build -system, while ``pyproject.toml`` explains how to turn the example into a wheel. -The file ``README.md`` should ideally explain how to use the project in more -detail. Its contents are arbitrary, but the file must exist for the following -build system to work. - -All source code is located in a ``src`` directory containing the Python package -as a subdirectory. - -Compilation will turn ``my_ext.cpp`` into a shared library in the package -directory, which has an underscored platform-dependent name (e.g., -``_my_ext_impl.cpython-311-darwin.so``) to indicate that it is an -implementation detail. The ``src/my_ext/__init__.py`` imports the extension and -exposes relevant functionality. In this small example project, it only contains -a single line: - -.. code-block:: python - - from ._my_ext_impl import hello - -The file ``src/my_ext.cpp`` contains minimal bindings for an example function: - -.. code-block:: cpp - - #include - - NB_MODULE(_my_ext_impl, m) { - m.def("hello", []() { return "Hello world!"; }); - } - -The next two steps will set up the infrastructure needed for wheel generation. - -Step 2: Specify build dependencies and metadata ------------------------------------------------ - -In the root directory of the project, create a file named ``pyproject.toml`` -listing *build-time dependencies*. Note that runtime dependencies *do not* need -to be added here. The following core dependencies are required by nanobind: - -.. code-block:: toml - - [build-system] - requires = ["scikit-build-core >=0.4.3", "nanobind >=1.3.2"] - build-backend = "scikit_build_core.build" - -You may need to increase the minimum nanobind version in the above snippet if -you are using features from versions newer than 1.3.2. - -Just below the list of build-time requirements, specify project metadata including: - -- The project's name (which must be a valid package name) -- The version number -- A brief (1-line) description of the project -- The name of a more detailed README file -- The list of authors with email addresses. -- The software license -- The project web page -- Runtime dependencies, if applicable - -An example is shown below: - -.. code-block:: toml - - [project] - name = "my_ext" - version = "0.0.1" - description = "A brief description of what this project does" - readme = "README.md" - requires-python = ">=3.8" - authors = [ - { name = "Your Name", email = "your.email@address.com" }, - ] - classifiers = [ - "License :: BSD", - ] - # Optional: runtime dependency specification - # dependencies = [ "cryptography >=41.0" ] - - [project.urls] - Homepage = "https://github.com/your/project" - -We will use `scikit-build-core -`__ to build wheels, and -this tool also has its own configuration block in ``pyproject.toml``. The -following defaults are recommended: - -.. code-block:: toml - - [tool.scikit-build] - # Protect the configuration against future changes in scikit-build-core - minimum-version = "0.4" - - # Setuptools-style build caching in a local directory - build-dir = "build/{wheel_tag}" - - # Build stable ABI wheels for CPython 3.12+ - wheel.py-api = "cp312" - -Step 3: Set up a CMake build system ------------------------------------ - -Next, we will set up a suitable ``CMakeLists.txt`` file in the root directory. -Since this build system is designed to be invoked through -``scikit-build-core``, it does not make sense to perform a standalone CMake -build. The message at the top warns users attempting to do this. - -.. code-block:: cmake - - # Set the minimum CMake version and policies for highest tested version - cmake_minimum_required(VERSION 3.15...3.27) - - # Set up the project and ensure there is a working C++ compiler - project(my_ext LANGUAGES CXX) - - # Warn if the user invokes CMake directly - if (NOT SKBUILD) - message(WARNING "\ - This CMake file is meant to be executed using 'scikit-build-core'. - Running it directly will almost certainly not produce the desired - result. If you are a user trying to install this package, use the - command below, which will install all necessary build dependencies, - compile the package in an isolated environment, and then install it. - ===================================================================== - $ pip install . - ===================================================================== - If you are a software developer, and this is your own package, then - it is usually much more efficient to install the build dependencies - in your environment once and use the following command that avoids - a costly creation of a new virtual environment at every compilation: - ===================================================================== - $ pip install nanobind scikit-build-core[pyproject] - $ pip install --no-build-isolation -ve . - ===================================================================== - You may optionally add -Ceditable.rebuild=true to auto-rebuild when - the package is imported. Otherwise, you need to rerun the above - after editing C++ files.") - endif() - -Next, import Python and nanobind including the ``Development.SABIModule`` -component that can be used to create `stable ABI -`__ builds. - -.. code-block:: cmake - - # Try to import all Python components potentially needed by nanobind - find_package(Python 3.8 - REQUIRED COMPONENTS Interpreter Development.Module - OPTIONAL_COMPONENTS Development.SABIModule) - - # Import nanobind through CMake's find_package mechanism - find_package(nanobind CONFIG REQUIRED) - -The last two steps build and install the actual extension - -.. code-block:: cmake - - # We are now ready to compile the actual extension module - nanobind_add_module( - # Name of the extension - _my_ext_impl - - # Target the stable ABI for Python 3.12+, which reduces - # the number of binary wheels that must be built. This - # does nothing on older Python versions - STABLE_ABI - - # Source code goes here - src/my_ext.cpp - ) - - # Install directive for scikit-build-core - install(TARGETS _my_ext_impl LIBRARY DESTINATION my_ext) - - -Step 4: Install the package locally ------------------------------------ - -To install the package, run - -.. code-block:: bash - - $ cd - $ pip install . - -``pip`` will parse the ``pyproject.toml`` file and create a fresh environment -containing all needed dependencies. Following this, you should be able to -install and access the extension. - -.. code-block:: python - - >>> import my_ext - >>> my_ext.hello() - 'Hello world!' - -Alternatively, you can use the following command to generate a ``.whl`` file -instead of installing the package. - -.. code-block:: bash - - $ pip wheel . - -Step 5: Incremental rebuilds ----------------------------- - -The ``pip install`` and ``pip wheel`` commands are extremely conservative to -ensure reproducible builds. They create a pristine virtual environment and -install build-time dependencies before compiling the extension *from scratch*. - -It can be frustrating to wait for this lengthy sequence of steps after every -small change to a source file during the active development phase of a project. -To avoid this, first install the project's build dependencies, e.g.: - -.. code-block:: bash - - $ pip install nanobind scikit-build-core[pyproject] - -Next, install the project without build isolation to enable incremental builds: - -.. code-block:: bash - - $ pip install --no-build-isolation -ve . - -This command will need to be run after every change to reinstall the updated package. -For an even more interactive experience, use - -.. code-block:: bash - - $ pip install --no-build-isolation -Ceditable.rebuild=true -ve . - -This will automatically rebuild any code (if needed) whenever the ``my_ext`` -package is imported into a Python session. - -Step 6: Build wheels in the cloud ---------------------------------- - -On my machine, the ``pip wheel`` command produces a file named -``my_ext-0.0.1-cp311-cp311-macosx_13_0_arm64.whl`` that is specific to Python -3.11 running on an arm64 macOS machine. Other Python versions and operating -systems each require their own wheels, which leads to a dauntingly large build -matrix (though nanobind's stable ABI support will help to significantly reduce -the size of this matrix once Python 3.12 is more widespread). - -Rather than building these wheels manually on different machines, it is far -more efficient to use GitHub actions along with the powerful `cibuildwheel -`__ package to fully automate -the process. - -To do so, create a file named ``.github/workflows/wheels.yml`` containing -the contents of the `following file -`__. -You may want to remove the ``on: push:`` lines, otherwise, the action will run -after every commit, which is perhaps a bit excessive. In this case, you can -still trigger the action manually on the *Actions* tab of the GitHub project -page. - -Furthermore, append the following ``cibuildwheel``-specific configuration to -``pyproject.toml``: - -.. code-block:: toml - - [tool.cibuildwheel] - # Necessary to see build output from the actual compilation - build-verbosity = 1 - - # Optional: run pytest to ensure that the package was correctly built - # test-command = "pytest {project}/tests" - # test-requires = "pytest" - - # Needed for full C++17 support on macOS - [tool.cibuildwheel.macos.environment] - MACOSX_DEPLOYMENT_TARGET = "10.14" - -Following each run, the action provides a downloadable *build artifact*, which -is a ZIP file containing all the individual wheel files for each platform. - -By default, ``cibuildwheel`` will launch a very large build matrix, and it is -possible that your extension is not compatible with every single configuration. -For example, suppose that the project depends on Python 3.9+ and a 64 bit -processor. In this case, add further entries to the ``[tool.cibuildwheel]`` -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 -`__ explains the -possible options. - -If you set up a GitHub actions `secret -`__ named -``pypi_password`` containing a PyPI authentication token, the action will -automatically upload the generated wheels to the `Python Package Index (PyPI) -`__ when the action is triggered by a `software release event -`__. - diff --git a/src/nanobind/docs/porting.rst b/src/nanobind/docs/porting.rst deleted file mode 100644 index 832b7ae..0000000 --- a/src/nanobind/docs/porting.rst +++ /dev/null @@ -1,360 +0,0 @@ -.. cpp:namespace:: nanobind - -.. _porting: - -Porting guide -============= - -The API of nanobind is *extremely* similar to that of `pybind11 -`_, which makes porting existing -projects easy. Parts of the nanobind documentation are almost verbatim copies -pybind11's documentation. - -A number of noteworthy changes are documented below. - -Namespace ---------- - -nanobind types and functions are located in the ``nanobind`` namespace. The -following shorthand alias is recommended and used throughout the documentation: - -.. code-block:: cpp - - namespace nb = nanobind; - -Name changes ------------- - -The following macros, types, and functions were renamed: - -.. list-table:: - :widths: 42 48 - :header-rows: 1 - - * - pybind11 - - nanobind - - * - ``PYBIND11_MODULE(..)`` - - :c:macro:`NB_MODULE(..) ` - * - ``PYBIND11_OVERRIDE_*(..)`` - - :c:macro:`NB_OVERRIDE_*(..) ` - * - ``error_already_set`` - - :cpp:class:`python_error` - * - ``type::of()`` - - :cpp:func:`type\() ` - * - ``type`` - - :cpp:class:`type_object` - * - ``reinterpret_borrow(x)`` - - :cpp:func:`borrow\(x) ` - * - ``reinterpret_steal(x)`` - - :cpp:func:`steal\(x) ` - * - ``.def_readwrite(..)`` - - :cpp:func:`.def_rw(..) ` - * - ``.def_readonly(..)`` - - :cpp:func:`.def_ro(..) ` - * - ``.def_property(..)`` - - :cpp:func:`.def_prop_rw(..) ` - * - ``.def_property_readonly(..)`` - - :cpp:func:`.def_prop_ro(..) ` - * - ``.def_readwrite_static(..)`` - - :cpp:func:`.def_rw_static(..) ` - * - ``.def_readonly_static(..)`` - - :cpp:func:`.def_ro_static(..) ` - * - ``.def_property_static(..)`` - - :cpp:func:`.def_prop_rw_static(..) ` - * - ``.def_property_readonly_static(..)`` - - :cpp:func:`.def_prop_ro_static(..) ` - * - ``register_exception`` - - :cpp:class:`exception\ ` - - -None/null arguments -------------------- - -In contrast to pybind11, nanobind does *not* permit ``None``-valued function -arguments by default. You must enable them explicitly using the -:cpp:func:`arg::none() ` argument annotation, e.g.: - -.. code-block:: cpp - - m.def("func", &func, "arg"_a.none()); - -It is also possible to set a ``None`` default value, in which case the -:cpp:func:`.none() ` annotation can be omitted. - -.. code-block:: cpp - - m.def("func", &func, "arg"_a = nb::none()); - -``None``-valued arguments are only supported by two of the three parameter -passing styles described in the section on :ref:`information exchange -`. In particular, they are supported by :ref:`bindings ` -and :ref:`wrappers `, *but not* by :ref:`type casters -`. - -Shared pointers and holders ---------------------------- - -When nanobind instantiates a C++ type within Python, the resulting instance -data is stored *within* the created Python object ("``PyObject``"). -Alternatively, when an already existing C++ instance is transferred to Python -via a function return value and :cpp:enumerator:`rv_policy::reference`, -:cpp:enumerator:`rv_policy::reference_internal`, or -:cpp:enumerator:`rv_policy::take_ownership`, nanobind creates a smaller wrapper -``PyObject`` that only stores a pointer to the instance data. - -This is *very different* from pybind11, where the instance ``PyObject`` -contained a *holder type* (typically ``std::unique_ptr``) storing a pointer -to the instance data. Dealing with holders caused inefficiencies and introduced -complexity; they were therefore removed in nanobind. This has implications on -object ownership, shared ownership, and interactions with C++ shared/unique -pointers. The :ref:`intermediate ` and :ref:`advanced -` sections on object ownership provide further detail. - -The gist is that it is no longer necessary to specify holder types in the type -declaration: - -*pybind11*: - -.. code-block:: cpp - - py::class_>(m, "MyType") - -*nanobind*: - -.. code-block:: cpp - - nb::class_(m, "MyType") - -To bind functions that exchange shared/unique pointers, you must add one or -both of the following include directives to your code: - -.. code-block:: cpp - - #include - #include - -Binding functions that take ``std::unique_ptr`` arguments involves some -limitations that can be avoided by changing their signatures to -``std::unique_ptr>`` (:ref:`details `). - -Use of ``std::enable_shared_from_this`` is permitted, but since -nanobind does not use holder types, an object -constructed in Python will typically not have any associated -``std::shared_ptr`` until it is passed to a C++ function that -accepts ``std::shared_ptr``. That means a C++ function that accepts -a raw ``T*`` and calls ``shared_from_this()`` on it might stop working -when ported from pybind11 to nanobind. You can solve this problem -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. - -Custom constructors -------------------- -In pybind11, custom constructors (i.e. ones that do not already exist in the -C++ class) could be specified as a lambda function returning an instance of -the desired type. - -.. code-block:: cpp - - py::class_(m, "MyType") - .def(py::init([](int) { return MyType(...); })); - -Unfortunately, the implementation of this feature was quite complex and -often required further internal calls to the move or copy -constructor. nanobind instead reverts to how pybind11 originally -implemented this feature using in-place construction (`placement -new `_): - -.. code-block:: cpp - - nb::class_(m, "MyType") - .def("__init__", [](MyType *t) { new (t) MyType(...); }); - -The provided lambda function will be called with a pointer to uninitialized -memory that has already been allocated (this memory region is co-located -with the Python object for reasons of efficiency). The lambda function can -then either run an in-place constructor and return normally (in which case -the instance is assumed to be correctly constructed) or fail by raising an -exception. - -To turn an existing factory function into a constructor, you will need to -combine the above pattern with an invocation of the move/copy-constructor, -e.g.: - -.. code-block:: cpp - - nb::class_(m, "MyType") - .def("__init__", [](MyType *t) { new (t) MyType(MyType::create()); }); - -Implicit conversions --------------------- - -In pybind11, implicit conversions were specified using a follow-up function -call. This also works in nanobind, but it is recommended that you already -specify them within the constructor declaration: - -*pybind11*: - -.. code-block:: cpp - - py::class_(m, "MyType") - .def(py::init()); - - py::implicitly_convertible(); - -*nanobind*: - -.. code-block:: cpp - - nb::class_(m, "MyType") - .def(nb::init_implicit()); - - -Trampoline classes ------------------- -Trampolines, i.e., polymorphic class implementations that forward virtual -function calls to Python, now require an extra :c:macro:`NB_TRAMPOLINE(parent, -size) ` declaration, where ``parent`` refers to the parent class -and ``size`` is at least as big as the number of :c:macro:`NB_OVERRIDE_*() ` -calls. nanobind caches information to enable efficient function dispatch, for -which it must know the number of trampoline "slots". - -The macro ``PYBIND11_OVERRIDE_*(..)`` required the base type and return value -as the first two arguments. This information is no longer needed in nanobind, -and the arguments should be removed in :c:macro:`NB_OVERRIDE_*() -`: - -An example: - -.. code-block:: cpp - - struct PyAnimal : Animal { - NB_TRAMPOLINE(Animal, 1); - - std::string name() const override { - NB_OVERRIDE(name); - } - }; - -Trampoline declarations with an insufficient size may eventually trigger a -Python ``RuntimeError`` exception with a descriptive label, e.g.: - -.. code-block:: text - - nanobind::detail::get_trampoline('PyAnimal::what()'): the trampoline ran out of - slots (you will need to increase the value provided to the NB_TRAMPOLINE() macro) - -Iterator bindings ------------------ - -Use of the :cpp:func:`nb::make_iterator() `, -:cpp:func:`nb::make_key_iterator() `, and -:cpp:func:`nb::make_value_iterator() ` functions requires -including the additional header file ``nanobind/make_iterator.h``. The -interface of these functions has also slightly changed: all take a Python scope -and a name as first and second arguments, which are used to permanently -"install" the iterator type (which is created on demand). See the `test suite -`_ -for a worked out example. - -Type casters ------------- - -The API of custom type casters has changed *significantly*. The following -changes are needed: - -- ``load()`` was renamed to ``from_python()``. The function now takes an extra - ``uint8_t flags`` parameter (instead ``bool convert``, which is now - represented by the flag ``nb::detail::cast_flags::convert``). A - ``cleanup_list *`` pointer keeps track of Python temporaries that are created - by the conversion, and which need to be deallocated after a function call has - taken place. - - ``flags`` and ``cleanup`` should be passed to any recursive usage of - ``type_caster::from_python()``. If casting fails due to a Python exception, - the function should clear it (``PyErr_Clear()``) and return ``false``. If a - severe error condition arises that should be reported, use Python warning API - calls for this, e.g. ``PyErr_WarnFormat()``. - -- ``cast()`` was renamed to ``from_cpp()``. The function takes a return value - policy (as before) and a ``cleanup_list *`` pointer. If casting fails due to - a Python exception, the function should *leave the error set* (note the - asymmetry compared to ``from_python()``) and return ``nullptr``. - -Note that the cleanup list is only available when ``from_python()`` or -``from_cpp()`` are called as part of function dispatch, while usage by -:cpp:func:`nb::cast() ` may set ``cleanup`` to ``nullptr`` if implicit -conversions are not enabled. This case should be handled gracefully by refusing -the conversion if the cleanup list is absolutely required. - -Type casters may not raise C++ exceptions. Both ``from_python()`` and -``from_cpp()`` must be annotated with ``noexcept``. Exceptions or failure -conditions caused by a conversion should instead be caught *within* the -function body and handled as follows: - -- ``from_python()``: return ``false``. That's it. (Failed Python to C++ - conversion occur all the time while dispatching calls, causing nanobind - to simply move to the next function overload. Dedicated error reporting would - add undesirable overheads). In case of a severe internal error, use the - CPython warning API (e.g., ``PyErr_Warn()``) to notify the user. - -- ``from_cpp()``: a failure here is more serious, since a return value of a - successfully evaluated cannot be converted, causing the call to fail. To - provide further detail, use the CPython error API (e.g., ``PyErr_Format()``) - and return an invalid handle (``return nb::handle();``). - -The ``std::pair`` type caster (`link -`_) -may be useful as a starting point of custom implementations. - -.. _removed: - -Removed features ----------------- - -A number of pybind11 features are unavailable in nanobind. The list below -uses the following symbols: - -- ○: This removal is a design choice. Use pybind11 if you need this feature. -- ●: Unclear, to be discussed. - -Removed features include: - -- ○ **Multiple inheritance**: this feature was a persistent source of - complexity in pybind11 and it is one of the main casualties in creating - nanobind. -- ○ **Holders**: nanobind instances co-locate instance data with a Python - object instead of accessing it via a holder type. This is a major difference - compared to pybind11 and will require changes to binding code that used - custom holders (e.g. unique or shared pointers). The :ref:`intermediate - ` and :ref:`advanced ` sections on object ownership - provide further detail. -- ○ **Multi-intepreter, Embedding**: The ability to embed Python in an - executable or run several independent Python interpreters in the same process - is unsupported. Nanobind caters to bindings only. Multi-interpreter support - would require TLS lookups for nanobind data structures, which is undesirable. -- ○ **Function binding annotations**: The ``pos_only`` argument - annotation was removed. However, the same behavior can be achieved by - creating unnamed arguments; see the discussion in the section on - :ref:`keyword-only arguments `. -- ○ **Metaclasses**: creating types with custom metaclasses is unsupported. -- ○ **Module-local bindings**: support was removed (both for types and exceptions). -- ○ **Custom allocation**: C++ classes with an overloaded or deleted ``operator - new`` / ``operator delete`` are not supported. -- ○ **Compilation**: workarounds for buggy or non-standard-compliant - compilers were removed and will not be reintroduced. -- ○ The ``options`` class for customizing docstring generation was removed. -- ○ The NumPy array class (``py::array``) was removed in exchange for a more - powerful alternative (:cpp:class:`nb::ndarray\<..\> `) - that additionally supports CPU/GPU tensors produced by various frameworks - (NumPy, PyTorch, TensorFlow, JAX, etc.). Its API is not compatible with - pybind11, however. -- ● Buffer protocol binding (``.def_buffer()``) was removed in favor of - :cpp:class:`nb::ndarray\<..\> `. -- ● Support for evaluating Python files was removed. - -Bullet points marked with ● may be reintroduced eventually, but this will -need to be done in a careful opt-in manner that does not affect code -complexity, binary size, and compilation/runtime performance of basic bindings -that don't depend on these features. diff --git a/src/nanobind/docs/pypy.rst b/src/nanobind/docs/pypy.rst deleted file mode 100644 index 15a6190..0000000 --- a/src/nanobind/docs/pypy.rst +++ /dev/null @@ -1,23 +0,0 @@ -:orphan: - -.. cpp:namespace:: nanobind - -.. _pypy_issues: - -PyPy support ------------- - -PyPy 7.3.10 and newer versions are supported, though with some limitations: - -1. When nanobind types occur in reference cycles, then those cycles are not - collectable and will be leaked. This is a limitation of PyPy's ``cpyext`` - layer that was reported in `PyPy issue #3849 - `_. Note that this would - only affects users that use the :cpp:class:`nb::type_slots() ` - feature to implement a custom ``Py_tp_traverse`` or ``Py_tp_clear`` slot. - -2. nanobind normally complains about any reference leaks involving instances, - functions, and types when the interpreter shuts down. PyPy lacks the final - garbage collection step that is needed to identify such leaks See `PyPy - issue #3855 `_. Those - those checks therefore had to be disabled when compiling for PyPy. diff --git a/src/nanobind/docs/refleaks.rst b/src/nanobind/docs/refleaks.rst deleted file mode 100644 index 1624fa8..0000000 --- a/src/nanobind/docs/refleaks.rst +++ /dev/null @@ -1,485 +0,0 @@ -.. _refleaks: - -.. cpp:namespace:: nanobind - -Reference leaks -=============== - -When the Python interpreter shuts down, nanobind may generate warnings similar -to the following: - -.. code-block:: text - - nanobind: leaked 1 instances! - - leaked instance 0x102123728 of type "my_ext.MyClass" - nanobind: leaked 1 types! - - leaked type "my_ext.MyClass" - nanobind: leaked 1 functions! - - leaked function "__init__" - - nanobind: this is likely caused by a reference counting issue in the binding code. - See https://nanobind.readthedocs.io/en/latest/refleaks.html - -Reference leaks are the most frequently reported issue in this project—please -read this page carefully before opening a bug report. - -If you are a user of an extension experiencing this issue (e.g., ``my_ext`` in the -example above), **do not open a nanobind issue**. Instead, inform the extension -maintainers and direct them to this page. - -Why are these warnings generated? ---------------------------------- - -nanobind registers a callback that runs once Python has fully shut down. If any -nanobind-created instances, functions, or types still exist at this point, -something has gone wrong—they should have been automatically deleted by -Python's garbage collector. - -As Python objects can reference significant amounts of memory (e.g., -large CPU or GPU tensors), an inability to delete them is potentially very bad. - -Although leaks aren't always a serious problem, the decision was made to have -nanobind complain noisily about their presence to to encourage early detection -and resolution. Other binding tools do not report leaks, allowing them to -accumulate unnoticed until they cause serious problems. - -Disabling leak warnings ------------------------ - -Ignorance is bliss. If you prefer to simply not see these messages, you can -easily disable them by calling :cpp:func:`nb::set_leak_warnings() -` in your binding code: - -.. code-block:: cpp - - NB_MODULE(my_ext, m) { - nb::set_leak_warnings(false); - // ... - } - -Note that is a *global flag* shared by all nanobind extension libraries in the -same ABI domain. When changing global flags, please isolate your extension from -others by passing the ``NB_DOMAIN`` parameter to the -:cmake:command:`nanobind_add_module()` CMake command: - -.. code-block:: cmake - - nanobind_add_module( - my_module - NB_DOMAIN my_abi_domain - extension_file.cpp) - -Some projects choose to activate leak warnings for internal builds but disable -them for wheels uploaded to PyPI, as they can be confusing to end users. - -Reference counting fundamentals -------------------------------- - -Let's begin with some background material to understand the cause of these warnings. - -Each Python object tracks its *reference count*—the number of places it is -used. When this count reaches zero, the object is automatically deallocated. - -This mechanism is simple and efficient but it fails in the presence of -reference cycles, where objects indirectly references themselves (e.g., A → B → C → -A). In this case, the reference count never reaches zero, and reference -counting alone does not suffice to free the cycle (even if ``A``, ``B``, and -``C`` are not used anywhere else). - -In such cases, Python's cyclic *garbage collector* must step in. The garbage -collector periodically sweeps through all Python objects to find and break up -any cycles that are not referenced by other objects. To do its job, it must -know how objects are connected to each other. - -For pure Python code, this works seamlessly. Consider the following snippet: - -.. code-block:: python - - l = [] # 'l' ref. count = 1 - l.append(l) # 'l' ref. count = 2 - del l # 'l' ref. count = 1 - -Following the last line, the reference count of ``l`` remains ``1`` due to the -self-reference. Python's garbage collector will eventually visit the list and -its elements, identify the cycle, and delete it. - -Sources of reference leaks --------------------------- - -Under-defined types impede Python's ability to detect cycles, which can causes -leaks. However, user-defined types alone aren't enough---a specific mixture of -ingredients is needed to cause leaks. The following subsections review several -troublesome constructions. - -Class members -^^^^^^^^^^^^^ - -Consider this nanobind extension: - -.. code-block:: cpp - - #include - - namespace nb = nanobind; - - struct Wrapper { nb::object value; }; - - NB_MODULE(my_ext, m) { - nb::class_(m, "Wrapper") - .def(nb::init<>()) - .def_rw("value", &Wrapper::value); - } - -Now, run the following Python code. - -.. code-block:: pycon - - >>> import my_ext - >>> w = my_ext.Wrapper() - >>> w.value = w - -This triggers a leak warning: - -.. code-block:: text - - nanobind: leaked 1 instances! - - leaked instance 0x104d63728 of type "my_ext.Wrapper" - nanobind: leaked 1 types! - - leaked type "my_ext.Wrapper" - nanobind: leaked 3 functions! - - leaked function "" - - leaked function "" - - leaked function "__init__" - -This resembles the previous example with a self-referential list, -except that a user-defined ``Wrapper`` type is now used instead. - -The first message ("*leaked instance*") warns that a Python object of type -``Wrapper`` was not freed during the Python interpreter shutdown. This instance -in turn references other objects, which also become part of the leak: - -- ``w`` implicitly references the underlying type object ``my_ext.Wrapper``. - -- ``my_ext.Wrapper`` references several methods: ``__init__``, and anonymous - setter/getter functions. - -The root of the problem here is that Python lacks the ability to peek inside -the C++ ``Wrapper`` class to examine its connectivity. Therefore, it cannot -detect and free the cycle. - -The fact that we are storing a ``nb::object`` in the C++ instance is -irrelevant---the same issue would have occurred when using -``std::shared_ptr`` or an intrusively reference-counted object. - - -Function objects -^^^^^^^^^^^^^^^^ - -Functions are often a source of reference cycles. Let's reuse the earlier -example but instead assign a local function ``g`` to ``w.value``. - -.. code-block:: pycon - - >>> def f(): - ... w = my_ext.Wrapper() - ... def g(): - ... return w - ... w.value = g - ... - >>> f() - -This code behaves very badly: every call to ``f()`` will leak an uncollectable cycle. - -The local function ``g()`` is a `function closure -`_. That is to -say, besides being a function, it additionally captures variable state, in this -case the variable ``w``. This creates an inter-language ``Wrapper`` → -``function`` → ``Wrapper`` cycle. - -Here is another tricky case: let's move the code back to the top level and -create a dummy function that doesn't reference anything. - -.. code-block:: python - - >>> def f(): - ... pass - ... - >>> w = my_ext.Wrapper() - >>> w.value = f - -Given that the function is now empty, we may be tempted to assume that this -should fix the leak. However, this intuition is incorrect: - -.. code-block:: text - - nanobind: leaked 1 instances! - - leaked instance 0x104d63728 of type "my_ext.Wrapper" - nanobind: leaked 1 types! - - leaked type "my_ext.Wrapper" - nanobind: leaked 3 functions! - - leaked function "" - - leaked function "" - - leaked function "__init__" - -The reference cycle consists of: - -- ``w`` (``Wrapper`` instance) → ``f`` (Python function object). -- ``f`` (Python function object) → ``globals()``. -- ``globals()`` → ``w`` (``Wrapper`` instance). - -Functions *implicitly* depend on the global module namespace, which in turn -associates the name ``w`` with the instance. Reference leaks involving globals -can be especially noisy because they can pull in thousands of other objects -that dangle from the uncollectable cycle. - -Modifying ``Wrapper`` so that it uses an STL function object does not help. - -.. code-block:: cpp - - #include - - struct Wrapper { - std::function value; - }; - -This produces same cycle, just with more layers of indirection: - -- ``w`` → ``std::function`` instance -- ``std::function`` instance → nanobind function dispatch object -- nanobind function dispatch object → ``f``. -- ``f`` → ``globals()``. -- ``globals()`` → ``w``. - -It is easy to encounter such cycles when binding C++ classes with callbacks -that invoke Python functions. An example would be a button class in a GUI -framework that allows the user to assign a button press handler. - -Default arguments -^^^^^^^^^^^^^^^^^ - -Here is another subtle case, where the ``Wrapper`` constructor was modified -to set a default argument. - -.. code-block:: cpp - - struct Wrapper { nb::object value; }; - - NB_MODULE(my_ext, m) { - nb::class_(m, "Wrapper") - .def(nb::init() = Wrapper()); - } - -Now, we *don't even need to use* the ``Wrapper`` type. - -.. code-block:: python - - import my_ext - -Its mere presence produces a leak: - -.. code-block:: text - - nanobind: leaked 1 instances! - - leaked instance 0x1035fbb68 of type "my_ext.Wrapper" - nanobind: leaked 1 types! - - leaked type "my_ext.Wrapper" - nanobind: leaked 1 functions! - - leaked function "__init__" - -The reference cycle here is as follows: - -- ``my_ext.Wrapper`` type → ``my_ext.Wrapper.__init__`` function -- ``my_ext.Wrapper.__init__`` function → ``my_ext.Wrapper`` instance (the constructed default argument) -- ``my_ext.Wrapper`` instance → ``my_ext.Wrapper`` type (instances implictly reference their type) - -Default arguments in general are harmless. However, default arguments that -introduce cycles between instance and type objects can cause uncollectable cycles. - -.. _fixing_refleaks: - -Fixing reference leaks ----------------------- - -As the above examples hopefully demonstrate, this can be quite the -minefield---and these were "easy" cycles with only only a few hops. In -practice, leaks can be significantly more complex. - -For this reason, it is recommended that you *immediately* investigate and -squash leaks when they occur, especially while things are still under control -(i.e., when there is only a single source of leaks). Start by building your -extension in debug mode, in which case Dr.Jit will exhaustively print warnings -about all leaked instances/type. - -Look at the listed types and think about what objects they reference directly -or indirectly. C++ code that stores Python functions (i.e., callbacks) is -especially suspect, since functions can implicitly depend on globals and other -state through theyr closure object. Does a simple ``import`` statement suffice to -cause leaks? This might implicate default function arguments. - -Once you have identified a type binding as likely culprit, you must tell Python -how to traverse instances of this type to break cycles. nanobind provides no -abstractions for this at the moment. You must drop down to the CPython API -level and declare two callbacks (referred to as *type slots*): - -- ``tp_traverse``: Python's GC will call this function to discover references - of user-defined types. - -- ``tp_clear``: Python's GC will call this function to break collectable cycles. - -In particular, *all* types in the cycle must implement the ``tp_traverse`` -*type slot*, and *at least one* of them must implement the ``tp_clear`` type -slot. - -Here is an example of the required code for a ``Wrapper`` type: - -.. code-block:: cpp - - 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 - - // The tp_traverse method may be called after __new__ but before or during - // __init__, before the C++ constructor has been completed. We must not - // inspect the C++ state if the constructor has not yet completed. - if (!nb::inst_ready(self)) { - return 0; - } - - // Get the C++ object associated with 'self' (this always succeeds) - Wrapper *w = nb::inst_ptr(self); - - // If w->value has an associated Python object, return it. - // If not, value.ptr() will equal NULL, which is also fine. - nb::handle value = nb::find(w->value); - - // Inform the Python GC about the instance - Py_VISIT(value.ptr()); - - return 0; - } - - int wrapper_tp_clear(PyObject *self) { - // Get the C++ object associated with 'self' (this always succeeds) - Wrapper *w = nb::inst_ptr(self); - - // Break the reference cycle! - w->value = {}; - - return 0; - } - - // Table of custom type slots we want to install - PyType_Slot wrapper_slots[] = { - { Py_tp_traverse, (void *) wrapper_tp_traverse }, - { Py_tp_clear, (void *) wrapper_tp_clear }, - { 0, 0 } - }; - -The types ``visitproc``, ``PyType_Slot``, and macro ``Py_VISIT()`` are part of -the Python C API. - -The expression :cpp:func:`nb::inst_ptr\(self) ` efficiently -returns the C++ instance associated with a Python object and is explained in -the documentation about nanobind's :cpp:ref:`low level interface `. - -Note the use of the :cpp:func:`nb::find() ` function, which behaves like -:cpp:func:`nb::cast() ` by returning the Python object associated with a -C++ instance. The main difference is that :cpp:func:`nb::cast() ` will -create the Python object if it doesn't exist, while :cpp:func:`nb::find() -` returns a ``nullptr`` object in that case. When given a -``std::function<>`` instance, :cpp:func:`nb::find() ` retrieves the -associated Python ``function`` object (if present), which means that the -``wrapper_tp_traverse()`` works for all of the examples shown in this -documentation section. - -To activate this machinery, the ``Wrapper`` type bindings must be made aware of -these extra type slots via :cpp:class:`nb::type_slots `: - -.. code-block:: cpp - - nb::class_(m, "Wrapper", nb::type_slots(slots)) - -With this change, the cycle can be garbage-collected, and the leak warnings -disappear. - -.. note:: - - When targeting free-threaded Python, it is important that the ``tp_traverse`` - callback does not hold additional references to the objects being traversed. - - A previous version of this documentation page suggested the following - - .. code-block:: cpp - - nb::object value = nb::find(w->value); - Py_VISIT(value.ptr()); - - However, these now have to change to - - .. code-block:: cpp - - nb::handle value = nb::find(w->value); - Py_VISIT(value.ptr()); - - -Additional sources of leaks ---------------------------- - -In most of cases, leaks are caused by cycles, and the text above explains -how deal with them. For completeness, let's consider some other possibilities. - -- **Reference counting bugs**. If you write raw Python C API code or use the - nanobind wrappers including functions like ``Py_[X]INCREF()``, - ``Py_[X]DECREF()``, :cpp:func:`nb::steal() `, :cpp:func:`nb::borrow() - `, :cpp:func:`.dec_ref() `, - :cpp:func:`.inc_ref() ` - , etc., then incorrect - use of such calls can cause a reference to leak that prevents the associated - object from being deleted. - -- **Interactions with other tools that leak references**. Python extension - libraries---especially *huge* ones with C library components like PyTorch, - Tensorflow, etc., have been observed to leak references to nanobind - objects. - - Some of these frameworks cache JIT-compiled functions based on the arguments - with which they were called, and such caching schemes could leak references - to nanobind types if they aren't cleaned up by the responsible extensions - (this is a hypothesis). In this case, the leak would be benign---even so, it - 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 - this may be reported as a leak since Python finalization appears not to - release the exception object. See `issue #376 - `__ for a discussion. - diff --git a/src/nanobind/docs/release.rst b/src/nanobind/docs/release.rst deleted file mode 100644 index 9cf66f6..0000000 --- a/src/nanobind/docs/release.rst +++ /dev/null @@ -1,25 +0,0 @@ -How to make a new release? --------------------------- - -1. Ensure that the full version of nanobind is checked out (including the - ``robin_map`` submodule) - -2. Run ``python src/version.py -w X.Y.Z`` - -3. Add release date to ``docs/changelog.rst``. - -4. Update ``setup.py`` if new directories were added (see ``package_data``). - Update ``cmake/nanobind-config.cmake`` if new C++ source or header files - were added. - -5. Commit: ``git commit -am "vX.Y.Z release"`` - -6. Tag: ``git tag -a vX.Y.Z -m "vX.Y.Z release"`` - -7. Push: ``git push`` and ``git push --tags`` - -8. Run ``pipx run build`` - -9. Upload: ``twine upload --repository nanobind `` - -10. Run ``python src/version.py -w X.Y.Zdev1`` diff --git a/src/nanobind/docs/requirements.txt b/src/nanobind/docs/requirements.txt deleted file mode 100644 index 601bd4d..0000000 --- a/src/nanobind/docs/requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -furo -sphinx==6.1.3 -sphinx-copybutton==0.5.1 -sphinxcontrib-moderncmakedomain==3.25.0 -sphinxcontrib-svg2pdfconverter==1.2.2 diff --git a/src/nanobind/docs/typing.rst b/src/nanobind/docs/typing.rst deleted file mode 100644 index 391971e..0000000 --- a/src/nanobind/docs/typing.rst +++ /dev/null @@ -1,674 +0,0 @@ -.. cpp:namespace:: nanobind - -.. _typing: - -Typing -====== - -This section covers three broad typing-related topics: - -1. How to create rich type annotation in C++ bindings so that projects - using them can be effectively type-checked. - -2. How to :ref:`automatically generate stub files ` that are needed to - enable static type checking and autocompletion in Python IDEs. - -3. How to write :ref:`pattern files ` to handle advanced use - cases requiring significant stub customization. - -Signature customization ------------------------ - -In larger binding projects, some customization of function or class signatures -is often needed so that static type checkers can effectively use the generated -stubs. - -.. _typing_signature_functions: - -Functions -^^^^^^^^^ - -Nanobind generates typed function signatures automatically, but these are not -always satisfactory. For example, the following function binding - -.. code-block:: cpp - - nb::class_(m, "Int") - .def(nb::self == nb::self); - -is likely to be rejected because the default ``__eq__`` function signature - -.. code-block:: text - - __eq__(self, arg: Int, /) -> bool - -is more specific than that of the parent class ``object``: - -.. code-block:: text - - __eq__(self, arg: object, /) -> bool - -In this case, a static type checker like `MyPy -`__ will report a failure: - -.. code-block:: text - - error: Argument 1 of "__eq__" is incompatible with supertype "object"; supertype defines the argument type as "object" [override] - -To handle such cases, you can use the :cpp:class:`nb::sig ` -attribute to overrides the function signature with a custom string. - -.. code-block:: cpp - - nb::class_(m, "Int") - .def(nb::self == nb::self, - nb::sig("def __eq__(self, arg: object, /) -> bool")); - -The argument must be a valid Python function signature of the form ``def -name(...) -> ...`` without trailing colon (``":"``) and newlines, where -``name`` must furthermore match the name given to the binding declaration. In -this case, the name is implicitly given by the operator. It must match -``"name"`` in the case of ``.def("name", ...)``-style bindings with an explicit -name. The signature can span multiple lines, e.g., to prefix one or more -decorators. - -The modified signature is shown in generated stubs, docstrings, and error -messages (e.g., when a function receives incompatible arguments). - -In cases where a custom signature is only needed to tweak how nanobind renders -the signature of a default argument, the more targeted -:cpp:func:`nb::arg("name").sig("signature") ` annotation is -preferable to :cpp:class:`nb::sig `. - -.. _typing_signature_classes: - -Classes -^^^^^^^ - -Signature customization is also available for class bindings, though only -stubs are affected in this case. - -Consider the example below, which defines an iterable vector type storing -integers. Suppose that ``GeneralIterator`` iterates over arbitrary data and -does not provide a useful ``int``-typed signature. - -.. code-block:: cpp - - using IntVec = std::vector; - - nb::class_(m, "IntVec") - .def("__iter__", - [](const IntVec &v) -> GeneralIterator { ... }) - -It may be useful to inherit from ``collections.abc.Iterable[int]`` to -communicate more information to static type checkers, but such a Python → C++ -inheritance chain is not permitted by nanobind. - -.. _typing_liberties: - -Stubs often take certain liberties in deviating somewhat from the precise type -signature of the underlying implementation, which is fine as long as this -improves the capabilities of the type checker (the stubs are only used by the -static type checking phase, which never imports the actual extension). - -Here, we could specify - -.. code-block:: cpp - - nb::class_(m, "IntVec", - nb::sig("class IntVec(collections.abc.Iterable[int])")); - -This is technically a lie. Such shenanigans are worthwhile because they can -greatly improve the development experience (e.g. `VS Code -`__ autocomplete) involving compiled extensions. - -The supplied signature string must be a valid Python class signature of the -form ``class ClassName(...)`` excluding trailing colon (``":"``) and newline, -where ``ClassName`` must furthermore match the name provided in the main class -binding declaration. -The signature can span multiple lines, e.g., to prefix one or more decorators. - -Generic types -------------- - -.. _typing_generics_parameterizing: - -Parameterizing generic types -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Various standard Python types are `generic -`__ can be -parameterized to improve the effectiveness of static type checkers such as -`MyPy `__. In the presence of such a -specialization, a type checker can, e.g., infer that the variable ``a`` below -is of type ``int``. - -.. code-block:: python - - def f() -> list[int]: ... - - a = f()[0] - -This is even supported for *abstract types*---for example, -``collections.abc.Mapping[str, int]`` indicates an abstract mapping from -strings to integers. - -nanobind provides the template class :cpp:class:`nb::typed\ ` -to generate parameterized type annotations in C++ bindings. For example, the -argument and return value of the following function binding reproduces the -exact list and mapping types mentioned above. - -.. code-block:: cpp - - m.def("f", [](nb::typed arg) - -> nb::typed { ... }); - -(Usually, :cpp:class:`nb::typed\ ` would be applied to -:ref:`wrapper ` types, though this is not a strict limitation.) - -An important limitation of this feature is that it *only* affects function -signatures. Nanobind will (as always) ensure that ``f`` can only be called with -a ``nb::mapping``, but it will *not* insert additional runtime checks to verify that -``arg`` indeed maps strings to integers. It is the responsibility of the -function to perform these checks and, if needed, to raise a -:cpp:func:`nb::type_error `. - -The parameterized C++ type :cpp:class:`nb::typed\ ` -subclasses the type ``T`` and can be used interchangeably with ``T``. The other -arguments (``Ts...``) are used to generate a Python type signature but have no -other effect (for example, parameterizing by ``str`` on the Python end can -alternatively be achieved by passing ``nb::str``, ``std::string``, or ``const -char*`` as part of the ``Ts...`` parameter pack). - -There are two special forms of ``nb::typed`` that will be rendered -as something other than ``T[Ts...]``: - -* In some cases, a function may wish to accept or return an arbitrary - Python object, but generate signatures that describe it as some more - specific type ``T``. The types ``nb::typed`` and - ``nb::typed`` will be rendered as ``T`` rather than - as the nonsensical ``object[T]`` that they would be without this rule. - (If you want nanobind to check that an argument is actually of type ``T``, - while still giving you a generic Python object to work with, - then use :cpp:class:`nb::handle_t\ ` instead.) - -* Type parameters for ``nb::callable`` can be provided using a C++ function - signature, since there would otherwise be no way to express the nested - brackets used in Python callable signatures. In order to express the Python type - ``Callable[[str, float], int]``, which is a function taking two parameters - (string and float) and returning an integer, you might write - ``nb::typed``. For a callable type - that accepts any arguments, like ``Callable[..., int]``, use a C-style - variadic function signature: ``nb::typed``. - (The latter could also be written without this special support, as - ``nb::typed``.) - -.. _typing_generics_creating: - -Creating generic types -^^^^^^^^^^^^^^^^^^^^^^ - -Python types inheriting from `types.Generic -`__ can be -*parameterized* by other types including generic `type variables -`__ that act as -placeholders. Such constructions enable more effective static type checking. In -the snippet below, tools like `MyPy `__ or -`PyRight `__ can infer that ``x`` and -``y`` have types ``Wrapper[int]`` and ``int``, respectively. - -.. code-block:: python - - import typing - - # 1. Instantiate a placeholder type ("type variable") used below - T = typing.TypeVar("T") - - # 2. Create a generic type by inheriting from typing.Generic - class Wrapper(typing.Generic[T]): - # The constructor references the placeholder type - def __init__(self, value: T): - self.value = value - - # .. this type is then preserved in the getter - def get(self) -> T: - return self.value - - # Based on the typed constructor, MyPy knows that 'x' has type 'Wrapper[int]' - x = Wrapper(3) - - # Based on the typed 'Wrapped.get' method, 'y' is inferred to have type 'int' - y = x.get() - -Note that parameterization of a generic type doesn't generate new code or -modify its functionality. It is not to be confused with C++ template -instantiation. The feature only exists to propagate fine-grained type -information and thereby aid static type checking. - -Similar functionality can also be supported in nanobind-based binding projects. -This looks as follows: - -.. code-block:: cpp - - #include // needed by nb::type_var below - - struct Wrapper { - nb::object value; - }; - - NB_MODULE(my_ext, m) { - // 1. Instantiate a placeholder type ("type variable") used below - m.attr("T") = nb::type_var("T"); - - // 2. Create a generic type, and indicate in generated stubs - // that it derives from Generic[T] - nb::class_ wrapper(m, "Wrapper", nb::is_generic(), - nb::sig("class Wrapper(typing.Generic[T])")) - .def(nb::init(), - nb::sig("def __init__(self, arg: T, /) -> None")) - .def("get", [](Wrapper &w) { return w.value; }, - nb::sig("def get(self, /) -> T")); - } - -This involves the following steps: - -- The :cpp:func:`nb::type_var ` constructor generates a type variable - analogous to the previous Python snippet and assigns it to the name ``"T"`` - within the module. - -- If we were to follow the previous Python example, the next step would require - defining ``Wrapper`` as a subclass of ``typing.Generic[T]``. However, this - isn't possible because nanobind-based classes cannot derive from Python - types. - -- The solution to this problem takes the following :ref:`liberties - `: - - - It passes the :cpp:class:`nb::is_generic ` annotation to the - :cpp:class:`nb::class_\<...\> ` constructor, causing the addition - of a ``__class_getattr__`` member that enables type parameterization. - Following this step, an expression like ``Wrapper[int]`` becomes valid and - returns a ``typing.TypeAlias`` (in other words, the behavior is *as if* we - had derived from ``typing.Generic[T]``). - - However, `MyPy `__ and similar tools don't - quite know what to do with custom types overriding ``__class_getattr__`` - themselves, since the official parameterization mechanism is to subclass - ``typing.Generic``. - - - Therefore, we *lie* about this in the stub and declare - ``typing.Generic[T]`` as a base class. Only static type checkers will - see this information, and it helps them to interpret how the type works. - - - That's it! - -You may also extend parameterized forms of such generic types: - -.. code-block:: cpp - - nb::class_(m, "Subclass", wrapper[nb::type()]); - -nanobind's stub generator will render this as ``class Subclass(Wrapper[Foo]):``. - -Any-typed return values -^^^^^^^^^^^^^^^^^^^^^^^ - -The return value of a function can sometimes be unclear (dynamic), in which -case it can be helpful to declare ``typing.Any`` as a pragmatic return type -(this effectively disables analysis of the return value in static type -checkers). nanobind provides a :py:class:`nb::any ` wrapper type that is -equivalent to :py:class:`nb::object ` except that its type signature -renders as ``typing.Any`` to facilitate this. - -.. _stubs: - -Stub generation ---------------- - -A *stub file* provides a *typed* and potentially documented summary of a -module's class, function, and variable declarations. Stub files have the -extension ``.pyi`` and are often shipped along with Python extensions. They -are needed to enable autocompletion and static type checking in tools like -`Visual Studio Code `__, `MyPy -`__, `PyRight -`__ and `PyType -`__. - -Take for example the following function: - -.. code-block:: python - - def square(x: int) -> int: - '''Return the square of the input''' - return x*x - -The associated default stub removes the body, while retaining the docstring: - -.. code-block:: python - - def square(x: int) -> int: - '''Return the square of the input''' - -An undocumented stub replaces the entire body with the Python ellipsis object -(``...``). - -.. code-block:: python - - def square(x: int) -> int: ... - -Complex default arguments are often also abbreviated with ``...`` to improve -the readability of signatures. You can read more about stub files in the -`typing documentation -`__ and the `MyPy -documentation `__. - -nanobind's ``stubgen`` tool automates the process of stub generation to turn -modules containing a mixture of ordinary Python code and C++ bindings into an -associated ``.pyi`` file. - -The main challenge here is that C++ bindings are unlike ordinary Python -objects, which causes standard mechanisms to extract their signature to fail. -Existing tools like MyPy's `stubgen -`__ and `pybind11-stubgen -`__ must therefore parse -docstrings to infer function signatures, which is brittle and does not always -produce high-quality output. - -nanobind functions expose a ``__nb_signature__`` property, which provides -structured information about typed function signatures, overload chains, and -default arguments. nanobind's ``stubgen`` leverages this information to -reliably generate high-quality stubs that are usable by static type checkers. - -There are three ways to interface with the stub generator described in -the following subsections. - -CMake interface -^^^^^^^^^^^^^^^ - -nanobind's CMake interface provides the :cmake:command:`nanobind_add_stub` -command for stub generation at build or install time. It generates a single -stub at a time--more complex cases involving large numbers of stubs are easily -handled using standard CMake constructs (e.g. a ``foreach()`` loop). - -The command requires a target name (e.g., ``my_ext_stub``) that must be unique -but has no other significance. Once all dependencies (``DEPENDS`` parameter) -are met, it will invoke ``stubgen`` to turn a single module (``MODULE`` -parameter) into a stub file (``OUTPUT`` parameter). - -For this to work, the module must be importable. ``stubgen`` will add all paths -specified as part of the ``PYTHON_PATH`` parameter and then execute ``import -my_ext``, raising an error if this fails. - -.. code-block:: cmake - - nanobind_add_stub( - my_ext_stub - MODULE my_ext - OUTPUT my_ext.pyi - PYTHON_PATH $ - DEPENDS my_ext - ) - -Typed extensions normally identify themselves via the presence of an empty file -named ``py.typed`` in each module directory. :cmake:command:`nanobind_add_stub` -can optionally generate this file as well. - -.. code-block:: cmake - - nanobind_add_stub( - ... - MARKER_FILE py.typed - ... - ) - -CMake tracks the generated outputs in its dependency graph. The combination of -compiled extension module, stub, and marker file can subsequently be installed -by subsequent ``install()`` directives. - -.. code-block:: cmake - - install(TARGETS my_ext DESTINATION ".") - install(FILES py.typed my_ext.pyi DESTINATION ".") - -In certain situations, it may be tricky to import an extension that is built -but not yet installed to its final destination. To handle such cases, specify -the ``INSTALL_TIME`` parameter to :cmake:command:`nanobind_add_stub` to delay -stub generation to the installation phase. - -.. code-block:: cmake - - install(TARGETS my_ext DESTINATION ".") - - nanobind_add_stub( - my_ext_stub - INSTALL_TIME - MODULE my_ext - OUTPUT my_ext.pyi - PYTHON_PATH "." - ) - -This requires several changes: - -1. ``PYTHON_PATH`` must be adjusted so that it references a location relative - to ``CMAKE_INSTALL_PREFIX`` from which the installed module is importable. - -2. The :cmake:command:`nanobind_add_stub` command should be preceded by - ``install(TARGETS my_ext)`` and ``install(FILES`` commands that place all - data (compiled extension files, plain Python code, etc.) needed to bring the - module into an importable state. - - Place all relevant ``install()`` directives within the same - ``CMakeLists.txt`` file to ensure that these steps are executed - sequentially. - -3. Dependencies (``DEPENDS``) no longer need to be listed. These are build-time - constraints that do not apply in the installation phase. - -4. The output file path (``OUTPUT``) is relative to ``CMAKE_INSTALL_PREFIX`` - and may need adjustments as well. - -The :cmake:command:`nanobind_add_stub` command has a few other options, please -refer to its documentation for details. - -Command line interface -^^^^^^^^^^^^^^^^^^^^^^ - -Alternatively, you can invoke ``stubgen`` on the command line. The nanobind -package must be installed for this to work, e.g., via ``pip install nanobind``. -The command line interface is also able to generate multiple stubs at once -(simply specify ``-m MODULE`` several times). - -.. code-block:: bash - - $ python -m nanobind.stubgen -m my_ext -M py.typed - Module "my_ext" .. - - importing .. - - analyzing .. - - writing stub "my_ext.pyi" .. - - writing marker file "py.typed" .. - -Unless an output file (``-o``) or output directory (``-O``) is specified, this -places the ``.pyi`` files directly into the module. Existing stubs are -overwritten without warning. - -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] - - Generate stubs for nanobind-based extensions. - - options: - -h, --help show this help message and exit - -o FILE, --output-file FILE write generated stubs to the specified file - -O PATH, --output-dir PATH write generated stubs to the specified directory - -i PATH, --import PATH add the directory to the Python import path (can - specify multiple times) - -m MODULE, --module MODULE generate a stub for the specified module (can - specify multiple times) - -r, --recursive recursively process submodules - -M FILE, --marker-file FILE generate a marker file (usually named 'py.typed') - -p FILE, --pattern-file FILE apply the given patterns to the generated stub - (see the docs for syntax) - -P, --include-private include private members (with single leading or - trailing underscore) - -D, --exclude-docstrings exclude docstrings from the generated stub - -q, --quiet do not generate any output in the absence of failures - - -Python interface -^^^^^^^^^^^^^^^^ - -Finally, you can import ``stubgen`` into your own Python programs and use it to -programmatically generate stubs with a finer degree of control. - -To do so, construct an instance of the ``StubGen`` class and repeatedly call -``.put()`` to register modules or contents within the modules (specific -methods, classes, etc.). Afterwards, the ``.get()`` method returns a string -containing the stub declarations. - - -.. code-block:: python - - from nanobind.stubgen import StubGen - import my_module - - sg = StubGen() - sg.put(my_module) - print(sg.get()) - -Note that for now, the ``nanobind.stubgen.StubGen`` API is considered -experimental and not subject to the semantic versioning policy used by the -nanobind project. - -.. _pattern_files: - -Pattern files -------------- - -In complex binding projects requiring static type checking, the previously -discussed mechanisms for controlling typed signatures (:cpp:class:`nb::sig -`, :cpp:class:`nb::typed `) may be insufficient. Two common reasons -are as follows: - -- the ``@typing.overload`` chain associated with a function may sometimes - require significant deviations from the actual overloads present on the C++ - side. - -- Some members of a module could be inherited from existing Python packages or - extension libraries, in which case patching their signature via - :cpp:class:`nb::sig ` is not even an option. - -``stubgen`` supports *pattern files* as a last-resort solution to handle such -advanced needs. These are files written in a *domain-specific language* (DSL) -that specifies replacement patterns to dynamically rewrite stubs during -generation. To use one, simply add it to the :cmake:command:`nanobind_add_stub` -command. - -.. code-block:: cmake - - nanobind_add_stub( - ... - PATTERN_FILE - ... - ) - -A pattern file contains sequence of patterns. Each pattern consists of a query -and an indented replacement block to be applied when the query matches. - -.. code-block:: text - - # This is the first pattern - query 1: - replacement 1 - - # And this is the second one - query 2: - replacement 2 - -Empty lines and lines beginning with ``#`` are ignored. The amount of -indentation is arbitrary: ``stubgen`` will re-indent the replacement as needed -based on where the query matched. - -When the stub generator traverses the module, it computes the *fully qualified -name* of every type, function, property, etc. (for example: -``"my_ext.MyClass.my_function"``). The queries in a pattern file are checked -against these qualified names one by one until the first one matches. - -For example, suppose that we had the following lackluster stub entry: - -.. code-block:: python - - class MyClass: - def my_function(arg: object) -> object: ... - -The pattern below matches this function stub and inserts an alternative with -two typed overloads. - -.. code-block:: text - - my_ext.MyClass.my_function: - @overload - def my_function(arg: int) -> int: - """A helpful docstring""" - - @overload - def my_function(arg: str) -> str: ... - -Patterns can also *remove* entries, by simply not specifying a replacement -block. Also, queries don't have to match the entire qualified name. For -example, the following pattern deletes all occurrences of anything -containing the string ``secret`` somewhere in its name - -.. code-block:: text - - secret: - -In fact (you may have guessed it), the queries are *regular expressions*! The -query supports all features of Python's builtin `re -`__ library. - -When the query uses *groups*, the replacement block may access the contents of -each numbered group using using the syntax ``\1``, ``\2``, etc. This permits -writing generic patterns that can be applied to a number of stub entries at -once: - -.. code-block:: text - - __(eq|ne)__: - def __\1__(self, arg, /) -> bool: ... - -Named groups are also supported: - -.. code-block:: text - - __(?Peq|ne)__: - def __\op__(self, arg, /) -> bool : ... - -Finally, sometimes, it is desirable to rewrite only the signature of a function -in a stub but to keep its docstring so that it doesn't have to be copied into -the pattern file. The special escape code ``\doc`` references the previously -existing docstring. - -.. code-block:: text - - my_ext.lookup: - def lookup(array: Array[T], index: int) -> T: - \doc - -If your replacement rule requires additional types to work (e.g., from ``typing.*``), -you may use the special ``\from`` escape code to import them: - -.. code-block:: text - - @overload - my_ext.lookup: - \from typing import Optional as _Opt, Literal - 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__``. diff --git a/src/nanobind/docs/utilities.rst b/src/nanobind/docs/utilities.rst deleted file mode 100644 index eebf6d3..0000000 --- a/src/nanobind/docs/utilities.rst +++ /dev/null @@ -1,59 +0,0 @@ -.. cpp:namespace:: nanobind - -.. _utilities: - -Utilities -========== - -.. _utilities_eval: - -Evaluating Python expressions from strings ------------------------------------------- - -nanobind provides the :cpp:func:`eval` and :cpp:func:`exec` functions to -evaluate Python expressions and statements. The following example illustrates -how they can be used. - -.. code-block:: cpp - - // At beginning of file - #include - - ... - - // Evaluate in scope of main module - nb::object scope = nb::module_::import_("__main__").attr("__dict__"); - - // Evaluate an isolated expression - int result = nb::eval("my_variable + 10", scope).cast(); - - // Evaluate a sequence of statements - nb::exec( - "print('Hello')\n" - "print('world!');", - scope); - -C++11 raw string literals are also supported and quite handy for this purpose. -The only requirement is that the first statement must be on a new line -following the raw string delimiter ``R"(``, ensuring all lines have common -leading indent: - -.. code-block:: cpp - - nb::exec(R"( - x = get_answer() - if x == 42: - print('Hello World!') - else: - print('Bye!') - )", scope - ); - -.. note:: - - :cpp:func:`eval` accepts a template parameter that describes how the - string/file should be interpreted. Possible choices include ``eval_expr`` - (isolated expression), ``eval_single_statement`` (a single statement, - return value is always ``none``), and ``eval_statements`` (sequence of - statements, return value is always ``none``). `eval` defaults to - ``eval_expr`` and `exec` is just a shortcut for ``eval``. diff --git a/src/nanobind/docs/why.rst b/src/nanobind/docs/why.rst deleted file mode 100644 index 5f58e2c..0000000 --- a/src/nanobind/docs/why.rst +++ /dev/null @@ -1,252 +0,0 @@ -.. _why: - -.. cpp:namespace:: nanobind - -Why another binding library? -============================ - -I started the `pybind11 `__ project back in -2015 to generate better C++/Python bindings for a project I had been working -on. Thanks to many amazing contributions by others, pybind11 has since become a -core dependency of software used across the world including flagship projects -like `PyTorch `__ and `Tensorflow -`__. Every day, it is downloaded over 400'000 times. -Hundreds of contributed extensions and generalizations address use cases of -this diverse audience. However, all of this success also came with costs: the -complexity of the library grew tremendously, which had a negative impact on -efficiency. - -Curiously, the situation now is reminiscent of 2015: binding generation with -existing tools (`Boost.Python `__, `pybind11 -`__) is slow and produces enormous binaries -with overheads on runtime performance. At the same time, key improvements in -C++17 and Python 3.8 provide opportunities for drastic simplifications. -Therefore, I am starting *another* binding project. This time, the scope is -intentionally limited so that this doesn't turn into an endless cycle. - -So what is different? ---------------------- - -nanobind is highly related to pybind11 and inherits most of its conventions -and syntax. The main difference is a change in philosophy: pybind11 must -deal with *all of C++* to bind legacy codebases, while nanobind targets -a smaller C++ subset. *The codebase has to adapt to the binding tool and not -the other way around*, which allows nanobind to be simpler and faster. Pull -requests with extensions and generalizations to handle subtle fringe cases were -welcomed in pybind11, but they will likely be rejected in this project. - -An overview of removed features is provided in a :ref:`separate section -`. Besides feature removal, the rewrite was also an opportunity to -address :ref:`long-standing performance issues ` and add a -number of :ref:`major quality-of-life improvements ` and -:ref:`smaller features `. - -.. _perf_improvements: - -Performance improvements ------------------------- - -The :ref:`benchmark section ` evaluates the impact of the following -performance improvements: - -- **Compact objects**: C++ objects are now co-located with the Python object - whenever possible (less pointer chasing compared to pybind11). The - per-instance overhead for wrapping a C++ type into a Python object shrinks by - a factor of 2.3x. (pybind11: 56 bytes, nanobind: 24 bytes.) - -- **Compact functions**: C++ function binding information is now co-located - with the Python function object (less pointer chasing). - -- **Compact types**: C++ type binding information is now co-located with the Python type object - (less pointer chasing, fewer hashtable lookups). - -- **Fast hash table**: nanobind upgrades several important internal - associative data structures that previously used ``std::unordered_map`` to a - more efficient alternative (`tsl::robin_map - `__, which is included as a git - submodule). - -- **Vector calls**: function calls from/to Python are realized using `PEP 590 - vector calls `__, which gives a nice - speed boost. The main function dispatch loop no longer allocates heap memory. - -- **Library component**: pybind11 was designed as a header-only library, which - is generally a good thing because it simplifies the compilation workflow. - However, one major downside of this is that a large amount of redundant code - has to be compiled in each binding file (e.g., the function dispatch loop and - all of the related internal data structures). nanobind compiles a separate - shared or static support library ("*libnanobind*") and links it against the - binding code to avoid redundant compilation. The CMake interface - :cmake:command:`nanobind_add_module()` fully automates these extra - steps. - -- **Smaller headers**: ``#include `` pulls in a large - portion of the STL (about 2.1 MiB of headers with Clang and libc++). nanobind - minimizes STL usage to avoid this problem. Type casters even for for basic - types like ``std::string`` require an explicit opt-in by including an extra - header file (e.g. ``#include ``). - -- **Simpler compilation**: pybind11 was dependent on *link time optimization* - (LTO) to produce reasonably-sized bindings, which makes linking a build time - bottleneck. With nanobind's split into a precompiled library and minimal - metatemplating, LTO is no longer crucial and can be skipped. - -- **Free-threading**: Python 3.13+ supports a free-threaded mode that removes - the *Global Interpreter Lock* (GIL). Both pybind11 and nanobind support - free-threading as of recently. When comparing the two, nanobind provides - better multi-core scaling using a localized locking scheme. In pybind11, lock - contention on a central ``internals`` data structure used in every binding - operation becomes a bottleneck in practice. - -- **Lifetime management**: nanobind maintains efficient internal data - structures for lifetime management (needed for :cpp:class:`nb::keep_alive - `, :cpp:enumerator:`nb::rv_policy::reference_internal - `, the ``std::shared_ptr`` interface, etc.). - With these changes, bound types no longer need to be weak-referenceable, - which saves a pointer per instance. - -.. _major_additions: - -Major additions ---------------- - -nanobind includes a number of quality-of-life improvements for developers: - -- **N-dimensional arrays**: nanobind can exchange data with modern array programming - frameworks. It uses either `DLPack `__ or the - `buffer protocol `__ to achieve - *zero-copy* CPU/GPU array exchange with frameworks like `NumPy - `__, `PyTorch `__, `TensorFlow - `__, `JAX `__, etc. See - the :ref:`section on n-dimensional arrays ` for details. - -- **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. - -- **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 - `__ make compiled - extension code compatible with visual autocomplete in editors like `Visual - Studio Code `__ and static type checkers like - `MyPy `__, `PyRight - `__ and `PyType - `__. - -- **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. - -- **Leak warnings**: When the Python interpreter shuts down, nanobind reports - instance, type, and function leaks related to bindings, which is useful for - tracking down reference counting issues. If these warnings are undesired, - call :cpp:func:`nb::set_leak_warnings(false) `. nanobind - also fully deletes its internal data structures when the Python interpreter - terminates, which avoids memory leak reports in tools like *valgrind*. - -- **Better docstrings**: pybind11 pre-renders docstrings while the binding code - runs. In other words, every call to ``.def(...)`` to bind a function - immediately creates the underlying docstring. When a function takes a C++ - type as parameter that is not yet registered in pybind11, the docstring will - include a C++ type name (e.g. ``std::vector>``), - which can look rather ugly. pybind11 binding declarations must be carefully - arranged to work around this issue. - - nanobind avoids the issue altogether by not pre-rendering docstrings: they - are created on the fly when queried. nanobind also has improved - out-of-the-box compatibility with documentation generation tools like `Sphinx - `__. - -- **Low-level API**: nanobind exposes an optional low-level API to provide - fine-grained control over diverse aspects including :ref:`instance creation - `, :ref:`type creation `, and it can store - :ref:`supplemental data ` in types. The low-level API provides a - useful escape hatch to pursue advanced projects that were not foreseen in - the design of this library. - -.. _minor_additions: - -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 - `PyRight `__. In such cases, specify - the :cpp:class:`nb::sig ` attribute to override the default - nanobind-provided signature. - - For example, the following function signature annotation creates an overload - that should only be called with an ``1``-valued integer literal. While the - function also includes a runtime check, a static type checker can now ensure - that this error condition cannot possibly be triggered by a given piece of code. - - .. code-block:: cpp - - m.def("f", - [](int arg) { - if (arg != 1) - nb::raise("invalid input"); - return arg; - }, - nb::sig("def f(arg: typing.Literal[1], /) -> int")); - - Please see the section on :ref:`customizing function signatures - ` and :ref:`class signatures - ` for further details. - -TLDR ----- - -My recommendation is that current pybind11 users look into migrating to -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. diff --git a/src/nanobind/ext/robin_map/.clang-format b/src/nanobind/ext/robin_map/.clang-format deleted file mode 100644 index f6cb8ad..0000000 --- a/src/nanobind/ext/robin_map/.clang-format +++ /dev/null @@ -1 +0,0 @@ -BasedOnStyle: Google diff --git a/src/nanobind/ext/robin_map/.codecov.yml b/src/nanobind/ext/robin_map/.codecov.yml deleted file mode 100644 index 92a0a4a..0000000 --- a/src/nanobind/ext/robin_map/.codecov.yml +++ /dev/null @@ -1,5 +0,0 @@ -comment: off -coverage: - status: - project: off - patch: off diff --git a/src/nanobind/ext/robin_map/.github/workflows/ci.yml b/src/nanobind/ext/robin_map/.github/workflows/ci.yml deleted file mode 100644 index 96b698a..0000000 --- a/src/nanobind/ext/robin_map/.github/workflows/ci.yml +++ /dev/null @@ -1,136 +0,0 @@ -name: CI - -on: [push, pull_request, release] - -jobs: - build: - strategy: - fail-fast: false - matrix: - config: - - { - name: linux-x64-gcc, - os: ubuntu-latest, - cxx: g++, - cmake-build-type: Release - } - - { - name: linux-x64-gcc-no-exceptions, - os: ubuntu-latest, - cxx: g++, - cxx-flags: -fno-exceptions, - cmake-build-type: Release - } - - { - name: linux-x64-clang, - os: ubuntu-latest, - cxx: clang++, - cmake-build-type: Release - } - - { - name: macos-x64-gcc, - os: macos-13, - cxx: g++, - cmake-build-type: Release - } - - { - name: macos-x64-clang, - os: macos-13, - cxx: clang++, - cmake-build-type: Release - } - - { - name: linux-x64-clang-sanitize, - os: ubuntu-latest, - cxx: clang++, - cxx-flags: "-fsanitize=address,undefined", - cmake-build-type: Release - } - - { - name: linux-x64-gcc-coverage, - os: ubuntu-latest, - cxx: g++, - cxx-flags: --coverage, - gcov-tool: gcov, - 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, - 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, - cmake-build-type: Release, - cmake-generator: Visual Studio 17 2022, - cmake-platform: Win32, - vcpkg-triplet: x86-windows-static-md - } - name: ${{matrix.config.name}} - runs-on: ${{matrix.config.os}} - steps: - - uses: actions/checkout@v2 - - # Windows - - name: Install boost (Windows) - run: vcpkg install boost-test:${{matrix.config.vcpkg-triplet}} - if: runner.os == 'Windows' - - - name: Configure CMake (Windows) - run: cmake -G "${{matrix.config.cmake-generator}}" -A ${{matrix.config.cmake-platform}} -DCMAKE_BUILD_TYPE=${{matrix.config.cmake-build-type}} -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake" -DVCPKG_TARGET_TRIPLET=${{matrix.config.vcpkg-triplet}} -S ${{github.workspace}}/tests -B ${{github.workspace}}/build - if: runner.os == 'Windows' - - - name: Build (Windows) - run: cmake --build ${{github.workspace}}/build --config ${{matrix.config.cmake-build-type}} --verbose - if: runner.os == 'Windows' - - - name: Test (Windows) - run: ${{github.workspace}}/build/${{matrix.config.cmake-build-type}}/tsl_robin_map_tests.exe - if: runner.os == 'Windows' - - # Linux or macOS - - name: Install boost (Linux or macOS) - run: vcpkg install boost-test - if: runner.os == 'Linux' || runner.os == 'macOS' - - - name: Configure CMake (Linux or macOS) - run: cmake -DCMAKE_BUILD_TYPE=${{matrix.config.cmake-build-type}} -DCMAKE_TOOLCHAIN_FILE="$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake" -S ${{github.workspace}}/tests -B ${{github.workspace}}/build - env: - CXX: ${{matrix.config.cxx}} - CXXFLAGS: ${{matrix.config.cxx-flags}} - if: runner.os == 'Linux' || runner.os == 'macOS' - - - name: Build (Linux or macOS) - run: cmake --build ${{github.workspace}}/build --verbose - if: runner.os == 'Linux' || runner.os == 'macOS' - - - name: Test (Linux or macOS) - run: ${{github.workspace}}/build/tsl_robin_map_tests - if: runner.os == 'Linux' || runner.os == 'macOS' - - - name: Coverage - run: | - sudo apt-get install -y lcov - lcov -c -b ${{github.workspace}}/include -d ${{github.workspace}}/build -o ${{github.workspace}}/coverage.info --no-external --gcov-tool ${{matrix.config.gcov-tool}} - bash <(curl -s https://codecov.io/bash) -f ${{github.workspace}}/coverage.info - if: ${{matrix.config.name == 'linux-x64-gcc-coverage'}} diff --git a/src/nanobind/ext/robin_map/CMakeLists.txt b/src/nanobind/ext/robin_map/CMakeLists.txt deleted file mode 100644 index be1a3ff..0000000 --- a/src/nanobind/ext/robin_map/CMakeLists.txt +++ /dev/null @@ -1,87 +0,0 @@ -cmake_minimum_required(VERSION 3.5) - -project(tsl-robin-map VERSION 1.4.0 LANGUAGES CXX) - -include(GNUInstallDirs) - - -set(IS_MAIN_PROJECT FALSE) -if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) - set(IS_MAIN_PROJECT TRUE) -endif() - -# Enable the install target if the project is used main project (disable by default if subproject) -option(TSL_ROBIN_MAP_ENABLE_INSTALL "Enable install target" ${IS_MAIN_PROJECT}) - - -add_library(robin_map INTERFACE) -# Use tsl::robin_map as target, more consistent with other libraries conventions (Boost, Qt, ...) -add_library(tsl::robin_map ALIAS robin_map) - -target_include_directories(robin_map INTERFACE - "$" - "$") - -list(APPEND headers "${CMAKE_CURRENT_SOURCE_DIR}/include/tsl/robin_growth_policy.h" - "${CMAKE_CURRENT_SOURCE_DIR}/include/tsl/robin_hash.h" - "${CMAKE_CURRENT_SOURCE_DIR}/include/tsl/robin_map.h" - "${CMAKE_CURRENT_SOURCE_DIR}/include/tsl/robin_set.h") -target_sources(robin_map INTERFACE "$") - -if(MSVC) - target_sources(robin_map INTERFACE - "$" - "$") -endif() - -# Installation -if(TSL_ROBIN_MAP_ENABLE_INSTALL) - include(CMakePackageConfigHelpers) - - ## Install include directory and potential natvis file - install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/tsl" - DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") - - if(MSVC) - install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/tsl-robin-map.natvis" - DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}") - endif() - - - - ## Create and install tsl-robin-mapConfig.cmake - configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/tsl-robin-mapConfig.cmake.in" - "${CMAKE_CURRENT_BINARY_DIR}/tsl-robin-mapConfig.cmake" - INSTALL_DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/cmake/tsl-robin-map") - - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/tsl-robin-mapConfig.cmake" - DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/cmake/tsl-robin-map") - - - ## Create local tsl-robin-mapTargets.cmake - export(TARGETS robin_map NAMESPACE tsl:: FILE "${CMAKE_CURRENT_BINARY_DIR}/tsl-robin-mapTargets.cmake") - - ## Create and install global tsl-robin-mapTargets.cmake - install(TARGETS robin_map - EXPORT tsl-robin-mapTargets) - - install(EXPORT tsl-robin-mapTargets - NAMESPACE tsl:: - DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/cmake/tsl-robin-map") - - - - ## Create and install tsl-robin-mapConfigVersion.cmake - # tsl-robin-map is header-only and does not depend on the architecture. - # Remove CMAKE_SIZEOF_VOID_P from tsl-robin-mapConfigVersion.cmake so that a - # tsl-robin-mapConfig.cmake generated for a 64 bit target can be used for 32 bit - # targets and vice versa. - set(CMAKE_SIZEOF_VOID_P_BACKUP ${CMAKE_SIZEOF_VOID_P}) - unset(CMAKE_SIZEOF_VOID_P) - write_basic_package_version_file("${CMAKE_CURRENT_BINARY_DIR}/tsl-robin-mapConfigVersion.cmake" - COMPATIBILITY SameMajorVersion) - set(CMAKE_SIZEOF_VOID_P ${CMAKE_SIZEOF_VOID_P_BACKUP}) - - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/tsl-robin-mapConfigVersion.cmake" - DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/cmake/tsl-robin-map") -endif() diff --git a/src/nanobind/ext/robin_map/LICENSE b/src/nanobind/ext/robin_map/LICENSE deleted file mode 100644 index e9c5ae9..0000000 --- a/src/nanobind/ext/robin_map/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2017 Thibaut Goetghebuer-Planchon - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/src/nanobind/ext/robin_map/README.md b/src/nanobind/ext/robin_map/README.md deleted file mode 100644 index 09fc544..0000000 --- a/src/nanobind/ext/robin_map/README.md +++ /dev/null @@ -1,521 +0,0 @@ -[![CI](https://github.com/Tessil/robin-map/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/Tessil/robin-map/actions/workflows/ci.yml) - -## A C++ implementation of a fast hash map and hash set using robin hood hashing - -The robin-map library is a C++ implementation of a fast hash map and hash set using open-addressing and linear robin hood hashing with backward shift deletion to resolve collisions. - -Four classes are provided: `tsl::robin_map`, `tsl::robin_set`, `tsl::robin_pg_map` and `tsl::robin_pg_set`. The first two are faster and use a power of two growth policy, the last two use a prime growth policy instead and are able to cope better with a poor hash function. Use the prime version if there is a chance of repeating patterns in the lower bits of your hash (e.g. you are storing pointers with an identity hash function). See [GrowthPolicy](#growth-policy) for details. - -A **benchmark** of `tsl::robin_map` against other hash maps may be found [here](https://tessil.github.io/2016/08/29/benchmark-hopscotch-map.html). This page also gives some advices on which hash table structure you should try for your use case (useful if you are a bit lost with the multiple hash tables implementations in the `tsl` namespace). - -### Key features - -- Header-only library, just add the [include](include/) directory to your include path and you are ready to go. If you use CMake, you can also use the `tsl::robin_map` exported target from the [CMakeLists.txt](CMakeLists.txt). -- Fast hash table, check the [benchmark](https://tessil.github.io/2016/08/29/benchmark-hopscotch-map.html) for some numbers. -- Support for move-only and non-default constructible key/value. -- Support for heterogeneous lookups allowing the usage of `find` with a type different than `Key` (e.g. if you have a map that uses `std::unique_ptr` as key, you can use a `foo*` or a `std::uintptr_t` as key parameter to `find` without constructing a `std::unique_ptr`, see [example](#heterogeneous-lookups)). -- No need to reserve any sentinel value from the keys. -- Possibility to store the hash value alongside the stored key-value for faster rehash and lookup if the hash or the key equal functions are expensive to compute. Note that hash may be stored even if not asked explicitly when the library can detect that it will have no impact on the size of the structure in memory due to alignment. See the [StoreHash](https://tessil.github.io/robin-map/classtsl_1_1robin__map.html#details) template parameter for details. -- If the hash is known before a lookup, it is possible to pass it as parameter to speed-up the lookup (see `precalculated_hash` parameter in [API](https://tessil.github.io/robin-map/classtsl_1_1robin__map.html#a35021b11aabb61820236692a54b3a0f8)). -- Support for efficient serialization and deserialization (see [example](#serialization) and the `serialize/deserialize` methods in the [API](https://tessil.github.io/robin-map/classtsl_1_1robin__map.html) for details). -- The library can be used with exceptions disabled (through `-fno-exceptions` option on Clang and GCC, without an `/EH` option on MSVC or simply by defining `TSL_NO_EXCEPTIONS`). `std::terminate` is used in replacement of the `throw` instruction when exceptions are disabled. -- API closely similar to `std::unordered_map` and `std::unordered_set`. - -### Differences compared to `std::unordered_map` - -`tsl::robin_map` tries to have an interface similar to `std::unordered_map`, but some differences exist. -- The **strong exception guarantee only holds** if the following statement is true `std::is_nothrow_swappable::value && std::is_nothrow_move_constructible::value` (where `value_type` is `Key` for `tsl::robin_set` and `std::pair` for `tsl::robin_map`). Otherwise if an exception is thrown during the swap or the move, the structure may end up in a undefined state. Note that per the standard, a `value_type` with a noexcept copy constructor and no move constructor also satisfies this condition and will thus guarantee the strong exception guarantee for the structure (see [API](https://tessil.github.io/robin-map/classtsl_1_1robin__map.html#details) for details). -- The type `Key`, and also `T` in case of map, must be swappable. They must also be copy and/or move constructible. -- Iterator invalidation doesn't behave in the same way, any operation modifying the hash table invalidate them (see [API](https://tessil.github.io/robin-map/classtsl_1_1robin__map.html#details) for details). -- References and pointers to keys or values in the map are invalidated in the same way as iterators to these keys-values. -- For iterators of `tsl::robin_map`, `operator*()` and `operator->()` return a reference and a pointer to `const std::pair` instead of `std::pair` making the value `T` not modifiable. To modify the value you have to call the `value()` method of the iterator to get a mutable reference. Example: -```c++ -tsl::robin_map map = {{1, 1}, {2, 1}, {3, 1}}; -for(auto it = map.begin(); it != map.end(); ++it) { - //it->second = 2; // Illegal - it.value() = 2; // Ok -} -``` -- No support for some buckets related methods (like `bucket_size`, `bucket`, ...). - -These differences also apply between `std::unordered_set` and `tsl::robin_set`. - -Thread-safety guarantees are the same as `std::unordered_map/set` (i.e. possible to have multiple readers with no writer). - -### Growth policy - -The library supports multiple growth policies through the `GrowthPolicy` template parameter. Three policies are provided by the library but you can easily implement your own if needed. - -* **[tsl::rh::power_of_two_growth_policy.](https://tessil.github.io/robin-map/classtsl_1_1rh_1_1power__of__two__growth__policy.html)** Default policy used by `tsl::robin_map/set`. This policy keeps the size of the bucket array of the hash table to a power of two. This constraint allows the policy to avoid the usage of the slow modulo operation to map a hash to a bucket, instead of hash % 2n, it uses hash & (2n - 1) (see [fast modulo](https://en.wikipedia.org/wiki/Modulo_operation#Performance_issues)). Fast but this may cause a lot of collisions with a poor hash function as the modulo with a power of two only masks the most significant bits in the end. -* **[tsl::rh::prime_growth_policy.](https://tessil.github.io/robin-map/classtsl_1_1rh_1_1prime__growth__policy.html)** Default policy used by `tsl::robin_pg_map/set`. The policy keeps the size of the bucket array of the hash table to a prime number. When mapping a hash to a bucket, using a prime number as modulo will result in a better distribution of the hash across the buckets even with a poor hash function. To allow the compiler to optimize the modulo operation, the policy use a lookup table with constant primes modulos (see [API](https://tessil.github.io/robin-map/classtsl_1_1rh_1_1prime__growth__policy.html#details) for details). Slower than `tsl::rh::power_of_two_growth_policy` but more secure. -* **[tsl::rh::mod_growth_policy.](https://tessil.github.io/robin-map/classtsl_1_1rh_1_1mod__growth__policy.html)** The policy grows the map by a customizable growth factor passed in parameter. It then just use the modulo operator to map a hash to a bucket. Slower but more flexible. - - -To implement your own policy, you have to implement the following interface. - -```c++ -struct custom_policy { - // Called on hash table construction and rehash, min_bucket_count_in_out is the minimum buckets - // that the hash table needs. The policy can change it to a higher number of buckets if needed - // and the hash table will use this value as bucket count. If 0 bucket is asked, then the value - // must stay at 0. - explicit custom_policy(std::size_t& min_bucket_count_in_out); - - // Return the bucket [0, bucket_count()) to which the hash belongs. - // If bucket_count() is 0, it must always return 0. - std::size_t bucket_for_hash(std::size_t hash) const noexcept; - - // Return the number of buckets that should be used on next growth - std::size_t next_bucket_count() const; - - // Maximum number of buckets supported by the policy - std::size_t max_bucket_count() const; - - // Reset the growth policy as if the policy was created with a bucket count of 0. - // After a clear, the policy must always return 0 when bucket_for_hash() is called. - void clear() noexcept; -} -``` - -### Installation - -To use robin-map, just add the [include](include/) directory to your include path. It is a **header-only** library. - -If you use CMake, you can also use the `tsl::robin_map` exported target from the [CMakeLists.txt](CMakeLists.txt) with `target_link_libraries`. -```cmake -# Example where the robin-map project is stored in a third-party directory -add_subdirectory(third-party/robin-map) -target_link_libraries(your_target PRIVATE tsl::robin_map) -``` - -If the project has been installed through `make install`, you can also use `find_package(tsl-robin-map REQUIRED)` instead of `add_subdirectory`. - -The library is available in [vcpkg](https://github.com/Microsoft/vcpkg/tree/master/ports/robin-map) and [conan](https://conan.io/center/tsl-robin-map). It's also present in [Debian](https://packages.debian.org/buster/robin-map-dev), [Ubuntu](https://packages.ubuntu.com/disco/robin-map-dev) and [Fedora](https://apps.fedoraproject.org/packages/robin-map-devel) package repositories. - -The code should work with any C++17 standard-compliant compiler. - -To run the tests you will need the Boost Test library and CMake. - -```bash -git clone https://github.com/Tessil/robin-map.git -cd robin-map/tests -mkdir build -cd build -cmake .. -cmake --build . -./tsl_robin_map_tests -``` - -### Usage - -The API can be found [here](https://tessil.github.io/robin-map/). - -All methods are not documented yet, but they replicate the behavior of the ones in `std::unordered_map` and `std::unordered_set`, except if specified otherwise. - - -### Example - -```c++ -#include -#include -#include -#include -#include - -int main() { - tsl::robin_map map = {{"a", 1}, {"b", 2}}; - map["c"] = 3; - map["d"] = 4; - - map.insert({"e", 5}); - map.erase("b"); - - for(auto it = map.begin(); it != map.end(); ++it) { - //it->second += 2; // Not valid. - it.value() += 2; - } - - // {d, 6} {a, 3} {e, 7} {c, 5} - for(const auto& key_value : map) { - std::cout << "{" << key_value.first << ", " << key_value.second << "}" << std::endl; - } - - - if(map.find("a") != map.end()) { - std::cout << "Found \"a\"." << std::endl; - } - - const std::size_t precalculated_hash = std::hash()("a"); - // If we already know the hash beforehand, we can pass it in parameter to speed-up lookups. - if(map.find("a", precalculated_hash) != map.end()) { - std::cout << "Found \"a\" with hash " << precalculated_hash << "." << std::endl; - } - - - /* - * Calculating the hash and comparing two std::string may be slow. - * We can store the hash of each std::string in the hash map to make - * the inserts and lookups faster by setting StoreHash to true. - */ - tsl::robin_map, - std::equal_to, - std::allocator>, - true> map2; - - map2["a"] = 1; - map2["b"] = 2; - - // {a, 1} {b, 2} - for(const auto& key_value : map2) { - std::cout << "{" << key_value.first << ", " << key_value.second << "}" << std::endl; - } - - - - - tsl::robin_set set; - set.insert({1, 9, 0}); - set.insert({2, -1, 9}); - - // {0} {1} {2} {9} {-1} - for(const auto& key : set) { - std::cout << "{" << key << "}" << std::endl; - } -} -``` - -#### Heterogeneous lookups - -Heterogeneous overloads allow the usage of other types than `Key` for lookup and erase operations as long as the used types are hashable and comparable to `Key`. - -To activate the heterogeneous overloads in `tsl::robin_map/set`, the qualified-id `KeyEqual::is_transparent` must be valid. It works the same way as for [`std::map::find`](http://en.cppreference.com/w/cpp/container/map/find). You can either use [`std::equal_to<>`](http://en.cppreference.com/w/cpp/utility/functional/equal_to_void) or define your own function object. - -Both `KeyEqual` and `Hash` will need to be able to deal with the different types. - -```c++ -#include -#include -#include -#include - - -struct employee { - employee(int id, std::string name) : m_id(id), m_name(std::move(name)) { - } - - // Either we include the comparators in the class and we use `std::equal_to<>`... - friend bool operator==(const employee& empl, int empl_id) { - return empl.m_id == empl_id; - } - - friend bool operator==(int empl_id, const employee& empl) { - return empl_id == empl.m_id; - } - - friend bool operator==(const employee& empl1, const employee& empl2) { - return empl1.m_id == empl2.m_id; - } - - - int m_id; - std::string m_name; -}; - -// ... or we implement a separate class to compare employees. -struct equal_employee { - using is_transparent = void; - - bool operator()(const employee& empl, int empl_id) const { - return empl.m_id == empl_id; - } - - bool operator()(int empl_id, const employee& empl) const { - return empl_id == empl.m_id; - } - - bool operator()(const employee& empl1, const employee& empl2) const { - return empl1.m_id == empl2.m_id; - } -}; - -struct hash_employee { - std::size_t operator()(const employee& empl) const { - return std::hash()(empl.m_id); - } - - std::size_t operator()(int id) const { - return std::hash()(id); - } -}; - - -int main() { - // Use std::equal_to<> which will automatically deduce and forward the parameters - tsl::robin_map> map; - map.insert({employee(1, "John Doe"), 2001}); - map.insert({employee(2, "Jane Doe"), 2002}); - map.insert({employee(3, "John Smith"), 2003}); - - // John Smith 2003 - auto it = map.find(3); - if(it != map.end()) { - std::cout << it->first.m_name << " " << it->second << std::endl; - } - - map.erase(1); - - - - // Use a custom KeyEqual which has an is_transparent member type - tsl::robin_map map2; - map2.insert({employee(4, "Johnny Doe"), 2004}); - - // 2004 - std::cout << map2.at(4) << std::endl; -} -``` - -#### Serialization - -The library provides an efficient way to serialize and deserialize a map or a set so that it can be saved to a file or send through the network. -To do so, it requires the user to provide a function object for both serialization and deserialization. - -```c++ -struct serializer { - // Must support the following types for U: std::int16_t, std::uint32_t, - // std::uint64_t, float and std::pair if a map is used or Key for - // a set. - template - void operator()(const U& value); -}; -``` - -```c++ -struct deserializer { - // Must support the following types for U: std::int16_t, std::uint32_t, - // std::uint64_t, float and std::pair if a map is used or Key for - // a set. - template - U operator()(); -}; -``` - -Note that the implementation leaves binary compatibility (endianness, float binary representation, size of int, ...) of the types it serializes/deserializes in the hands of the provided function objects if compatibility is required. - -More details regarding the `serialize` and `deserialize` methods can be found in the [API](https://tessil.github.io/robin-map/classtsl_1_1robin__map.html). - -```c++ -#include -#include -#include -#include -#include - - -class serializer { -public: - serializer(const char* file_name) { - m_ostream.exceptions(m_ostream.badbit | m_ostream.failbit); - m_ostream.open(file_name, std::ios::binary); - } - - template::value>::type* = nullptr> - void operator()(const T& value) { - m_ostream.write(reinterpret_cast(&value), sizeof(T)); - } - - void operator()(const std::pair& value) { - (*this)(value.first); - (*this)(value.second); - } - -private: - std::ofstream m_ostream; -}; - -class deserializer { -public: - deserializer(const char* file_name) { - m_istream.exceptions(m_istream.badbit | m_istream.failbit | m_istream.eofbit); - m_istream.open(file_name, std::ios::binary); - } - - template - T operator()() { - T value; - deserialize(value); - - return value; - } - -private: - template::value>::type* = nullptr> - void deserialize(T& value) { - m_istream.read(reinterpret_cast(&value), sizeof(T)); - } - - void deserialize(std::pair& value) { - deserialize(value.first); - deserialize(value.second); - } - -private: - std::ifstream m_istream; -}; - - -int main() { - const tsl::robin_map map = {{1, -1}, {2, -2}, {3, -3}, {4, -4}}; - - - const char* file_name = "robin_map.data"; - { - serializer serial(file_name); - map.serialize(serial); - } - - { - deserializer dserial(file_name); - auto map_deserialized = tsl::robin_map::deserialize(dserial); - - assert(map == map_deserialized); - } - - { - deserializer dserial(file_name); - - /** - * If the serialized and deserialized map are hash compatibles (see conditions in API), - * setting the argument to true speed-up the deserialization process as we don't have - * to recalculate the hash of each key. We also know how much space each bucket needs. - */ - const bool hash_compatible = true; - auto map_deserialized = - tsl::robin_map::deserialize(dserial, hash_compatible); - - assert(map == map_deserialized); - } -} -``` - -##### Serialization with Boost Serialization and compression with zlib - -It is possible to use a serialization library to avoid the boilerplate. - -The following example uses Boost Serialization with the Boost zlib compression stream to reduce the size of the resulting serialized file. The example requires C++20 due to the usage of the template parameter list syntax in lambdas, but it can be adapted to less recent versions. - -```c++ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -namespace boost { namespace serialization { - template - void serialize(Archive & ar, tsl::robin_map& map, const unsigned int version) { - split_free(ar, map, version); - } - - template - void save(Archive & ar, const tsl::robin_map& map, const unsigned int /*version*/) { - auto serializer = [&ar](const auto& v) { ar & v; }; - map.serialize(serializer); - } - - template - void load(Archive & ar, tsl::robin_map& map, const unsigned int /*version*/) { - auto deserializer = [&ar]() { U u; ar & u; return u; }; - map = tsl::robin_map::deserialize(deserializer); - } -}} - - -int main() { - tsl::robin_map map = {{1, -1}, {2, -2}, {3, -3}, {4, -4}}; - - - const char* file_name = "robin_map.data"; - { - std::ofstream ofs; - ofs.exceptions(ofs.badbit | ofs.failbit); - ofs.open(file_name, std::ios::binary); - - boost::iostreams::filtering_ostream fo; - fo.push(boost::iostreams::zlib_compressor()); - fo.push(ofs); - - boost::archive::binary_oarchive oa(fo); - - oa << map; - } - - { - std::ifstream ifs; - ifs.exceptions(ifs.badbit | ifs.failbit | ifs.eofbit); - ifs.open(file_name, std::ios::binary); - - boost::iostreams::filtering_istream fi; - fi.push(boost::iostreams::zlib_decompressor()); - fi.push(ifs); - - boost::archive::binary_iarchive ia(fi); - - tsl::robin_map map_deserialized; - ia >> map_deserialized; - - assert(map == map_deserialized); - } -} -``` - -#### Performance pitfalls - -Two potential performance pitfalls involving `tsl::robin_map` and -`tsl::robin_set` are noteworthy: - -1. *Bad hashes*. Hash functions that produce many collisions can lead to the - following surprising behavior: when the number of collisions exceeds a - certain threshold, the hash table will automatically expand to fix the - problem. However, in degenerate cases, this expansion might have _no effect_ - on the collision count, causing a failure mode where a linear sequence of - insertion leads to exponential storage growth. - - This case has mainly been observed when using the default power-of-two - growth strategy with the default STL `std::hash` for arithmetic types - `T`, which is often an identity! See issue - [#39](https://github.com/Tessil/robin-map/issues/39) for an example. The - solution is simple: use a better hash function and/or `tsl::robin_pg_set` / - `tsl::robin_pg_map`. - -2. *Element erasure and low load factors*. `tsl::robin_map` and - `tsl::robin_set` mirror the STL map/set API, which exposes an `iterator - erase(iterator)` method that removes an element at a certain position, - returning a valid iterator that points to the next element. - - Constructing this new iterator object requires walking to the next nonempty - bucket in the table, which can be a expensive operation when the hash table - has a low *load factor* (i.e., when `capacity()` is much larger then - `size()`). - - The `erase()` method furthermore never shrinks & re-hashes the table as - this is not permitted by the specification of this function. A linear - sequence of random removals without intermediate insertions can then lead to - a degenerate case with quadratic runtime cost. - - In such cases, an iterator return value is often not even needed, so the - cost is entirely unnecessary. Both `tsl::robin_set` and `tsl::robin_map` - therefore provide an alternative erasure method `void erase_fast(iterator)` - that does not return an iterator to avoid having to find the next element. - -### License - -The code is licensed under the MIT license, see the [LICENSE file](LICENSE) for details. diff --git a/src/nanobind/ext/robin_map/cmake/tsl-robin-mapConfig.cmake.in b/src/nanobind/ext/robin_map/cmake/tsl-robin-mapConfig.cmake.in deleted file mode 100644 index d2ce233..0000000 --- a/src/nanobind/ext/robin_map/cmake/tsl-robin-mapConfig.cmake.in +++ /dev/null @@ -1,9 +0,0 @@ -# This module sets the following variables: -# * tsl-robin-map_FOUND - true if tsl-robin-map found on the system -# * tsl-robin-map_INCLUDE_DIRS - the directory containing tsl-robin-map headers -@PACKAGE_INIT@ - -if(NOT TARGET tsl::robin_map) - include("${CMAKE_CURRENT_LIST_DIR}/tsl-robin-mapTargets.cmake") - get_target_property(tsl-robin-map_INCLUDE_DIRS tsl::robin_map INTERFACE_INCLUDE_DIRECTORIES) -endif() diff --git a/src/nanobind/ext/robin_map/doxygen.conf b/src/nanobind/ext/robin_map/doxygen.conf deleted file mode 100644 index 534fdb5..0000000 --- a/src/nanobind/ext/robin_map/doxygen.conf +++ /dev/null @@ -1,2483 +0,0 @@ -# Doxyfile 1.8.11 - -# This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project. -# -# All text after a double hash (##) is considered a comment and is placed in -# front of the TAG it is preceding. -# -# All text after a single hash (#) is considered a comment and will be ignored. -# The format is: -# TAG = value [value, ...] -# For lists, items can also be appended using: -# TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (\" \"). - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- - -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all text -# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv -# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv -# for the list of possible encodings. -# The default value is: UTF-8. - -DOXYFILE_ENCODING = UTF-8 - -# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by -# double-quotes, unless you are using Doxywizard) that should identify the -# project for which the documentation is generated. This name is used in the -# title of most generated pages and in a few other places. -# The default value is: My Project. - -PROJECT_NAME = robin-map - -# The PROJECT_NUMBER tag can be used to enter a project or revision number. This -# could be handy for archiving the generated documentation or if some version -# control system is used. - -PROJECT_NUMBER = - -# Using the PROJECT_BRIEF tag one can provide an optional one line description -# for a project that appears at the top of each page and should give viewer a -# quick idea about the purpose of the project. Keep the description short. - -PROJECT_BRIEF = - -# With the PROJECT_LOGO tag one can specify a logo or an icon that is included -# in the documentation. The maximum height of the logo should not exceed 55 -# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy -# the logo to the output directory. - -PROJECT_LOGO = - -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path -# into which the generated documentation will be written. If a relative path is -# entered, it will be relative to the location where doxygen was started. If -# left blank the current directory will be used. - -OUTPUT_DIRECTORY = doc/ - -# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- -# directories (in 2 levels) under the output directory of each output format and -# will distribute the generated files over these directories. Enabling this -# option can be useful when feeding doxygen a huge amount of source files, where -# putting all generated files in the same directory would otherwise causes -# performance problems for the file system. -# The default value is: NO. - -CREATE_SUBDIRS = NO - -# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII -# characters to appear in the names of generated files. If set to NO, non-ASCII -# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode -# U+3044. -# The default value is: NO. - -ALLOW_UNICODE_NAMES = NO - -# The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all constant output in the proper language. -# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, -# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), -# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, -# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), -# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, -# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, -# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, -# Ukrainian and Vietnamese. -# The default value is: English. - -OUTPUT_LANGUAGE = English - -# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member -# descriptions after the members that are listed in the file and class -# documentation (similar to Javadoc). Set to NO to disable this. -# The default value is: YES. - -BRIEF_MEMBER_DESC = YES - -# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief -# description of a member or function before the detailed description -# -# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the -# brief descriptions will be completely suppressed. -# The default value is: YES. - -REPEAT_BRIEF = YES - -# This tag implements a quasi-intelligent brief description abbreviator that is -# used to form the text in various listings. Each string in this list, if found -# as the leading text of the brief description, will be stripped from the text -# and the result, after processing the whole list, is used as the annotated -# text. Otherwise, the brief description is used as-is. If left blank, the -# following values are used ($name is automatically replaced with the name of -# the entity):The $name class, The $name widget, The $name file, is, provides, -# specifies, contains, represents, a, an and the. - -ABBREVIATE_BRIEF = "The $name class" \ - "The $name widget" \ - "The $name file" \ - is \ - provides \ - specifies \ - contains \ - represents \ - a \ - an \ - the - -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# doxygen will generate a detailed section even if there is only a brief -# description. -# The default value is: NO. - -ALWAYS_DETAILED_SEC = NO - -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all -# inherited members of a class in the documentation of that class as if those -# members were ordinary class members. Constructors, destructors and assignment -# operators of the base classes will not be shown. -# The default value is: NO. - -INLINE_INHERITED_MEMB = NO - -# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path -# before files name in the file list and in the header files. If set to NO the -# shortest path that makes the file name unique will be used -# The default value is: YES. - -FULL_PATH_NAMES = YES - -# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. -# Stripping is only done if one of the specified strings matches the left-hand -# part of the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the path to -# strip. -# -# Note that you can specify absolute paths here, but also relative paths, which -# will be relative from the directory where doxygen is started. -# This tag requires that the tag FULL_PATH_NAMES is set to YES. - -STRIP_FROM_PATH = - -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the -# path mentioned in the documentation of a class, which tells the reader which -# header file to include in order to use a class. If left blank only the name of -# the header file containing the class definition is used. Otherwise one should -# specify the list of include paths that are normally passed to the compiler -# using the -I flag. - -STRIP_FROM_INC_PATH = - -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but -# less readable) file names. This can be useful is your file systems doesn't -# support long names like on DOS, Mac, or CD-ROM. -# The default value is: NO. - -SHORT_NAMES = NO - -# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the -# first line (until the first dot) of a Javadoc-style comment as the brief -# description. If set to NO, the Javadoc-style will behave just like regular Qt- -# style comments (thus requiring an explicit @brief command for a brief -# description.) -# The default value is: NO. - -JAVADOC_AUTOBRIEF = NO - -# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first -# line (until the first dot) of a Qt-style comment as the brief description. If -# set to NO, the Qt-style will behave just like regular Qt-style comments (thus -# requiring an explicit \brief command for a brief description.) -# The default value is: NO. - -QT_AUTOBRIEF = NO - -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a -# multi-line C++ special comment block (i.e. a block of //! or /// comments) as -# a brief description. This used to be the default behavior. The new default is -# to treat a multi-line C++ comment block as a detailed description. Set this -# tag to YES if you prefer the old behavior instead. -# -# Note that setting this tag to YES also means that rational rose comments are -# not recognized any more. -# The default value is: NO. - -MULTILINE_CPP_IS_BRIEF = NO - -# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the -# documentation from any documented member that it re-implements. -# The default value is: YES. - -INHERIT_DOCS = YES - -# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new -# page for each member. If set to NO, the documentation of a member will be part -# of the file/class/namespace that contains it. -# The default value is: NO. - -SEPARATE_MEMBER_PAGES = NO - -# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen -# uses this value to replace tabs by spaces in code fragments. -# Minimum value: 1, maximum value: 16, default value: 4. - -TAB_SIZE = 4 - -# This tag can be used to specify a number of aliases that act as commands in -# the documentation. An alias has the form: -# name=value -# For example adding -# "sideeffect=@par Side Effects:\n" -# will allow you to put the command \sideeffect (or @sideeffect) in the -# documentation, which will result in a user-defined paragraph with heading -# "Side Effects:". You can put \n's in the value part of an alias to insert -# newlines. - -ALIASES = - -# This tag can be used to specify a number of word-keyword mappings (TCL only). -# A mapping has the form "name=value". For example adding "class=itcl::class" -# will allow you to use the command class in the itcl::class meaning. - -TCL_SUBST = - -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources -# only. Doxygen will then generate output that is more tailored for C. For -# instance, some of the names that are used will be different. The list of all -# members will be omitted, etc. -# The default value is: NO. - -OPTIMIZE_OUTPUT_FOR_C = NO - -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or -# Python sources only. Doxygen will then generate output that is more tailored -# for that language. For instance, namespaces will be presented as packages, -# qualified scopes will look different, etc. -# The default value is: NO. - -OPTIMIZE_OUTPUT_JAVA = NO - -# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran -# sources. Doxygen will then generate output that is tailored for Fortran. -# The default value is: NO. - -OPTIMIZE_FOR_FORTRAN = NO - -# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL -# sources. Doxygen will then generate output that is tailored for VHDL. -# The default value is: NO. - -OPTIMIZE_OUTPUT_VHDL = NO - -# Doxygen selects the parser to use depending on the extension of the files it -# parses. With this tag you can assign which parser to use for a given -# extension. Doxygen has a built-in mapping, but you can override or extend it -# using this tag. The format is ext=language, where ext is a file extension, and -# language is one of the parsers supported by doxygen: IDL, Java, Javascript, -# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: -# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: -# Fortran. In the later case the parser tries to guess whether the code is fixed -# or free formatted code, this is the default for Fortran type files), VHDL. For -# instance to make doxygen treat .inc files as Fortran files (default is PHP), -# and .f files as C (default is Fortran), use: inc=Fortran f=C. -# -# Note: For files without extension you can use no_extension as a placeholder. -# -# Note that for custom extensions you also need to set FILE_PATTERNS otherwise -# the files are not read by doxygen. - -EXTENSION_MAPPING = - -# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments -# according to the Markdown format, which allows for more readable -# documentation. See http://daringfireball.net/projects/markdown/ for details. -# The output of markdown processing is further processed by doxygen, so you can -# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in -# case of backward compatibilities issues. -# The default value is: YES. - -MARKDOWN_SUPPORT = YES - -# When enabled doxygen tries to link words that correspond to documented -# classes, or namespaces to their corresponding documentation. Such a link can -# be prevented in individual cases by putting a % sign in front of the word or -# globally by setting AUTOLINK_SUPPORT to NO. -# The default value is: YES. - -AUTOLINK_SUPPORT = YES - -# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want -# to include (a tag file for) the STL sources as input, then you should set this -# tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); -# versus func(std::string) {}). This also make the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. -# The default value is: NO. - -BUILTIN_STL_SUPPORT = NO - -# If you use Microsoft's C++/CLI language, you should set this option to YES to -# enable parsing support. -# The default value is: NO. - -CPP_CLI_SUPPORT = NO - -# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: -# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen -# will parse them like normal C++ but will assume all classes use public instead -# of private inheritance when no explicit protection keyword is present. -# The default value is: NO. - -SIP_SUPPORT = NO - -# For Microsoft's IDL there are propget and propput attributes to indicate -# getter and setter methods for a property. Setting this option to YES will make -# doxygen to replace the get and set methods by a property in the documentation. -# This will only work if the methods are indeed getting or setting a simple -# type. If this is not the case, or you want to show the methods anyway, you -# should set this option to NO. -# The default value is: YES. - -IDL_PROPERTY_SUPPORT = YES - -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default -# all members of a group must be documented explicitly. -# The default value is: NO. - -DISTRIBUTE_GROUP_DOC = NO - -# If one adds a struct or class to a group and this option is enabled, then also -# any nested class or struct is added to the same group. By default this option -# is disabled and one has to add nested compounds explicitly via \ingroup. -# The default value is: NO. - -GROUP_NESTED_COMPOUNDS = NO - -# Set the SUBGROUPING tag to YES to allow class member groups of the same type -# (for instance a group of public functions) to be put as a subgroup of that -# type (e.g. under the Public Functions section). Set it to NO to prevent -# subgrouping. Alternatively, this can be done per class using the -# \nosubgrouping command. -# The default value is: YES. - -SUBGROUPING = YES - -# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions -# are shown inside the group in which they are included (e.g. using \ingroup) -# instead of on a separate page (for HTML and Man pages) or section (for LaTeX -# and RTF). -# -# Note that this feature does not work in combination with -# SEPARATE_MEMBER_PAGES. -# The default value is: NO. - -INLINE_GROUPED_CLASSES = NO - -# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions -# with only public data fields or simple typedef fields will be shown inline in -# the documentation of the scope in which they are defined (i.e. file, -# namespace, or group documentation), provided this scope is documented. If set -# to NO, structs, classes, and unions are shown on a separate page (for HTML and -# Man pages) or section (for LaTeX and RTF). -# The default value is: NO. - -INLINE_SIMPLE_STRUCTS = NO - -# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or -# enum is documented as struct, union, or enum with the name of the typedef. So -# typedef struct TypeS {} TypeT, will appear in the documentation as a struct -# with name TypeT. When disabled the typedef will appear as a member of a file, -# namespace, or class. And the struct will be named TypeS. This can typically be -# useful for C code in case the coding convention dictates that all compound -# types are typedef'ed and only the typedef is referenced, never the tag name. -# The default value is: NO. - -TYPEDEF_HIDES_STRUCT = NO - -# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This -# cache is used to resolve symbols given their name and scope. Since this can be -# an expensive process and often the same symbol appears multiple times in the -# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small -# doxygen will become slower. If the cache is too large, memory is wasted. The -# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range -# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 -# symbols. At the end of a run doxygen will report the cache usage and suggest -# the optimal cache size from a speed point of view. -# Minimum value: 0, maximum value: 9, default value: 0. - -LOOKUP_CACHE_SIZE = 0 - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- - -# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in -# documentation are documented, even if no documentation was available. Private -# class members and static file members will be hidden unless the -# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. -# Note: This will also disable the warnings about undocumented members that are -# normally produced when WARNINGS is set to YES. -# The default value is: NO. - -EXTRACT_ALL = YES - -# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will -# be included in the documentation. -# The default value is: NO. - -EXTRACT_PRIVATE = NO - -# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal -# scope will be included in the documentation. -# The default value is: NO. - -EXTRACT_PACKAGE = NO - -# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be -# included in the documentation. -# The default value is: NO. - -EXTRACT_STATIC = NO - -# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined -# locally in source files will be included in the documentation. If set to NO, -# only classes defined in header files are included. Does not have any effect -# for Java sources. -# The default value is: YES. - -EXTRACT_LOCAL_CLASSES = YES - -# This flag is only useful for Objective-C code. If set to YES, local methods, -# which are defined in the implementation section but not in the interface are -# included in the documentation. If set to NO, only methods in the interface are -# included. -# The default value is: NO. - -EXTRACT_LOCAL_METHODS = NO - -# If this flag is set to YES, the members of anonymous namespaces will be -# extracted and appear in the documentation as a namespace called -# 'anonymous_namespace{file}', where file will be replaced with the base name of -# the file that contains the anonymous namespace. By default anonymous namespace -# are hidden. -# The default value is: NO. - -EXTRACT_ANON_NSPACES = NO - -# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all -# undocumented members inside documented classes or files. If set to NO these -# members will be included in the various overviews, but no documentation -# section is generated. This option has no effect if EXTRACT_ALL is enabled. -# The default value is: NO. - -HIDE_UNDOC_MEMBERS = NO - -# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. If set -# to NO, these classes will be included in the various overviews. This option -# has no effect if EXTRACT_ALL is enabled. -# The default value is: NO. - -HIDE_UNDOC_CLASSES = NO - -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend -# (class|struct|union) declarations. If set to NO, these declarations will be -# included in the documentation. -# The default value is: NO. - -HIDE_FRIEND_COMPOUNDS = NO - -# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any -# documentation blocks found inside the body of a function. If set to NO, these -# blocks will be appended to the function's detailed documentation block. -# The default value is: NO. - -HIDE_IN_BODY_DOCS = NO - -# The INTERNAL_DOCS tag determines if documentation that is typed after a -# \internal command is included. If the tag is set to NO then the documentation -# will be excluded. Set it to YES to include the internal documentation. -# The default value is: NO. - -INTERNAL_DOCS = NO - -# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file -# names in lower-case letters. If set to YES, upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. -# The default value is: system dependent. - -CASE_SENSE_NAMES = NO - -# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with -# their full class and namespace scopes in the documentation. If set to YES, the -# scope will be hidden. -# The default value is: NO. - -HIDE_SCOPE_NAMES = NO - -# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will -# append additional text to a page's title, such as Class Reference. If set to -# YES the compound reference will be hidden. -# The default value is: NO. - -HIDE_COMPOUND_REFERENCE= NO - -# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of -# the files that are included by a file in the documentation of that file. -# The default value is: YES. - -SHOW_INCLUDE_FILES = YES - -# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each -# grouped member an include statement to the documentation, telling the reader -# which file to include in order to use the member. -# The default value is: NO. - -SHOW_GROUPED_MEMB_INC = NO - -# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include -# files with double quotes in the documentation rather than with sharp brackets. -# The default value is: NO. - -FORCE_LOCAL_INCLUDES = NO - -# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the -# documentation for inline members. -# The default value is: YES. - -INLINE_INFO = YES - -# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the -# (detailed) documentation of file and class members alphabetically by member -# name. If set to NO, the members will appear in declaration order. -# The default value is: YES. - -SORT_MEMBER_DOCS = YES - -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief -# descriptions of file, namespace and class members alphabetically by member -# name. If set to NO, the members will appear in declaration order. Note that -# this will also influence the order of the classes in the class list. -# The default value is: NO. - -SORT_BRIEF_DOCS = NO - -# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the -# (brief and detailed) documentation of class members so that constructors and -# destructors are listed first. If set to NO the constructors will appear in the -# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. -# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief -# member documentation. -# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting -# detailed member documentation. -# The default value is: NO. - -SORT_MEMBERS_CTORS_1ST = NO - -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy -# of group names into alphabetical order. If set to NO the group names will -# appear in their defined order. -# The default value is: NO. - -SORT_GROUP_NAMES = NO - -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by -# fully-qualified names, including namespaces. If set to NO, the class list will -# be sorted only by class name, not including the namespace part. -# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the alphabetical -# list. -# The default value is: NO. - -SORT_BY_SCOPE_NAME = NO - -# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper -# type resolution of all parameters of a function it will reject a match between -# the prototype and the implementation of a member function even if there is -# only one candidate or it is obvious which candidate to choose by doing a -# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still -# accept a match between prototype and implementation in such cases. -# The default value is: NO. - -STRICT_PROTO_MATCHING = NO - -# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo -# list. This list is created by putting \todo commands in the documentation. -# The default value is: YES. - -GENERATE_TODOLIST = YES - -# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test -# list. This list is created by putting \test commands in the documentation. -# The default value is: YES. - -GENERATE_TESTLIST = YES - -# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug -# list. This list is created by putting \bug commands in the documentation. -# The default value is: YES. - -GENERATE_BUGLIST = YES - -# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) -# the deprecated list. This list is created by putting \deprecated commands in -# the documentation. -# The default value is: YES. - -GENERATE_DEPRECATEDLIST= YES - -# The ENABLED_SECTIONS tag can be used to enable conditional documentation -# sections, marked by \if ... \endif and \cond -# ... \endcond blocks. - -ENABLED_SECTIONS = - -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the -# initial value of a variable or macro / define can have for it to appear in the -# documentation. If the initializer consists of more lines than specified here -# it will be hidden. Use a value of 0 to hide initializers completely. The -# appearance of the value of individual variables and macros / defines can be -# controlled using \showinitializer or \hideinitializer command in the -# documentation regardless of this setting. -# Minimum value: 0, maximum value: 10000, default value: 30. - -MAX_INITIALIZER_LINES = 30 - -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at -# the bottom of the documentation of classes and structs. If set to YES, the -# list will mention the files that were used to generate the documentation. -# The default value is: YES. - -SHOW_USED_FILES = YES - -# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This -# will remove the Files entry from the Quick Index and from the Folder Tree View -# (if specified). -# The default value is: YES. - -SHOW_FILES = YES - -# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces -# page. This will remove the Namespaces entry from the Quick Index and from the -# Folder Tree View (if specified). -# The default value is: YES. - -SHOW_NAMESPACES = YES - -# The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from -# the version control system). Doxygen will invoke the program by executing (via -# popen()) the command command input-file, where command is the value of the -# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided -# by doxygen. Whatever the program writes to standard output is used as the file -# version. For an example see the documentation. - -FILE_VERSION_FILTER = - -# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed -# by doxygen. The layout file controls the global structure of the generated -# output files in an output format independent way. To create the layout file -# that represents doxygen's defaults, run doxygen with the -l option. You can -# optionally specify a file name after the option, if omitted DoxygenLayout.xml -# will be used as the name of the layout file. -# -# Note that if you run doxygen from a directory containing a file called -# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE -# tag is left empty. - -LAYOUT_FILE = - -# The CITE_BIB_FILES tag can be used to specify one or more bib files containing -# the reference definitions. This must be a list of .bib files. The .bib -# extension is automatically appended if omitted. This requires the bibtex tool -# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. -# For LaTeX the style of the bibliography can be controlled using -# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the -# search path. See also \cite for info how to create references. - -CITE_BIB_FILES = - -#--------------------------------------------------------------------------- -# Configuration options related to warning and progress messages -#--------------------------------------------------------------------------- - -# The QUIET tag can be used to turn on/off the messages that are generated to -# standard output by doxygen. If QUIET is set to YES this implies that the -# messages are off. -# The default value is: NO. - -QUIET = NO - -# The WARNINGS tag can be used to turn on/off the warning messages that are -# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES -# this implies that the warnings are on. -# -# Tip: Turn warnings on while writing the documentation. -# The default value is: YES. - -WARNINGS = YES - -# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate -# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag -# will automatically be disabled. -# The default value is: YES. - -WARN_IF_UNDOCUMENTED = YES - -# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some parameters -# in a documented function, or documenting parameters that don't exist or using -# markup commands wrongly. -# The default value is: YES. - -WARN_IF_DOC_ERROR = YES - -# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that -# are documented, but have no documentation for their parameters or return -# value. If set to NO, doxygen will only warn about wrong or incomplete -# parameter documentation, but not about the absence of documentation. -# The default value is: NO. - -WARN_NO_PARAMDOC = NO - -# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when -# a warning is encountered. -# The default value is: NO. - -WARN_AS_ERROR = NO - -# The WARN_FORMAT tag determines the format of the warning messages that doxygen -# can produce. The string should contain the $file, $line, and $text tags, which -# will be replaced by the file and line number from which the warning originated -# and the warning text. Optionally the format may contain $version, which will -# be replaced by the version of the file (if it could be obtained via -# FILE_VERSION_FILTER) -# The default value is: $file:$line: $text. - -WARN_FORMAT = "$file:$line: $text" - -# The WARN_LOGFILE tag can be used to specify a file to which warning and error -# messages should be written. If left blank the output is written to standard -# error (stderr). - -WARN_LOGFILE = - -#--------------------------------------------------------------------------- -# Configuration options related to the input files -#--------------------------------------------------------------------------- - -# The INPUT tag is used to specify the files and/or directories that contain -# documented source files. You may enter file names like myfile.cpp or -# directories like /usr/src/myproject. Separate the files or directories with -# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING -# Note: If this tag is empty the current directory is searched. - -INPUT = include/tsl/ README.md - -# This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses -# libiconv (or the iconv built into libc) for the transcoding. See the libiconv -# documentation (see: http://www.gnu.org/software/libiconv) for the list of -# possible encodings. -# The default value is: UTF-8. - -INPUT_ENCODING = UTF-8 - -# If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and -# *.h) to filter out the source-files in the directories. -# -# Note that for custom extensions or not directly supported extensions you also -# need to set EXTENSION_MAPPING for the extension otherwise the files are not -# read by doxygen. -# -# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, -# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, -# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, -# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f, *.for, *.tcl, -# *.vhd, *.vhdl, *.ucf, *.qsf, *.as and *.js. - -FILE_PATTERNS = *.c \ - *.cc \ - *.cxx \ - *.cpp \ - *.c++ \ - *.java \ - *.ii \ - *.ixx \ - *.ipp \ - *.i++ \ - *.inl \ - *.idl \ - *.ddl \ - *.odl \ - *.h \ - *.hh \ - *.hxx \ - *.hpp \ - *.h++ \ - *.cs \ - *.d \ - *.php \ - *.php4 \ - *.php5 \ - *.phtml \ - *.inc \ - *.m \ - *.markdown \ - *.md \ - *.mm \ - *.dox \ - *.py \ - *.pyw \ - *.f90 \ - *.f \ - *.for \ - *.tcl \ - *.vhd \ - *.vhdl \ - *.ucf \ - *.qsf \ - *.as \ - *.js - -# The RECURSIVE tag can be used to specify whether or not subdirectories should -# be searched for input files as well. -# The default value is: NO. - -RECURSIVE = YES - -# The EXCLUDE tag can be used to specify files and/or directories that should be -# excluded from the INPUT source files. This way you can easily exclude a -# subdirectory from a directory tree whose root is specified with the INPUT tag. -# -# Note that relative paths are relative to the directory from which doxygen is -# run. - -EXCLUDE = - -# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or -# directories that are symbolic links (a Unix file system feature) are excluded -# from the input. -# The default value is: NO. - -EXCLUDE_SYMLINKS = NO - -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. -# -# Note that the wildcards are matched against the file with absolute path, so to -# exclude all test directories for example use the pattern */test/* - -EXCLUDE_PATTERNS = - -# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names -# (namespaces, classes, functions, etc.) that should be excluded from the -# output. The symbol name can be a fully qualified name, a word, or if the -# wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test -# -# Note that the wildcards are matched against the file with absolute path, so to -# exclude all test directories use the pattern */test/* - -EXCLUDE_SYMBOLS = \ -tsl::detail_robin_hash::has_is_transparent* \ -tsl::detail_robin_hash::make_void* \ -tsl::detail_robin_hash::is_power_of_two_policy* \ -tsl::detail_robin_hash::bucket_entry* - -# The EXAMPLE_PATH tag can be used to specify one or more files or directories -# that contain example code fragments that are included (see the \include -# command). - -EXAMPLE_PATH = - -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and -# *.h) to filter out the source-files in the directories. If left blank all -# files are included. - -EXAMPLE_PATTERNS = * - -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude commands -# irrespective of the value of the RECURSIVE tag. -# The default value is: NO. - -EXAMPLE_RECURSIVE = NO - -# The IMAGE_PATH tag can be used to specify one or more files or directories -# that contain images that are to be included in the documentation (see the -# \image command). - -IMAGE_PATH = - -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command: -# -# -# -# where is the value of the INPUT_FILTER tag, and is the -# name of an input file. Doxygen will then use the output that the filter -# program writes to standard output. If FILTER_PATTERNS is specified, this tag -# will be ignored. -# -# Note that the filter must not add or remove lines; it is applied before the -# code is scanned, but not when the output code is generated. If lines are added -# or removed, the anchors will not be placed correctly. -# -# Note that for custom extensions or not directly supported extensions you also -# need to set EXTENSION_MAPPING for the extension otherwise the files are not -# properly processed by doxygen. - -INPUT_FILTER = - -# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. The filters are a list of the form: pattern=filter -# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how -# filters are used. If the FILTER_PATTERNS tag is empty or if none of the -# patterns match the file name, INPUT_FILTER is applied. -# -# Note that for custom extensions or not directly supported extensions you also -# need to set EXTENSION_MAPPING for the extension otherwise the files are not -# properly processed by doxygen. - -FILTER_PATTERNS = - -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will also be used to filter the input files that are used for -# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). -# The default value is: NO. - -FILTER_SOURCE_FILES = NO - -# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file -# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and -# it is also possible to disable source filtering for a specific pattern using -# *.ext= (so without naming a filter). -# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. - -FILTER_SOURCE_PATTERNS = - -# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that -# is part of the input, its contents will be placed on the main page -# (index.html). This can be useful if you have a project on for instance GitHub -# and want to reuse the introduction page also for the doxygen output. - -USE_MDFILE_AS_MAINPAGE = README.md - -#--------------------------------------------------------------------------- -# Configuration options related to source browsing -#--------------------------------------------------------------------------- - -# If the SOURCE_BROWSER tag is set to YES then a list of source files will be -# generated. Documented entities will be cross-referenced with these sources. -# -# Note: To get rid of all source code in the generated output, make sure that -# also VERBATIM_HEADERS is set to NO. -# The default value is: NO. - -SOURCE_BROWSER = NO - -# Setting the INLINE_SOURCES tag to YES will include the body of functions, -# classes and enums directly into the documentation. -# The default value is: NO. - -INLINE_SOURCES = NO - -# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any -# special comment blocks from generated source code fragments. Normal C, C++ and -# Fortran comments will always remain visible. -# The default value is: YES. - -STRIP_CODE_COMMENTS = YES - -# If the REFERENCED_BY_RELATION tag is set to YES then for each documented -# function all documented functions referencing it will be listed. -# The default value is: NO. - -REFERENCED_BY_RELATION = NO - -# If the REFERENCES_RELATION tag is set to YES then for each documented function -# all documented entities called/used by that function will be listed. -# The default value is: NO. - -REFERENCES_RELATION = NO - -# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set -# to YES then the hyperlinks from functions in REFERENCES_RELATION and -# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will -# link to the documentation. -# The default value is: YES. - -REFERENCES_LINK_SOURCE = YES - -# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the -# source code will show a tooltip with additional information such as prototype, -# brief description and links to the definition and documentation. Since this -# will make the HTML file larger and loading of large files a bit slower, you -# can opt to disable this feature. -# The default value is: YES. -# This tag requires that the tag SOURCE_BROWSER is set to YES. - -SOURCE_TOOLTIPS = YES - -# If the USE_HTAGS tag is set to YES then the references to source code will -# point to the HTML generated by the htags(1) tool instead of doxygen built-in -# source browser. The htags tool is part of GNU's global source tagging system -# (see http://www.gnu.org/software/global/global.html). You will need version -# 4.8.6 or higher. -# -# To use it do the following: -# - Install the latest version of global -# - Enable SOURCE_BROWSER and USE_HTAGS in the config file -# - Make sure the INPUT points to the root of the source tree -# - Run doxygen as normal -# -# Doxygen will invoke htags (and that will in turn invoke gtags), so these -# tools must be available from the command line (i.e. in the search path). -# -# The result: instead of the source browser generated by doxygen, the links to -# source code will now point to the output of htags. -# The default value is: NO. -# This tag requires that the tag SOURCE_BROWSER is set to YES. - -USE_HTAGS = NO - -# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a -# verbatim copy of the header file for each class for which an include is -# specified. Set to NO to disable this. -# See also: Section \class. -# The default value is: YES. - -VERBATIM_HEADERS = YES - -# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the -# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the -# cost of reduced performance. This can be particularly helpful with template -# rich C++ code for which doxygen's built-in parser lacks the necessary type -# information. -# Note: The availability of this option depends on whether or not doxygen was -# generated with the -Duse-libclang=ON option for CMake. -# The default value is: NO. - -CLANG_ASSISTED_PARSING = YES - -# If clang assisted parsing is enabled you can provide the compiler with command -# line options that you would normally use when invoking the compiler. Note that -# the include paths will already be set by doxygen for the files and directories -# specified with INPUT and INCLUDE_PATH. -# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. - -CLANG_OPTIONS = -std=c++17 - -#--------------------------------------------------------------------------- -# Configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all -# compounds will be generated. Enable this if the project contains a lot of -# classes, structs, unions or interfaces. -# The default value is: YES. - -ALPHABETICAL_INDEX = YES - -# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in -# which the alphabetical index list will be split. -# Minimum value: 1, maximum value: 20, default value: 5. -# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. - -COLS_IN_ALPHA_INDEX = 5 - -# In case all classes in a project start with a common prefix, all classes will -# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag -# can be used to specify a prefix (or a list of prefixes) that should be ignored -# while generating the index headers. -# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. - -IGNORE_PREFIX = - -#--------------------------------------------------------------------------- -# Configuration options related to the HTML output -#--------------------------------------------------------------------------- - -# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output -# The default value is: YES. - -GENERATE_HTML = YES - -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a -# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of -# it. -# The default directory is: html. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_OUTPUT = html - -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each -# generated HTML page (for example: .htm, .php, .asp). -# The default value is: .html. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_FILE_EXTENSION = .html - -# The HTML_HEADER tag can be used to specify a user-defined HTML header file for -# each generated HTML page. If the tag is left blank doxygen will generate a -# standard header. -# -# To get valid HTML the header file that includes any scripts and style sheets -# that doxygen needs, which is dependent on the configuration options used (e.g. -# the setting GENERATE_TREEVIEW). It is highly recommended to start with a -# default header using -# doxygen -w html new_header.html new_footer.html new_stylesheet.css -# YourConfigFile -# and then modify the file new_header.html. See also section "Doxygen usage" -# for information on how to generate the default header that doxygen normally -# uses. -# Note: The header is subject to change so you typically have to regenerate the -# default header when upgrading to a newer version of doxygen. For a description -# of the possible markers and block names see the documentation. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_HEADER = - -# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each -# generated HTML page. If the tag is left blank doxygen will generate a standard -# footer. See HTML_HEADER for more information on how to generate a default -# footer and what special commands can be used inside the footer. See also -# section "Doxygen usage" for information on how to generate the default footer -# that doxygen normally uses. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_FOOTER = - -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style -# sheet that is used by each HTML page. It can be used to fine-tune the look of -# the HTML output. If left blank doxygen will generate a default style sheet. -# See also section "Doxygen usage" for information on how to generate the style -# sheet that doxygen normally uses. -# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as -# it is more robust and this tag (HTML_STYLESHEET) will in the future become -# obsolete. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_STYLESHEET = - -# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined -# cascading style sheets that are included after the standard style sheets -# created by doxygen. Using this option one can overrule certain style aspects. -# This is preferred over using HTML_STYLESHEET since it does not replace the -# standard style sheet and is therefore more robust against future updates. -# Doxygen will copy the style sheet files to the output directory. -# Note: The order of the extra style sheet files is of importance (e.g. the last -# style sheet in the list overrules the setting of the previous ones in the -# list). For an example see the documentation. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_EXTRA_STYLESHEET = - -# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or -# other source files which should be copied to the HTML output directory. Note -# that these files will be copied to the base HTML output directory. Use the -# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these -# files. In the HTML_STYLESHEET file, use the file name only. Also note that the -# files will be copied as-is; there are no commands or markers available. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_EXTRA_FILES = - -# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen -# will adjust the colors in the style sheet and background images according to -# this color. Hue is specified as an angle on a colorwheel, see -# http://en.wikipedia.org/wiki/Hue for more information. For instance the value -# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 -# purple, and 360 is red again. -# Minimum value: 0, maximum value: 359, default value: 220. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_COLORSTYLE_HUE = 220 - -# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors -# in the HTML output. For a value of 0 the output will use grayscales only. A -# value of 255 will produce the most vivid colors. -# Minimum value: 0, maximum value: 255, default value: 100. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_COLORSTYLE_SAT = 100 - -# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the -# luminance component of the colors in the HTML output. Values below 100 -# gradually make the output lighter, whereas values above 100 make the output -# darker. The value divided by 100 is the actual gamma applied, so 80 represents -# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not -# change the gamma. -# Minimum value: 40, maximum value: 240, default value: 80. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_COLORSTYLE_GAMMA = 80 - -# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting this -# to YES can help to show when doxygen was last run and thus if the -# documentation is up to date. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_TIMESTAMP = NO - -# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML -# documentation will contain sections that can be hidden and shown after the -# page has loaded. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_DYNAMIC_SECTIONS = NO - -# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries -# shown in the various tree structured indices initially; the user can expand -# and collapse entries dynamically later on. Doxygen will expand the tree to -# such a level that at most the specified number of entries are visible (unless -# a fully collapsed tree already exceeds this amount). So setting the number of -# entries 1 will produce a full collapsed tree by default. 0 is a special value -# representing an infinite number of entries and will result in a full expanded -# tree by default. -# Minimum value: 0, maximum value: 9999, default value: 100. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_INDEX_NUM_ENTRIES = 100 - -# If the GENERATE_DOCSET tag is set to YES, additional index files will be -# generated that can be used as input for Apple's Xcode 3 integrated development -# environment (see: http://developer.apple.com/tools/xcode/), introduced with -# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a -# Makefile in the HTML output directory. Running make will produce the docset in -# that directory and running make install will install the docset in -# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at -# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html -# for more information. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_DOCSET = NO - -# This tag determines the name of the docset feed. A documentation feed provides -# an umbrella under which multiple documentation sets from a single provider -# (such as a company or product suite) can be grouped. -# The default value is: Doxygen generated docs. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_FEEDNAME = "Doxygen generated docs" - -# This tag specifies a string that should uniquely identify the documentation -# set bundle. This should be a reverse domain-name style string, e.g. -# com.mycompany.MyDocSet. Doxygen will append .docset to the name. -# The default value is: org.doxygen.Project. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_BUNDLE_ID = org.doxygen.Project - -# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify -# the documentation publisher. This should be a reverse domain-name style -# string, e.g. com.mycompany.MyDocSet.documentation. -# The default value is: org.doxygen.Publisher. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_PUBLISHER_ID = org.doxygen.Publisher - -# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. -# The default value is: Publisher. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_PUBLISHER_NAME = Publisher - -# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three -# additional HTML index files: index.hhp, index.hhc, and index.hhk. The -# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on -# Windows. -# -# The HTML Help Workshop contains a compiler that can convert all HTML output -# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML -# files are now used as the Windows 98 help format, and will replace the old -# Windows help format (.hlp) on all Windows platforms in the future. Compressed -# HTML files also contain an index, a table of contents, and you can search for -# words in the documentation. The HTML workshop also contains a viewer for -# compressed HTML files. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_HTMLHELP = NO - -# The CHM_FILE tag can be used to specify the file name of the resulting .chm -# file. You can add a path in front of the file if the result should not be -# written to the html output directory. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -CHM_FILE = - -# The HHC_LOCATION tag can be used to specify the location (absolute path -# including file name) of the HTML help compiler (hhc.exe). If non-empty, -# doxygen will try to run the HTML help compiler on the generated index.hhp. -# The file has to be specified with full path. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -HHC_LOCATION = - -# The GENERATE_CHI flag controls if a separate .chi index file is generated -# (YES) or that it should be included in the master .chm file (NO). -# The default value is: NO. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -GENERATE_CHI = NO - -# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) -# and project file content. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -CHM_INDEX_ENCODING = - -# The BINARY_TOC flag controls whether a binary table of contents is generated -# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it -# enables the Previous and Next buttons. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -BINARY_TOC = NO - -# The TOC_EXPAND flag can be set to YES to add extra items for group members to -# the table of contents of the HTML help documentation and to the tree view. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -TOC_EXPAND = NO - -# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and -# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that -# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help -# (.qch) of the generated HTML documentation. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_QHP = NO - -# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify -# the file name of the resulting .qch file. The path specified is relative to -# the HTML output folder. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QCH_FILE = - -# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help -# Project output. For more information please see Qt Help Project / Namespace -# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). -# The default value is: org.doxygen.Project. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_NAMESPACE = org.doxygen.Project - -# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt -# Help Project output. For more information please see Qt Help Project / Virtual -# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- -# folders). -# The default value is: doc. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_VIRTUAL_FOLDER = doc - -# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom -# filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- -# filters). -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_CUST_FILTER_NAME = - -# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the -# custom filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- -# filters). -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_CUST_FILTER_ATTRS = - -# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this -# project's filter section matches. Qt Help Project / Filter Attributes (see: -# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_SECT_FILTER_ATTRS = - -# The QHG_LOCATION tag can be used to specify the location of Qt's -# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the -# generated .qhp file. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHG_LOCATION = - -# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be -# generated, together with the HTML files, they form an Eclipse help plugin. To -# install this plugin and make it available under the help contents menu in -# Eclipse, the contents of the directory containing the HTML and XML files needs -# to be copied into the plugins directory of eclipse. The name of the directory -# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. -# After copying Eclipse needs to be restarted before the help appears. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_ECLIPSEHELP = NO - -# A unique identifier for the Eclipse help plugin. When installing the plugin -# the directory name containing the HTML and XML files should also have this -# name. Each documentation set should have its own identifier. -# The default value is: org.doxygen.Project. -# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. - -ECLIPSE_DOC_ID = org.doxygen.Project - -# If you want full control over the layout of the generated HTML pages it might -# be necessary to disable the index and replace it with your own. The -# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top -# of each HTML page. A value of NO enables the index and the value YES disables -# it. Since the tabs in the index contain the same information as the navigation -# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -DISABLE_INDEX = NO - -# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index -# structure should be generated to display hierarchical information. If the tag -# value is set to YES, a side panel will be generated containing a tree-like -# index structure (just like the one that is generated for HTML Help). For this -# to work a browser that supports JavaScript, DHTML, CSS and frames is required -# (i.e. any modern browser). Windows users are probably better off using the -# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can -# further fine-tune the look of the index. As an example, the default style -# sheet generated by doxygen has an example that shows how to put an image at -# the root of the tree instead of the PROJECT_NAME. Since the tree basically has -# the same information as the tab index, you could consider setting -# DISABLE_INDEX to YES when enabling this option. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_TREEVIEW = NO - -# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that -# doxygen will group on one line in the generated HTML documentation. -# -# Note that a value of 0 will completely suppress the enum values from appearing -# in the overview section. -# Minimum value: 0, maximum value: 20, default value: 4. -# This tag requires that the tag GENERATE_HTML is set to YES. - -ENUM_VALUES_PER_LINE = 4 - -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used -# to set the initial width (in pixels) of the frame in which the tree is shown. -# Minimum value: 0, maximum value: 1500, default value: 250. -# This tag requires that the tag GENERATE_HTML is set to YES. - -TREEVIEW_WIDTH = 250 - -# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to -# external symbols imported via tag files in a separate window. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -EXT_LINKS_IN_WINDOW = NO - -# Use this tag to change the font size of LaTeX formulas included as images in -# the HTML documentation. When you change the font size after a successful -# doxygen run you need to manually remove any form_*.png images from the HTML -# output directory to force them to be regenerated. -# Minimum value: 8, maximum value: 50, default value: 10. -# This tag requires that the tag GENERATE_HTML is set to YES. - -FORMULA_FONTSIZE = 10 - -# Use the FORMULA_TRANPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are not -# supported properly for IE 6.0, but are supported on all modern browsers. -# -# Note that when changing this option you need to delete any form_*.png files in -# the HTML output directory before the changes have effect. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. - -FORMULA_TRANSPARENT = YES - -# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see -# http://www.mathjax.org) which uses client side Javascript for the rendering -# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX -# installed or if you want to formulas look prettier in the HTML output. When -# enabled you may also need to install MathJax separately and configure the path -# to it using the MATHJAX_RELPATH option. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -USE_MATHJAX = NO - -# When MathJax is enabled you can set the default output format to be used for -# the MathJax output. See the MathJax site (see: -# http://docs.mathjax.org/en/latest/output.html) for more details. -# Possible values are: HTML-CSS (which is slower, but has the best -# compatibility), NativeMML (i.e. MathML) and SVG. -# The default value is: HTML-CSS. -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_FORMAT = HTML-CSS - -# When MathJax is enabled you need to specify the location relative to the HTML -# output directory using the MATHJAX_RELPATH option. The destination directory -# should contain the MathJax.js script. For instance, if the mathjax directory -# is located at the same level as the HTML output directory, then -# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax -# Content Delivery Network so you can quickly see the result without installing -# MathJax. However, it is strongly recommended to install a local copy of -# MathJax from http://www.mathjax.org before deployment. -# The default value is: http://cdn.mathjax.org/mathjax/latest. -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest - -# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax -# extension names that should be enabled during MathJax rendering. For example -# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_EXTENSIONS = - -# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces -# of code that will be used on startup of the MathJax code. See the MathJax site -# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an -# example see the documentation. -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_CODEFILE = - -# When the SEARCHENGINE tag is enabled doxygen will generate a search box for -# the HTML output. The underlying search engine uses javascript and DHTML and -# should work on any modern browser. Note that when using HTML help -# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) -# there is already a search function so this one should typically be disabled. -# For large projects the javascript based search engine can be slow, then -# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to -# search using the keyboard; to jump to the search box use + S -# (what the is depends on the OS and browser, but it is typically -# , /