├── .appveyor.yml ├── .clang-format ├── .clang-tidy ├── .cmake-format.yaml ├── .codespell-ignore-lines ├── .gitattributes ├── .github ├── CODEOWNERS ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── bug-report.yml │ └── config.yml ├── dependabot.yml ├── labeler.yml ├── labeler_merged.yml ├── matchers │ └── pylint.json ├── pull_request_template.md └── workflows │ ├── ci.yml │ ├── configure.yml │ ├── docs-link.yml │ ├── format.yml │ ├── labeler.yml │ ├── nightlies.yml │ ├── pip.yml │ ├── reusable-standard.yml │ ├── tests-cibw.yml │ └── upstream.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .readthedocs.yml ├── CMakeLists.txt ├── CMakePresets.json ├── LICENSE ├── README.rst ├── SECURITY.md ├── docs ├── Doxyfile ├── Makefile ├── _static │ └── css │ │ └── custom.css ├── advanced │ ├── cast │ │ ├── chrono.rst │ │ ├── custom.rst │ │ ├── eigen.rst │ │ ├── functional.rst │ │ ├── index.rst │ │ ├── overview.rst │ │ ├── stl.rst │ │ └── strings.rst │ ├── classes.rst │ ├── deadlock.md │ ├── deprecated.rst │ ├── embedding.rst │ ├── exceptions.rst │ ├── functions.rst │ ├── misc.rst │ ├── pycpp │ │ ├── index.rst │ │ ├── numpy.rst │ │ ├── object.rst │ │ └── utilities.rst │ └── smart_ptrs.rst ├── basics.rst ├── benchmark.py ├── benchmark.rst ├── changelog.md ├── classes.rst ├── cmake │ └── index.rst ├── compiling.rst ├── conf.py ├── faq.rst ├── index.rst ├── installing.rst ├── limitations.rst ├── pybind11-logo.png ├── pybind11_vs_boost_python1.png ├── pybind11_vs_boost_python1.svg ├── pybind11_vs_boost_python2.png ├── pybind11_vs_boost_python2.svg ├── reference.rst ├── release.rst ├── requirements.in ├── requirements.txt └── upgrade.rst ├── include └── pybind11 │ ├── attr.h │ ├── buffer_info.h │ ├── cast.h │ ├── chrono.h │ ├── common.h │ ├── complex.h │ ├── conduit │ ├── README.txt │ ├── pybind11_conduit_v1.h │ ├── pybind11_platform_abi_id.h │ └── wrap_include_python_h.h │ ├── critical_section.h │ ├── detail │ ├── class.h │ ├── common.h │ ├── cpp_conduit.h │ ├── descr.h │ ├── dynamic_raw_ptr_cast_if_possible.h │ ├── exception_translation.h │ ├── function_record_pyobject.h │ ├── init.h │ ├── internals.h │ ├── native_enum_data.h │ ├── pybind11_namespace_macros.h │ ├── struct_smart_holder.h │ ├── type_caster_base.h │ ├── typeid.h │ ├── using_smart_holder.h │ └── value_and_holder.h │ ├── eigen.h │ ├── eigen │ ├── common.h │ ├── matrix.h │ └── tensor.h │ ├── embed.h │ ├── eval.h │ ├── functional.h │ ├── gil.h │ ├── gil_safe_call_once.h │ ├── gil_simple.h │ ├── iostream.h │ ├── native_enum.h │ ├── numpy.h │ ├── operators.h │ ├── options.h │ ├── pybind11.h │ ├── pytypes.h │ ├── stl.h │ ├── stl │ └── filesystem.h │ ├── stl_bind.h │ ├── subinterpreter.h │ ├── trampoline_self_life_support.h │ ├── type_caster_pyobject_ptr.h │ ├── typing.h │ └── warnings.h ├── noxfile.py ├── pybind11 ├── __init__.py ├── __main__.py ├── _version.py ├── commands.py ├── py.typed └── setup_helpers.py ├── pyproject.toml ├── tests ├── CMakeLists.txt ├── conftest.py ├── constructor_stats.h ├── cross_module_gil_utils.cpp ├── cross_module_interleaved_error_already_set.cpp ├── custom_exceptions.py ├── eigen_tensor_avoid_stl_array.cpp ├── env.py ├── exo_planet_c_api.cpp ├── exo_planet_pybind11.cpp ├── extra_python_package │ ├── pytest.ini │ └── test_files.py ├── extra_setuptools │ ├── pytest.ini │ └── test_setuphelper.py ├── home_planet_very_lonely_traveler.cpp ├── local_bindings.h ├── mod_per_interpreter_gil.cpp ├── mod_shared_interpreter_gil.cpp ├── object.h ├── pure_cpp │ ├── CMakeLists.txt │ ├── smart_holder_poc.h │ └── smart_holder_poc_test.cpp ├── pybind11_cross_module_tests.cpp ├── pybind11_tests.cpp ├── pybind11_tests.h ├── pyproject.toml ├── pytest.ini ├── requirements.txt ├── test_async.cpp ├── test_async.py ├── test_buffers.cpp ├── test_buffers.py ├── test_builtin_casters.cpp ├── test_builtin_casters.py ├── test_call_policies.cpp ├── test_call_policies.py ├── test_callbacks.cpp ├── test_callbacks.py ├── test_chrono.cpp ├── test_chrono.py ├── test_class.cpp ├── test_class.py ├── test_class_release_gil_before_calling_cpp_dtor.cpp ├── test_class_release_gil_before_calling_cpp_dtor.py ├── test_class_sh_basic.cpp ├── test_class_sh_basic.py ├── test_class_sh_disowning.cpp ├── test_class_sh_disowning.py ├── test_class_sh_disowning_mi.cpp ├── test_class_sh_disowning_mi.py ├── test_class_sh_factory_constructors.cpp ├── test_class_sh_factory_constructors.py ├── test_class_sh_inheritance.cpp ├── test_class_sh_inheritance.py ├── test_class_sh_mi_thunks.cpp ├── test_class_sh_mi_thunks.py ├── test_class_sh_property.cpp ├── test_class_sh_property.py ├── test_class_sh_property_non_owning.cpp ├── test_class_sh_property_non_owning.py ├── test_class_sh_shared_ptr_copy_move.cpp ├── test_class_sh_shared_ptr_copy_move.py ├── test_class_sh_trampoline_basic.cpp ├── test_class_sh_trampoline_basic.py ├── test_class_sh_trampoline_self_life_support.cpp ├── test_class_sh_trampoline_self_life_support.py ├── test_class_sh_trampoline_shared_from_this.cpp ├── test_class_sh_trampoline_shared_from_this.py ├── test_class_sh_trampoline_shared_ptr_cpp_arg.cpp ├── test_class_sh_trampoline_shared_ptr_cpp_arg.py ├── test_class_sh_trampoline_unique_ptr.cpp ├── test_class_sh_trampoline_unique_ptr.py ├── test_class_sh_unique_ptr_custom_deleter.cpp ├── test_class_sh_unique_ptr_custom_deleter.py ├── test_class_sh_unique_ptr_member.cpp ├── test_class_sh_unique_ptr_member.py ├── test_class_sh_virtual_py_cpp_mix.cpp ├── test_class_sh_virtual_py_cpp_mix.py ├── test_cmake_build │ ├── CMakeLists.txt │ ├── embed.cpp │ ├── installed_embed │ │ └── CMakeLists.txt │ ├── installed_function │ │ └── CMakeLists.txt │ ├── installed_target │ │ └── CMakeLists.txt │ ├── main.cpp │ ├── subdirectory_embed │ │ └── CMakeLists.txt │ ├── subdirectory_function │ │ └── CMakeLists.txt │ ├── subdirectory_target │ │ └── CMakeLists.txt │ └── test.py ├── test_const_name.cpp ├── test_const_name.py ├── test_constants_and_functions.cpp ├── test_constants_and_functions.py ├── test_copy_move.cpp ├── test_copy_move.py ├── test_cpp_conduit.cpp ├── test_cpp_conduit.py ├── test_cpp_conduit_traveler_bindings.h ├── test_cpp_conduit_traveler_types.h ├── test_custom_type_casters.cpp ├── test_custom_type_casters.py ├── test_custom_type_setup.cpp ├── test_custom_type_setup.py ├── test_docs_advanced_cast_custom.cpp ├── test_docs_advanced_cast_custom.py ├── test_docstring_options.cpp ├── test_docstring_options.py ├── test_eigen_matrix.cpp ├── test_eigen_matrix.py ├── test_eigen_tensor.cpp ├── test_eigen_tensor.inl ├── test_eigen_tensor.py ├── test_embed │ ├── CMakeLists.txt │ ├── catch.cpp │ ├── external_module.cpp │ ├── test_interpreter.cpp │ ├── test_interpreter.py │ ├── test_subinterpreter.cpp │ └── test_trampoline.py ├── test_enum.cpp ├── test_enum.py ├── test_eval.cpp ├── test_eval.py ├── test_eval_call.py ├── test_exceptions.cpp ├── test_exceptions.h ├── test_exceptions.py ├── test_factory_constructors.cpp ├── test_factory_constructors.py ├── test_gil_scoped.cpp ├── test_gil_scoped.py ├── test_iostream.cpp ├── test_iostream.py ├── test_kwargs_and_defaults.cpp ├── test_kwargs_and_defaults.py ├── test_local_bindings.cpp ├── test_local_bindings.py ├── test_methods_and_attributes.cpp ├── test_methods_and_attributes.py ├── test_modules.cpp ├── test_modules.py ├── test_multiple_inheritance.cpp ├── test_multiple_inheritance.py ├── test_multiple_interpreters.py ├── test_native_enum.cpp ├── test_native_enum.py ├── test_numpy_array.cpp ├── test_numpy_array.py ├── test_numpy_dtypes.cpp ├── test_numpy_dtypes.py ├── test_numpy_vectorize.cpp ├── test_numpy_vectorize.py ├── test_opaque_types.cpp ├── test_opaque_types.py ├── test_operator_overloading.cpp ├── test_operator_overloading.py ├── test_pickling.cpp ├── test_pickling.py ├── test_potentially_slicing_weak_ptr.cpp ├── test_potentially_slicing_weak_ptr.py ├── test_python_multiple_inheritance.cpp ├── test_python_multiple_inheritance.py ├── test_pytypes.cpp ├── test_pytypes.py ├── test_sequences_and_iterators.cpp ├── test_sequences_and_iterators.py ├── test_smart_ptr.cpp ├── test_smart_ptr.py ├── test_stl.cpp ├── test_stl.py ├── test_stl_binders.cpp ├── test_stl_binders.py ├── test_tagbased_polymorphic.cpp ├── test_tagbased_polymorphic.py ├── test_thread.cpp ├── test_thread.py ├── test_type_caster_pyobject_ptr.cpp ├── test_type_caster_pyobject_ptr.py ├── test_type_caster_std_function_specializations.cpp ├── test_type_caster_std_function_specializations.py ├── test_union.cpp ├── test_union.py ├── test_unnamed_namespace_a.cpp ├── test_unnamed_namespace_a.py ├── test_unnamed_namespace_b.cpp ├── test_unnamed_namespace_b.py ├── test_vector_unique_ptr_member.cpp ├── test_vector_unique_ptr_member.py ├── test_virtual_functions.cpp ├── test_virtual_functions.py ├── test_warnings.cpp ├── test_warnings.py ├── valgrind-numpy-scipy.supp └── valgrind-python.supp └── tools ├── FindCatch.cmake ├── FindEigen3.cmake ├── FindPythonLibsNew.cmake ├── JoinPaths.cmake ├── check-style.sh ├── cmake_uninstall.cmake.in ├── codespell_ignore_lines_from_errors.py ├── libsize.py ├── make_changelog.py ├── make_global.py ├── pybind11.pc.in ├── pybind11Common.cmake ├── pybind11Config.cmake.in ├── pybind11GuessPythonExtSuffix.cmake ├── pybind11NewTools.cmake ├── pybind11Tools.cmake └── test-pybind11GuessPythonExtSuffix.cmake /.appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 1.0.{build} 2 | image: 3 | - Visual Studio 2017 4 | test: off 5 | skip_branch_with_pr: true 6 | build: 7 | parallel: true 8 | platform: 9 | - x86 10 | environment: 11 | matrix: 12 | - PYTHON: 38 13 | CONFIG: Debug 14 | install: 15 | - ps: | 16 | $env:CMAKE_GENERATOR = "Visual Studio 15 2017" 17 | if ($env:PLATFORM -eq "x64") { $env:PYTHON = "$env:PYTHON-x64" } 18 | $env:PATH = "C:\Python$env:PYTHON\;C:\Python$env:PYTHON\Scripts\;$env:PATH" 19 | python -W ignore -m pip install --upgrade pip wheel 20 | python -W ignore -m pip install pytest numpy --no-warn-script-location pytest-timeout 21 | - ps: | 22 | Start-FileDownload 'https://gitlab.com/libeigen/eigen/-/archive/3.3.7/eigen-3.3.7.zip' 23 | 7z x eigen-3.3.7.zip -y > $null 24 | $env:CMAKE_INCLUDE_PATH = "eigen-3.3.7;$env:CMAKE_INCLUDE_PATH" 25 | build_script: 26 | - cmake -G "%CMAKE_GENERATOR%" -A "%CMAKE_ARCH%" 27 | -DCMAKE_CXX_STANDARD=14 28 | -DPYBIND11_WERROR=ON 29 | -DDOWNLOAD_CATCH=ON 30 | -DCMAKE_SUPPRESS_REGENERATION=1 31 | . 32 | - set MSBuildLogger="C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" 33 | - cmake --build . --config %CONFIG% --target pytest -- /m /v:m /logger:%MSBuildLogger% 34 | - cmake --build . --config %CONFIG% --target cpptest -- /m /v:m /logger:%MSBuildLogger% 35 | on_failure: if exist "tests\test_cmake_build" type tests\test_cmake_build\*.log* 36 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | # See all possible options and defaults with: 3 | # clang-format --style=llvm --dump-config 4 | BasedOnStyle: LLVM 5 | AccessModifierOffset: -4 6 | AllowShortLambdasOnASingleLine: true 7 | AlwaysBreakTemplateDeclarations: Yes 8 | BinPackArguments: false 9 | BinPackParameters: false 10 | BreakBeforeBinaryOperators: All 11 | BreakConstructorInitializers: BeforeColon 12 | ColumnLimit: 99 13 | CommentPragmas: 'NOLINT:.*|^ IWYU pragma:' 14 | IncludeBlocks: Regroup 15 | IndentCaseLabels: true 16 | IndentPPDirectives: AfterHash 17 | IndentWidth: 4 18 | Language: Cpp 19 | SpaceAfterCStyleCast: true 20 | Standard: Cpp11 21 | StatementMacros: ['PyObject_HEAD'] 22 | TabWidth: 4 23 | IncludeCategories: 24 | - Regex: '' 35 | Priority: 4 36 | - Regex: '.*' 37 | Priority: 5 38 | ... 39 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | FormatStyle: file 2 | 3 | Checks: | 4 | *bugprone*, 5 | *performance*, 6 | clang-analyzer-optin.cplusplus.VirtualCall, 7 | clang-analyzer-optin.performance.Padding, 8 | cppcoreguidelines-init-variables, 9 | cppcoreguidelines-prefer-member-initializer, 10 | cppcoreguidelines-pro-type-static-cast-downcast, 11 | cppcoreguidelines-slicing, 12 | google-explicit-constructor, 13 | llvm-namespace-comment, 14 | misc-definitions-in-headers, 15 | misc-misplaced-const, 16 | misc-non-copyable-objects, 17 | misc-static-assert, 18 | misc-throw-by-value-catch-by-reference, 19 | misc-uniqueptr-reset-release, 20 | misc-unused-parameters, 21 | modernize-avoid-bind, 22 | modernize-loop-convert, 23 | modernize-make-shared, 24 | modernize-redundant-void-arg, 25 | modernize-replace-auto-ptr, 26 | modernize-replace-disallow-copy-and-assign-macro, 27 | modernize-replace-random-shuffle, 28 | modernize-shrink-to-fit, 29 | modernize-use-auto, 30 | modernize-use-bool-literals, 31 | modernize-use-default-member-init, 32 | modernize-use-emplace, 33 | modernize-use-equals-default, 34 | modernize-use-equals-delete, 35 | modernize-use-noexcept, 36 | modernize-use-nullptr, 37 | modernize-use-override, 38 | modernize-use-using, 39 | readability-avoid-const-params-in-decls, 40 | readability-braces-around-statements, 41 | readability-const-return-type, 42 | readability-container-size-empty, 43 | readability-delete-null-pointer, 44 | readability-else-after-return, 45 | readability-implicit-bool-conversion, 46 | readability-inconsistent-declaration-parameter-name, 47 | readability-make-member-function-const, 48 | readability-misplaced-array-index, 49 | readability-non-const-parameter, 50 | readability-qualified-auto, 51 | readability-redundant-function-ptr-dereference, 52 | readability-redundant-smartptr-get, 53 | readability-redundant-string-cstr, 54 | readability-simplify-subscript-expr, 55 | readability-static-accessed-through-instance, 56 | readability-static-definition-in-anonymous-namespace, 57 | readability-string-compare, 58 | readability-suspicious-call-argument, 59 | readability-uniqueptr-delete-release, 60 | -bugprone-chained-comparison, 61 | -bugprone-easily-swappable-parameters, 62 | -bugprone-exception-escape, 63 | -bugprone-reserved-identifier, 64 | -bugprone-unused-raii, 65 | -performance-enum-size, 66 | -performance-inefficient-string-concatenation, 67 | 68 | CheckOptions: 69 | - key: modernize-use-equals-default.IgnoreMacros 70 | value: false 71 | - key: performance-for-range-copy.WarnOnAllAutoCopies 72 | value: true 73 | - key: performance-inefficient-string-concatenation.StrictMode 74 | value: true 75 | - key: performance-unnecessary-value-param.AllowedTypes 76 | value: 'exception_ptr$;' 77 | - key: readability-implicit-bool-conversion.AllowPointerConditions 78 | value: true 79 | 80 | HeaderFilterRegex: 'pybind11/.*h' 81 | -------------------------------------------------------------------------------- /.cmake-format.yaml: -------------------------------------------------------------------------------- 1 | parse: 2 | additional_commands: 3 | pybind11_add_module: 4 | flags: 5 | - THIN_LTO 6 | - MODULE 7 | - SHARED 8 | - NO_EXTRAS 9 | - EXCLUDE_FROM_ALL 10 | - SYSTEM 11 | 12 | format: 13 | line_width: 99 14 | tab_size: 2 15 | 16 | # If an argument group contains more than this many sub-groups 17 | # (parg or kwarg groups) then force it to a vertical layout. 18 | max_subgroups_hwrap: 2 19 | 20 | # If a positional argument group contains more than this many 21 | # arguments, then force it to a vertical layout. 22 | max_pargs_hwrap: 6 23 | 24 | # If a cmdline positional group consumes more than this many 25 | # lines without nesting, then invalidate the layout (and nest) 26 | max_rows_cmdline: 2 27 | separate_ctrl_name_with_space: false 28 | separate_fn_name_with_space: false 29 | dangle_parens: false 30 | 31 | # If the trailing parenthesis must be 'dangled' on its on 32 | # 'line, then align it to this reference: `prefix`: the start' 33 | # 'of the statement, `prefix-indent`: the start of the' 34 | # 'statement, plus one indentation level, `child`: align to' 35 | # the column of the arguments 36 | dangle_align: prefix 37 | # If the statement spelling length (including space and 38 | # parenthesis) is smaller than this amount, then force reject 39 | # nested layouts. 40 | min_prefix_chars: 4 41 | 42 | # If the statement spelling length (including space and 43 | # parenthesis) is larger than the tab width by more than this 44 | # amount, then force reject un-nested layouts. 45 | max_prefix_chars: 10 46 | 47 | # If a candidate layout is wrapped horizontally but it exceeds 48 | # this many lines, then reject the layout. 49 | max_lines_hwrap: 2 50 | 51 | line_ending: unix 52 | 53 | # Format command names consistently as 'lower' or 'upper' case 54 | command_case: canonical 55 | 56 | # Format keywords consistently as 'lower' or 'upper' case 57 | # unchanged is valid too 58 | keyword_case: 'upper' 59 | 60 | # A list of command names which should always be wrapped 61 | always_wrap: [] 62 | 63 | # If true, the argument lists which are known to be sortable 64 | # will be sorted lexicographically 65 | enable_sort: true 66 | 67 | # If true, the parsers may infer whether or not an argument 68 | # list is sortable (without annotation). 69 | autosort: false 70 | 71 | # Causes a few issues - can be solved later, possibly. 72 | markup: 73 | enable_markup: false 74 | -------------------------------------------------------------------------------- /.codespell-ignore-lines: -------------------------------------------------------------------------------- 1 | template 2 | template 3 | auto &this_ = static_cast(*this); 4 | if (load_impl(temp, false)) { 5 | ssize_t nd = 0; 6 | auto trivial = broadcast(buffers, nd, shape); 7 | auto ndim = (size_t) nd; 8 | int nd; 9 | ssize_t ndim() const { return detail::array_proxy(m_ptr)->nd; } 10 | using op = op_impl; 11 | template 12 | template 13 | class_ &def(const detail::op_ &op, const Extra &...extra) { 14 | class_ &def_cast(const detail::op_ &op, const Extra &...extra) { 15 | int valu; 16 | explicit movable_int(int v) : valu{v} {} 17 | movable_int(movable_int &&other) noexcept : valu(other.valu) { other.valu = 91; } 18 | explicit indestructible_int(int v) : valu{v} {} 19 | REQUIRE(hld.as_raw_ptr_unowned()->valu == 19); 20 | REQUIRE(othr.valu == 19); 21 | REQUIRE(orig.valu == 91); 22 | (m.pass_valu, "Valu", "pass_valu:Valu(_MvCtor)*_CpCtor"), 23 | atyp_valu rtrn_valu() { atyp_valu obj{"Valu"}; return obj; } 24 | assert m.atyp_valu().get_mtxt() == "Valu" 25 | // valu(e), ref(erence), ptr or p (pointer), r = rvalue, m = mutable, c = const, 26 | @pytest.mark.parametrize("access", ["ro", "rw", "static_ro", "static_rw"]) 27 | struct IntStruct { 28 | explicit IntStruct(int v) : value(v){}; 29 | ~IntStruct() { value = -value; } 30 | IntStruct(const IntStruct &) = default; 31 | IntStruct &operator=(const IntStruct &) = default; 32 | py::class_(m, "IntStruct").def(py::init([](const int i) { return IntStruct(i); })); 33 | py::implicitly_convertible(); 34 | m.def("test", [](int expected, const IntStruct &in) { 35 | [](int expected, const IntStruct &in) { 36 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | docs/*.svg binary 2 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | *.cmake @henryiii 2 | CMakeLists.txt @henryiii 3 | *.yml @henryiii 4 | *.yaml @henryiii 5 | /tools/ @henryiii 6 | /pybind11/ @henryiii 7 | noxfile.py @henryiii 8 | .clang-format @henryiii 9 | .clang-tidy @henryiii 10 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: File an issue about a bug 3 | title: "[BUG]: " 4 | labels: [triage] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Please do your best to make the issue as easy to act on as possible, and only submit here if there is clearly a problem with pybind11 (ask first if unsure). **Note that a reproducer in a PR is much more likely to get immediate attention.** 10 | 11 | - type: checkboxes 12 | id: steps 13 | attributes: 14 | label: Required prerequisites 15 | description: Make sure you've completed the following steps before submitting your issue -- thank you! 16 | options: 17 | - label: Make sure you've read the [documentation](https://pybind11.readthedocs.io). Your issue may be addressed there. 18 | required: true 19 | - label: Search the [issue tracker](https://github.com/pybind/pybind11/issues) and [Discussions](https:/pybind/pybind11/discussions) to verify that this hasn't already been reported. +1 or comment there if it has. 20 | required: true 21 | - label: Consider asking first in the [Gitter chat room](https://gitter.im/pybind/Lobby) or in a [Discussion](https:/pybind/pybind11/discussions/new). 22 | required: false 23 | 24 | - type: input 25 | id: version 26 | attributes: 27 | label: What version (or hash if on master) of pybind11 are you using? 28 | validations: 29 | required: true 30 | 31 | - type: textarea 32 | id: description 33 | attributes: 34 | label: Problem description 35 | placeholder: >- 36 | Provide a short description, state the expected behavior and what 37 | actually happens. Include relevant information like what version of 38 | pybind11 you are using, what system you are on, and any useful commands 39 | / output. 40 | validations: 41 | required: true 42 | 43 | - type: textarea 44 | id: code 45 | attributes: 46 | label: Reproducible example code 47 | placeholder: >- 48 | The code should be minimal, have no external dependencies, isolate the 49 | function(s) that cause breakage. Submit matched and complete C++ and 50 | Python snippets that can be easily compiled and run to diagnose the 51 | issue. — Note that a reproducer in a PR is much more likely to get 52 | immediate attention: failing tests in the pybind11 CI are the best 53 | starting point for working out fixes. 54 | render: text 55 | 56 | - type: input 57 | id: regression 58 | attributes: 59 | label: Is this a regression? Put the last known working version here if it is. 60 | description: Put the last known working version here if this is a regression. 61 | value: Not a regression 62 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Ask a question 4 | url: https://github.com/pybind/pybind11/discussions/new 5 | about: Please ask and answer questions here, or propose new ideas. 6 | - name: Gitter room 7 | url: https://gitter.im/pybind/Lobby 8 | about: A room for discussing pybind11 with an active community 9 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # Maintain dependencies for GitHub Actions 4 | - package-ecosystem: "github-actions" 5 | directory: "/" 6 | schedule: 7 | interval: "weekly" 8 | groups: 9 | actions: 10 | patterns: 11 | - "*" 12 | ignore: 13 | - dependency-name: actions/checkout 14 | versions: 15 | - "<5" 16 | -------------------------------------------------------------------------------- /.github/labeler.yml: -------------------------------------------------------------------------------- 1 | docs: 2 | all: 3 | - changed-files: 4 | - all-globs-to-all-files: 5 | - '!docs/changelog.md' 6 | - '!docs/upgrade.rst' 7 | - base-branch: "^(?!dependabot).*" 8 | - base-branch: "^(?!pre-commit-ci).*" 9 | 10 | ci: 11 | - changed-files: 12 | - any-glob-to-any-file: 13 | - '.github/workflows/*.yml' 14 | -------------------------------------------------------------------------------- /.github/labeler_merged.yml: -------------------------------------------------------------------------------- 1 | # Add 'needs changelog` label to any change to code files as long as the `CHANGELOG` hasn't changed 2 | # Skip dependabot and pre-commit-ci PRs 3 | needs changelog: 4 | - all: 5 | - changed-files: 6 | - all-globs-to-all-files: "!docs/changelog.md" 7 | - base-branch: "^(?!dependabot).*" 8 | - base-branch: "^(?!pre-commit-ci).*" 9 | -------------------------------------------------------------------------------- /.github/matchers/pylint.json: -------------------------------------------------------------------------------- 1 | { 2 | "problemMatcher": [ 3 | { 4 | "severity": "warning", 5 | "pattern": [ 6 | { 7 | "regexp": "^([^:]+):(\\d+):(\\d+): ([A-DF-Z]\\d+): \\033\\[[\\d;]+m([^\\033]+).*$", 8 | "file": 1, 9 | "line": 2, 10 | "column": 3, 11 | "code": 4, 12 | "message": 5 13 | } 14 | ], 15 | "owner": "pylint-warning" 16 | }, 17 | { 18 | "severity": "error", 19 | "pattern": [ 20 | { 21 | "regexp": "^([^:]+):(\\d+):(\\d+): (E\\d+): \\033\\[[\\d;]+m([^\\033]+).*$", 22 | "file": 1, 23 | "line": 2, 24 | "column": 3, 25 | "code": 4, 26 | "message": 5 27 | } 28 | ], 29 | "owner": "pylint-error" 30 | } 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 5 | ## Description 6 | 7 | 8 | 9 | 10 | ## Suggested changelog entry: 11 | 12 | 14 | 15 | * Placeholder. 16 | -------------------------------------------------------------------------------- /.github/workflows/configure.yml: -------------------------------------------------------------------------------- 1 | name: Config 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | types: 7 | - opened 8 | - synchronize 9 | - reopened 10 | - ready_for_review 11 | push: 12 | branches: 13 | - master 14 | - stable 15 | - v* 16 | 17 | permissions: 18 | contents: read 19 | 20 | jobs: 21 | # This tests various versions of CMake in various combinations, to make sure 22 | # the configure step passes. 23 | cmake: 24 | if: github.event.pull_request.draft == false 25 | strategy: 26 | fail-fast: false 27 | matrix: 28 | include: 29 | - runs-on: ubuntu-22.04 30 | cmake: "3.15" 31 | 32 | - runs-on: ubuntu-24.04 33 | cmake: "3.26" 34 | 35 | - runs-on: ubuntu-24.04 36 | cmake: "3.29" 37 | 38 | - runs-on: macos-13 39 | cmake: "3.15" 40 | 41 | - runs-on: macos-14 42 | cmake: "4.0" 43 | 44 | - runs-on: windows-2019 45 | cmake: "3.18" 46 | 47 | - runs-on: windows-latest 48 | cmake: "4.0" 49 | 50 | name: 🐍 3.11 • CMake ${{ matrix.cmake }} • ${{ matrix.runs-on }} 51 | runs-on: ${{ matrix.runs-on }} 52 | 53 | steps: 54 | - uses: actions/checkout@v4 55 | 56 | - name: Setup Python 3.11 57 | uses: actions/setup-python@v5 58 | with: 59 | python-version: 3.11 60 | 61 | - name: Install uv 62 | uses: astral-sh/setup-uv@v6 63 | 64 | - name: Prepare env 65 | run: uv pip install --python=python --system -r tests/requirements.txt 66 | 67 | # An action for adding a specific version of CMake: 68 | # https://github.com/jwlawson/actions-setup-cmake 69 | - name: Setup CMake ${{ matrix.cmake }} 70 | uses: jwlawson/actions-setup-cmake@v2.0 71 | with: 72 | cmake-version: ${{ matrix.cmake }} 73 | 74 | # These steps use a directory with a space in it intentionally 75 | - name: Configure 76 | shell: bash 77 | run: cmake -S. -B"build dir" -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON 78 | 79 | # Only build and test if this was manually triggered in the GitHub UI 80 | - name: Build 81 | working-directory: build dir 82 | if: github.event_name == 'workflow_dispatch' 83 | run: cmake --build . --config Release 84 | 85 | - name: Test 86 | working-directory: build dir 87 | if: github.event_name == 'workflow_dispatch' 88 | run: cmake --build . --config Release --target check 89 | -------------------------------------------------------------------------------- /.github/workflows/docs-link.yml: -------------------------------------------------------------------------------- 1 | name: Read the Docs PR preview 2 | 3 | on: 4 | pull_request_target: 5 | types: 6 | - opened 7 | - synchronize 8 | 9 | permissions: 10 | contents: read 11 | pull-requests: write 12 | 13 | concurrency: 14 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 15 | cancel-in-progress: true 16 | 17 | jobs: 18 | documentation-links: 19 | runs-on: ubuntu-latest 20 | if: github.event.repository.fork == false 21 | steps: 22 | - uses: actions/checkout@v4 23 | 24 | - name: Check for docs changes 25 | id: docs_changes 26 | run: | 27 | # Fetch the PR head 28 | git fetch origin pull/${{ github.event.pull_request.number }}/head:pr-head 29 | 30 | # Show diff between base (current checkout) and PR head 31 | if git diff --name-only HEAD pr-head | grep -q '^docs/'; then 32 | echo "docs_changed=true" >> "$GITHUB_OUTPUT" 33 | else 34 | echo "docs_changed=false" >> "$GITHUB_OUTPUT" 35 | fi 36 | 37 | - uses: readthedocs/actions/preview@v1 38 | if: steps.docs_changes.outputs.docs_changed == 'true' 39 | with: 40 | project-slug: "pybind11" 41 | single-version: "true" 42 | -------------------------------------------------------------------------------- /.github/workflows/format.yml: -------------------------------------------------------------------------------- 1 | # This is a format job. Pre-commit has a first-party GitHub action, so we use 2 | # that: https://github.com/pre-commit/action 3 | 4 | name: Format 5 | 6 | on: 7 | workflow_dispatch: 8 | pull_request: 9 | push: 10 | branches: 11 | - master 12 | - stable 13 | - "v*" 14 | 15 | permissions: 16 | contents: read 17 | 18 | env: 19 | FORCE_COLOR: 3 20 | # For cmake: 21 | VERBOSE: 1 22 | 23 | jobs: 24 | pre-commit: 25 | name: Format 26 | runs-on: ubuntu-latest 27 | steps: 28 | - uses: actions/checkout@v4 29 | - uses: actions/setup-python@v5 30 | with: 31 | python-version: "3.x" 32 | - name: Add matchers 33 | run: echo "::add-matcher::$GITHUB_WORKSPACE/.github/matchers/pylint.json" 34 | - uses: pre-commit/action@v3.0.1 35 | 36 | clang-tidy: 37 | # When making changes here, please also review the "Clang-Tidy" section 38 | # in .github/CONTRIBUTING.md and update as needed. 39 | name: Clang-Tidy 40 | runs-on: ubuntu-latest 41 | container: silkeh/clang:20 42 | steps: 43 | - uses: actions/checkout@v4 44 | 45 | - name: Install requirements 46 | run: apt-get update && apt-get install -y git python3-dev python3-pytest ninja-build 47 | 48 | - name: Configure 49 | run: cmake --preset tidy 50 | - name: Build 51 | run: cmake --build --preset tidy 52 | 53 | - name: Embedded 54 | run: cmake --build --preset tidy -t cpptest 55 | -------------------------------------------------------------------------------- /.github/workflows/labeler.yml: -------------------------------------------------------------------------------- 1 | name: Labeler 2 | on: 3 | pull_request_target: 4 | types: [closed] 5 | 6 | permissions: {} 7 | 8 | jobs: 9 | label: 10 | name: Labeler 11 | runs-on: ubuntu-latest 12 | permissions: 13 | contents: read 14 | pull-requests: write 15 | steps: 16 | 17 | - uses: actions/labeler@v5 18 | if: > 19 | github.event.pull_request.merged == true && 20 | !startsWith(github.event.pull_request.title, 'chore(deps):') && 21 | !startsWith(github.event.pull_request.title, 'ci(fix):') && 22 | !startsWith(github.event.pull_request.title, 'docs(changelog):') 23 | with: 24 | repo-token: ${{ secrets.GITHUB_TOKEN }} 25 | configuration-path: .github/labeler_merged.yml 26 | -------------------------------------------------------------------------------- /.github/workflows/nightlies.yml: -------------------------------------------------------------------------------- 1 | name: Upload nightly wheels to Anaconda Cloud 2 | 3 | on: 4 | # Run daily at 2:34 UTC to upload nightly wheels to Anaconda Cloud 5 | schedule: 6 | - cron: "34 2 * * *" 7 | # Run on demand with workflow dispatch 8 | workflow_dispatch: 9 | 10 | permissions: 11 | actions: read 12 | 13 | concurrency: 14 | group: ${{ github.workflow }}-${{ github.ref }} 15 | cancel-in-progress: true 16 | 17 | jobs: 18 | build_wheel: 19 | name: Build and upload wheel 20 | if: github.repository_owner == 'pybind' 21 | runs-on: ubuntu-latest 22 | steps: 23 | - uses: actions/checkout@v4 24 | with: 25 | fetch-depth: 0 26 | 27 | - name: Install uv 28 | uses: astral-sh/setup-uv@v6 29 | 30 | - name: Build SDist and wheels 31 | run: | 32 | uv tool install nox 33 | nox -s build 34 | nox -s build_global 35 | 36 | - uses: actions/upload-artifact@v4 37 | with: 38 | name: Packages 39 | path: dist/* 40 | 41 | upload_nightly_wheels: 42 | name: Upload nightly wheels to Anaconda Cloud 43 | if: github.repository_owner == 'pybind' 44 | needs: [build_wheel] 45 | runs-on: ubuntu-latest 46 | steps: 47 | - uses: actions/download-artifact@v4 48 | with: 49 | name: Packages 50 | path: dist 51 | 52 | - name: List wheel to be deployed 53 | run: ls -lha dist/*.whl 54 | 55 | - name: Upload wheel to Anaconda Cloud as nightly 56 | uses: scientific-python/upload-nightly-action@b36e8c0c10dbcfd2e05bf95f17ef8c14fd708dbf # 0.6.2 57 | with: 58 | artifacts_path: dist 59 | anaconda_nightly_upload_token: ${{ secrets.ANACONDA_ORG_UPLOAD_TOKEN }} 60 | -------------------------------------------------------------------------------- /.github/workflows/reusable-standard.yml: -------------------------------------------------------------------------------- 1 | name: Reusable Standard Test 2 | 3 | on: 4 | workflow_call: 5 | inputs: 6 | python-version: 7 | required: true 8 | type: string 9 | cmake-args: 10 | required: false 11 | type: string 12 | default: '' 13 | runs-on: 14 | required: true 15 | type: string 16 | 17 | jobs: 18 | standard: 19 | name: 🧪 20 | runs-on: ${{ inputs.runs-on }} 21 | 22 | steps: 23 | - uses: actions/checkout@v4 24 | 25 | - name: Setup Python ${{ inputs.python-version }} 26 | uses: actions/setup-python@v5 27 | with: 28 | python-version: ${{ inputs.python-version }} 29 | allow-prereleases: true 30 | 31 | - name: Setup Boost (Linux) 32 | if: runner.os == 'Linux' 33 | run: sudo apt-get install libboost-dev 34 | 35 | - name: Setup Boost (macOS) 36 | if: runner.os == 'macOS' 37 | run: brew install boost 38 | 39 | - name: Install uv 40 | uses: astral-sh/setup-uv@v6 41 | with: 42 | enable-cache: true 43 | 44 | - name: Prepare env 45 | run: uv pip install --python=python --system -r tests/requirements.txt 46 | 47 | - name: Setup annotations on Linux 48 | if: runner.os == 'Linux' 49 | run: uv pip install --python=python --system pytest-github-actions-annotate-failures 50 | 51 | # TODO Resolve Windows Ninja shared object issue on Python 3.8+ 52 | - name: Use Ninja except on Windows 53 | if: runner.os != 'Windows' 54 | run: echo "CMAKE_GENERATOR=Ninja" >> "$GITHUB_ENV" 55 | 56 | - name: Configure 57 | run: > 58 | cmake -S. -Bbuild -Werror=dev 59 | -DPYBIND11_WERROR=ON 60 | -DPYBIND11_PYTEST_ARGS=-v 61 | -DDOWNLOAD_CATCH=ON 62 | -DDOWNLOAD_EIGEN=ON 63 | ${{ inputs.cmake-args }} 64 | 65 | - name: Build 66 | run: cmake --build build 67 | 68 | - name: Python tests 69 | run: cmake --build build --target pytest 70 | 71 | - name: C++ tests 72 | run: cmake --build build --target cpptest 73 | 74 | - name: Interface test 75 | run: cmake --build build --target test_cmake_build 76 | 77 | - name: Setuptools helpers test 78 | run: | 79 | uv pip install --python=python --system setuptools 80 | pytest tests/extra_setuptools 81 | -------------------------------------------------------------------------------- /.github/workflows/tests-cibw.yml: -------------------------------------------------------------------------------- 1 | name: CIBW 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | branches: 7 | - master 8 | - stable 9 | - v* 10 | 11 | concurrency: 12 | group: ${{ github.workflow }}-${{ github.ref }} 13 | cancel-in-progress: true 14 | 15 | jobs: 16 | build-wasm-emscripten: 17 | name: Pyodide wheel 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/checkout@v4 21 | with: 22 | submodules: true 23 | fetch-depth: 0 24 | 25 | - uses: pypa/cibuildwheel@c90accef518b1dd0253bf43b639ce21f765d6794 26 | env: 27 | PYODIDE_BUILD_EXPORTS: whole_archive 28 | with: 29 | package-dir: tests 30 | only: cp312-pyodide_wasm32 31 | 32 | build-ios: 33 | name: iOS wheel 34 | runs-on: macos-latest 35 | steps: 36 | - uses: actions/checkout@v4 37 | with: 38 | submodules: true 39 | fetch-depth: 0 40 | 41 | - run: brew upgrade cmake 42 | 43 | - uses: pypa/cibuildwheel@c90accef518b1dd0253bf43b639ce21f765d6794 44 | env: 45 | CIBW_PLATFORM: ios 46 | with: 47 | package-dir: tests 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | CMakeCache.txt 2 | CMakeFiles 3 | Makefile 4 | cmake_install.cmake 5 | cmake_uninstall.cmake 6 | .DS_Store 7 | *.so 8 | *.pyd 9 | *.dll 10 | *.sln 11 | *.sdf 12 | *.opensdf 13 | *.vcxproj 14 | *.vcxproj.user 15 | *.filters 16 | example.dir 17 | Win32 18 | x64 19 | Release 20 | Debug 21 | .vs 22 | CTestTestfile.cmake 23 | Testing 24 | autogen 25 | MANIFEST 26 | /.ninja_* 27 | /*.ninja 28 | /docs/.build 29 | *.py[co] 30 | *.egg-info 31 | *~ 32 | .*.swp 33 | .DS_Store 34 | /dist 35 | /*build* 36 | .cache/ 37 | sosize-*.txt 38 | pybind11Config*.cmake 39 | pybind11Targets.cmake 40 | /*env* 41 | /.vscode 42 | /pybind11/include/* 43 | /pybind11/share/* 44 | /docs/_build/* 45 | .ipynb_checkpoints/ 46 | tests/main.cpp 47 | CMakeUserPresents.json 48 | 49 | /Python 50 | /tmp* 51 | .ruby-version 52 | .*cache*/ 53 | *.lock 54 | -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | # https://blog.readthedocs.com/migrate-configuration-v2/ 2 | 3 | version: 2 4 | 5 | build: 6 | os: ubuntu-22.04 7 | apt_packages: 8 | - librsvg2-bin 9 | tools: 10 | python: "3.11" 11 | 12 | sphinx: 13 | configuration: docs/conf.py 14 | 15 | python: 16 | install: 17 | - requirements: docs/requirements.txt 18 | 19 | formats: 20 | - pdf 21 | -------------------------------------------------------------------------------- /CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 6, 3 | "configurePresets": [ 4 | { 5 | "name": "default", 6 | "displayName": "Default", 7 | "binaryDir": "build", 8 | "generator": "Ninja", 9 | "errors": { 10 | "dev": true, 11 | "deprecated": true 12 | }, 13 | "cacheVariables": { 14 | "CMAKE_BUILD_TYPE": "Debug", 15 | "CMAKE_EXPORT_COMPILE_COMMANDS": true, 16 | "DOWNLOAD_CATCH": true, 17 | "DOWNLOAD_EIGEN": true, 18 | "PYBIND11_FINDPYTHON": "NEW", 19 | "PYBIND11_WERROR": true, 20 | "CMAKE_COLOR_DIAGNOSTICS": true 21 | } 22 | }, 23 | { 24 | "name": "venv", 25 | "displayName": "Venv", 26 | "inherits": "default", 27 | "cacheVariables": { 28 | "PYBIND11_CREATE_WITH_UV": "python3", 29 | "Python_ROOT_DIR": ".venv" 30 | } 31 | }, 32 | { 33 | "name": "tidy", 34 | "displayName": "Clang-tidy", 35 | "inherits": "default", 36 | "binaryDir": "build-tidy", 37 | "cacheVariables": { 38 | "CMAKE_CXX_CLANG_TIDY": "clang-tidy;--use-color;--warnings-as-errors=*", 39 | "CMAKE_CXX_STANDARD": "17" 40 | } 41 | } 42 | ], 43 | "buildPresets": [ 44 | { 45 | "name": "default", 46 | "displayName": "Default Build", 47 | "configurePreset": "default" 48 | }, 49 | { 50 | "name": "venv", 51 | "displayName": "Venv Build", 52 | "configurePreset": "venv" 53 | }, 54 | { 55 | "name": "tidy", 56 | "displayName": "Clang-tidy Build", 57 | "configurePreset": "tidy", 58 | "nativeToolOptions": ["-k0"] 59 | }, 60 | { 61 | "name": "tests", 62 | "displayName": "Tests (for workflow)", 63 | "configurePreset": "default", 64 | "targets": ["pytest", "cpptest", "test_cmake_build"] 65 | }, 66 | { 67 | "name": "testsvenv", 68 | "displayName": "Tests Venv (for workflow)", 69 | "configurePreset": "venv", 70 | "targets": ["pytest", "cpptest", "test_cmake_build"] 71 | } 72 | ], 73 | "workflowPresets": [ 74 | { 75 | "name": "default", 76 | "displayName": "Default Workflow", 77 | "steps": [ 78 | { "type": "configure", "name": "default" }, 79 | { "type": "build", "name": "default" }, 80 | { "type": "build", "name": "tests" } 81 | ] 82 | }, 83 | { 84 | "name": "venv", 85 | "displayName": "Default Workflow", 86 | "steps": [ 87 | { "type": "configure", "name": "venv" }, 88 | { "type": "build", "name": "venv" }, 89 | { "type": "build", "name": "testsvenv" } 90 | ] 91 | } 92 | ] 93 | } 94 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Wenzel Jakob , All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | 3. Neither the name of the copyright holder nor the names of its contributors 14 | may be used to endorse or promote products derived from this software 15 | without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | Please also refer to the file .github/CONTRIBUTING.md, which clarifies licensing of 29 | external contributions to this project including patches, pull requests, etc. 30 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Security updates are applied only to the latest release. 6 | 7 | ## Reporting a Vulnerability 8 | 9 | If you have discovered a security vulnerability in this project, please report it privately. **Do not disclose it as a public issue.** This gives us time to work with you to fix the issue before public exposure, reducing the chance that the exploit will be used before a patch is released. 10 | 11 | Please disclose it at [security advisory](https://github.com/pybind/pybind11/security/advisories/new). 12 | 13 | This project is maintained by a team of volunteers on a reasonable-effort basis. As such, please give us at least 90 days to work on a fix before public exposure. 14 | -------------------------------------------------------------------------------- /docs/Doxyfile: -------------------------------------------------------------------------------- 1 | PROJECT_NAME = pybind11 2 | INPUT = ../include/pybind11/ 3 | RECURSIVE = YES 4 | 5 | GENERATE_HTML = NO 6 | GENERATE_LATEX = NO 7 | GENERATE_XML = YES 8 | XML_OUTPUT = .build/doxygenxml 9 | XML_PROGRAMLISTING = YES 10 | 11 | MACRO_EXPANSION = YES 12 | EXPAND_ONLY_PREDEF = YES 13 | EXPAND_AS_DEFINED = PYBIND11_RUNTIME_EXCEPTION 14 | 15 | ALIASES = "rst=\verbatim embed:rst" 16 | ALIASES += "endrst=\endverbatim" 17 | 18 | QUIET = YES 19 | WARNINGS = YES 20 | WARN_IF_UNDOCUMENTED = NO 21 | PREDEFINED = PYBIND11_NOINLINE 22 | -------------------------------------------------------------------------------- /docs/_static/css/custom.css: -------------------------------------------------------------------------------- 1 | .highlight .go { 2 | color: #707070; 3 | } 4 | -------------------------------------------------------------------------------- /docs/advanced/cast/index.rst: -------------------------------------------------------------------------------- 1 | .. _type-conversions: 2 | 3 | Type conversions 4 | ################ 5 | 6 | Apart from enabling cross-language function calls, a fundamental problem 7 | that a binding tool like pybind11 must address is to provide access to 8 | native Python types in C++ and vice versa. There are three fundamentally 9 | different ways to do this—which approach is preferable for a particular type 10 | depends on the situation at hand. 11 | 12 | 1. Use a native C++ type everywhere. In this case, the type must be wrapped 13 | using pybind11-generated bindings so that Python can interact with it. 14 | 15 | 2. Use a native Python type everywhere. It will need to be wrapped so that 16 | C++ functions can interact with it. 17 | 18 | 3. Use a native C++ type on the C++ side and a native Python type on the 19 | Python side. pybind11 refers to this as a *type conversion*. 20 | 21 | Type conversions are the most "natural" option in the sense that native 22 | (non-wrapped) types are used everywhere. The main downside is that a copy 23 | of the data must be made on every Python ↔ C++ transition: this is 24 | needed since the C++ and Python versions of the same type generally won't 25 | have the same memory layout. 26 | 27 | pybind11 can perform many kinds of conversions automatically. An overview 28 | is provided in the table ":ref:`conversion_table`". 29 | 30 | The following subsections discuss the differences between these options in more 31 | detail. The main focus in this section is on type conversions, which represent 32 | the last case of the above list. 33 | 34 | .. toctree:: 35 | :maxdepth: 1 36 | 37 | overview 38 | strings 39 | stl 40 | functional 41 | chrono 42 | eigen 43 | custom 44 | -------------------------------------------------------------------------------- /docs/advanced/pycpp/index.rst: -------------------------------------------------------------------------------- 1 | Python C++ interface 2 | #################### 3 | 4 | pybind11 exposes Python types and functions using thin C++ wrappers, which 5 | makes it possible to conveniently call Python code from C++ without resorting 6 | to Python's C API. 7 | 8 | .. toctree:: 9 | :maxdepth: 2 10 | 11 | object 12 | numpy 13 | utilities 14 | -------------------------------------------------------------------------------- /docs/benchmark.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import datetime as dt 4 | import os 5 | import random 6 | 7 | nfns = 4 # Functions per class 8 | nargs = 4 # Arguments per function 9 | 10 | 11 | def generate_dummy_code_pybind11(nclasses=10): 12 | decl = "" 13 | bindings = "" 14 | 15 | for cl in range(nclasses): 16 | decl += f"class cl{cl:03};\n" 17 | decl += "\n" 18 | 19 | for cl in range(nclasses): 20 | decl += f"class {cl:03} {{\n" 21 | decl += "public:\n" 22 | bindings += f' py::class_(m, "cl{cl:03}")\n' 23 | for fn in range(nfns): 24 | ret = random.randint(0, nclasses - 1) 25 | params = [random.randint(0, nclasses - 1) for i in range(nargs)] 26 | decl += f" cl{ret:03} *fn_{fn:03}(" 27 | decl += ", ".join(f"cl{p:03} *" for p in params) 28 | decl += ");\n" 29 | bindings += f' .def("fn_{fn:03}", &cl{cl:03}::fn_{fn:03})\n' 30 | decl += "};\n\n" 31 | bindings += " ;\n" 32 | 33 | result = "#include \n\n" 34 | result += "namespace py = pybind11;\n\n" 35 | result += decl + "\n" 36 | result += "PYBIND11_MODULE(example, m) {\n" 37 | result += bindings 38 | result += "}" 39 | return result 40 | 41 | 42 | def generate_dummy_code_boost(nclasses=10): 43 | decl = "" 44 | bindings = "" 45 | 46 | for cl in range(nclasses): 47 | decl += f"class cl{cl:03};\n" 48 | decl += "\n" 49 | 50 | for cl in range(nclasses): 51 | decl += f"class cl{cl:03} {{\n" 52 | decl += "public:\n" 53 | bindings += f' py::class_("cl{cl:03}")\n' 54 | for fn in range(nfns): 55 | ret = random.randint(0, nclasses - 1) 56 | params = [random.randint(0, nclasses - 1) for i in range(nargs)] 57 | decl += f" cl{ret:03} *fn_{fn:03}(" 58 | decl += ", ".join(f"cl{p:03} *" for p in params) 59 | decl += ");\n" 60 | bindings += f' .def("fn_{fn:03}", &cl{cl:03}::fn_{fn:03}, py::return_value_policy())\n' 61 | decl += "};\n\n" 62 | bindings += " ;\n" 63 | 64 | result = "#include \n\n" 65 | result += "namespace py = boost::python;\n\n" 66 | result += decl + "\n" 67 | result += "BOOST_PYTHON_MODULE(example) {\n" 68 | result += bindings 69 | result += "}" 70 | return result 71 | 72 | 73 | for codegen in [generate_dummy_code_pybind11, generate_dummy_code_boost]: 74 | print("{") 75 | for i in range(10): 76 | nclasses = 2**i 77 | with open("test.cpp", "w") as f: 78 | f.write(codegen(nclasses)) 79 | n1 = dt.datetime.now() 80 | os.system( 81 | "g++ -Os -shared -rdynamic -undefined dynamic_lookup " 82 | "-fvisibility=hidden -std=c++14 test.cpp -I include " 83 | "-I /System/Library/Frameworks/Python.framework/Headers -o test.so" 84 | ) 85 | n2 = dt.datetime.now() 86 | elapsed = (n2 - n1).total_seconds() 87 | size = os.stat("test.so").st_size 88 | print(f" {{{nclasses * nfns}, {elapsed:.6f}, {size}}},") 89 | print("}") 90 | -------------------------------------------------------------------------------- /docs/cmake/index.rst: -------------------------------------------------------------------------------- 1 | CMake helpers 2 | ------------- 3 | 4 | Pybind11 can be used with ``add_subdirectory(extern/pybind11)``, or from an 5 | install with ``find_package(pybind11 CONFIG)``. The interface provided in 6 | either case is functionally identical. 7 | 8 | .. cmake-module:: ../../tools/pybind11Config.cmake.in 9 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. only:: latex 2 | 3 | Intro 4 | ===== 5 | 6 | .. include:: readme.rst 7 | 8 | .. only:: not latex 9 | 10 | Contents: 11 | 12 | .. toctree:: 13 | :maxdepth: 1 14 | 15 | changelog 16 | upgrade 17 | 18 | .. toctree:: 19 | :caption: The Basics 20 | :maxdepth: 2 21 | 22 | installing 23 | basics 24 | classes 25 | compiling 26 | 27 | .. toctree:: 28 | :caption: Advanced Topics 29 | :maxdepth: 2 30 | 31 | advanced/functions 32 | advanced/classes 33 | advanced/exceptions 34 | advanced/smart_ptrs 35 | advanced/cast/index 36 | advanced/pycpp/index 37 | advanced/embedding 38 | advanced/misc 39 | advanced/deprecated 40 | 41 | .. toctree:: 42 | :caption: Extra Information 43 | :maxdepth: 1 44 | 45 | faq 46 | benchmark 47 | limitations 48 | reference 49 | cmake/index 50 | -------------------------------------------------------------------------------- /docs/limitations.rst: -------------------------------------------------------------------------------- 1 | Limitations 2 | ########### 3 | 4 | Design choices 5 | ^^^^^^^^^^^^^^ 6 | 7 | pybind11 strives to be a general solution to binding generation, but it also has 8 | certain limitations: 9 | 10 | - pybind11 casts away ``const``-ness in function arguments and return values. 11 | This is in line with the Python language, which has no concept of ``const`` 12 | values. This means that some additional care is needed to avoid bugs that 13 | would be caught by the type checker in a traditional C++ program. 14 | 15 | - The NumPy interface ``pybind11::array`` greatly simplifies accessing 16 | numerical data from C++ (and vice versa), but it's not a full-blown array 17 | class like ``Eigen::Array`` or ``boost.multi_array``. ``Eigen`` objects are 18 | directly supported, however, with ``pybind11/eigen.h``. 19 | 20 | Large but useful features could be implemented in pybind11 but would lead to a 21 | significant increase in complexity. Pybind11 strives to be simple and compact. 22 | Users who require large new features are encouraged to write an extension to 23 | pybind11; see `pybind11_json `_ for an 24 | example. 25 | 26 | 27 | Known bugs 28 | ^^^^^^^^^^ 29 | 30 | These are issues that hopefully will one day be fixed, but currently are 31 | unsolved. If you know how to help with one of these issues, contributions 32 | are welcome! 33 | 34 | - Intel 20.2 is currently having an issue with the test suite. 35 | `#2573 `_ 36 | 37 | - Debug mode Python does not support 1-5 tests in the test suite currently. 38 | `#2422 `_ 39 | 40 | - PyPy3 7.3.1 and 7.3.2 have issues with several tests on 32-bit Windows. 41 | 42 | Known limitations 43 | ^^^^^^^^^^^^^^^^^ 44 | 45 | These are issues that are probably solvable, but have not been fixed yet. A 46 | clean, well written patch would likely be accepted to solve them. 47 | 48 | - Type casters are not kept alive recursively. 49 | `#2527 `_ 50 | One consequence is that containers of ``char *`` are currently not supported. 51 | `#2245 `_ 52 | 53 | Python 3.9.0 warning 54 | ^^^^^^^^^^^^^^^^^^^^ 55 | 56 | Combining older versions of pybind11 (< 2.6.0) with Python on exactly 3.9.0 57 | will trigger undefined behavior that typically manifests as crashes during 58 | interpreter shutdown (but could also destroy your data. **You have been 59 | warned**). 60 | 61 | This issue was `fixed in Python `_. 62 | As a mitigation for this bug, pybind11 2.6.0 or newer includes a workaround 63 | specifically when Python 3.9.0 is detected at runtime, leaking about 50 bytes 64 | of memory when a callback function is garbage collected. For reference, the 65 | pybind11 test suite has about 2,000 such callbacks, but only 49 are garbage 66 | collected before the end-of-process. Wheels (even if built with Python 3.9.0) 67 | will correctly avoid the leak when run in Python 3.9.1, and this does not 68 | affect other 3.X versions. 69 | -------------------------------------------------------------------------------- /docs/pybind11-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pybind/pybind11/7da1d53df51814d19ebcead549e6c5e0171a3653/docs/pybind11-logo.png -------------------------------------------------------------------------------- /docs/pybind11_vs_boost_python1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pybind/pybind11/7da1d53df51814d19ebcead549e6c5e0171a3653/docs/pybind11_vs_boost_python1.png -------------------------------------------------------------------------------- /docs/pybind11_vs_boost_python2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pybind/pybind11/7da1d53df51814d19ebcead549e6c5e0171a3653/docs/pybind11_vs_boost_python2.png -------------------------------------------------------------------------------- /docs/requirements.in: -------------------------------------------------------------------------------- 1 | breathe 2 | furo 3 | myst_parser 4 | sphinx 5 | sphinx-copybutton 6 | sphinxcontrib-moderncmakedomain 7 | sphinxcontrib-svg2pdfconverter 8 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | # This file was autogenerated by uv via the following command: 2 | # uv pip compile docs/requirements.in -o docs/requirements.txt 3 | alabaster==0.7.16 4 | # via sphinx 5 | babel==2.14.0 6 | # via sphinx 7 | beautifulsoup4==4.12.3 8 | # via furo 9 | breathe==4.35.0 10 | # via -r docs/requirements.in 11 | certifi==2024.7.4 12 | # via requests 13 | charset-normalizer==3.3.2 14 | # via requests 15 | docutils==0.20.1 16 | # via 17 | # breathe 18 | # myst-parser 19 | # sphinx 20 | furo==2024.1.29 21 | # via -r docs/requirements.in 22 | idna==3.7 23 | # via requests 24 | imagesize==1.4.1 25 | # via sphinx 26 | jinja2==3.1.6 27 | # via 28 | # myst-parser 29 | # sphinx 30 | markdown-it-py==3.0.0 31 | # via 32 | # mdit-py-plugins 33 | # myst-parser 34 | markupsafe==2.1.5 35 | # via jinja2 36 | mdit-py-plugins==0.4.2 37 | # via myst-parser 38 | mdurl==0.1.2 39 | # via markdown-it-py 40 | myst-parser==4.0.1 41 | # via -r docs/requirements.in 42 | packaging==24.0 43 | # via sphinx 44 | pygments==2.17.2 45 | # via 46 | # furo 47 | # sphinx 48 | pyyaml==6.0.2 49 | # via myst-parser 50 | requests==2.32.3 51 | # via sphinx 52 | snowballstemmer==2.2.0 53 | # via sphinx 54 | soupsieve==2.5 55 | # via beautifulsoup4 56 | sphinx==7.2.6 57 | # via 58 | # -r docs/requirements.in 59 | # breathe 60 | # furo 61 | # myst-parser 62 | # sphinx-basic-ng 63 | # sphinx-copybutton 64 | # sphinxcontrib-moderncmakedomain 65 | # sphinxcontrib-svg2pdfconverter 66 | sphinx-basic-ng==1.0.0b2 67 | # via furo 68 | sphinx-copybutton==0.5.2 69 | # via -r docs/requirements.in 70 | sphinxcontrib-applehelp==1.0.8 71 | # via sphinx 72 | sphinxcontrib-devhelp==1.0.6 73 | # via sphinx 74 | sphinxcontrib-htmlhelp==2.0.5 75 | # via sphinx 76 | sphinxcontrib-jsmath==1.0.1 77 | # via sphinx 78 | sphinxcontrib-moderncmakedomain==3.27.0 79 | # via -r docs/requirements.in 80 | sphinxcontrib-qthelp==1.0.7 81 | # via sphinx 82 | sphinxcontrib-serializinghtml==1.1.10 83 | # via sphinx 84 | sphinxcontrib-svg2pdfconverter==1.2.2 85 | # via -r docs/requirements.in 86 | urllib3==2.2.2 87 | # via requests 88 | -------------------------------------------------------------------------------- /include/pybind11/common.h: -------------------------------------------------------------------------------- 1 | #include "detail/common.h" 2 | #warning "Including 'common.h' is deprecated. It will be removed in v3.0. Use 'pybind11.h'." 3 | -------------------------------------------------------------------------------- /include/pybind11/complex.h: -------------------------------------------------------------------------------- 1 | /* 2 | pybind11/complex.h: Complex number support 3 | 4 | Copyright (c) 2016 Wenzel Jakob 5 | 6 | All rights reserved. Use of this source code is governed by a 7 | BSD-style license that can be found in the LICENSE file. 8 | */ 9 | 10 | #pragma once 11 | 12 | #include "pybind11.h" 13 | 14 | #include 15 | 16 | /// glibc defines I as a macro which breaks things, e.g., boost template names 17 | #ifdef I 18 | # undef I 19 | #endif 20 | 21 | PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) 22 | 23 | template 24 | struct format_descriptor, detail::enable_if_t::value>> { 25 | static constexpr const char c = format_descriptor::c; 26 | static constexpr const char value[3] = {'Z', c, '\0'}; 27 | static std::string format() { return std::string(value); } 28 | }; 29 | 30 | #ifndef PYBIND11_CPP17 31 | 32 | template 33 | constexpr const char 34 | format_descriptor, 35 | detail::enable_if_t::value>>::value[3]; 36 | 37 | #endif 38 | 39 | PYBIND11_NAMESPACE_BEGIN(detail) 40 | 41 | template 42 | struct is_fmt_numeric, detail::enable_if_t::value>> { 43 | static constexpr bool value = true; 44 | static constexpr int index = is_fmt_numeric::index + 3; 45 | }; 46 | 47 | template 48 | class type_caster> { 49 | public: 50 | bool load(handle src, bool convert) { 51 | if (!src) { 52 | return false; 53 | } 54 | if (!convert && !PyComplex_Check(src.ptr())) { 55 | return false; 56 | } 57 | Py_complex result = PyComplex_AsCComplex(src.ptr()); 58 | if (result.real == -1.0 && PyErr_Occurred()) { 59 | PyErr_Clear(); 60 | return false; 61 | } 62 | value = std::complex((T) result.real, (T) result.imag); 63 | return true; 64 | } 65 | 66 | static handle 67 | cast(const std::complex &src, return_value_policy /* policy */, handle /* parent */) { 68 | return PyComplex_FromDoubles((double) src.real(), (double) src.imag()); 69 | } 70 | 71 | PYBIND11_TYPE_CASTER(std::complex, const_name("complex")); 72 | }; 73 | PYBIND11_NAMESPACE_END(detail) 74 | PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) 75 | -------------------------------------------------------------------------------- /include/pybind11/conduit/README.txt: -------------------------------------------------------------------------------- 1 | NOTE 2 | ---- 3 | 4 | The C++ code here 5 | 6 | ** only depends on ** 7 | 8 | and nothing else. 9 | 10 | DO NOT ADD CODE WITH OTHER EXTERNAL DEPENDENCIES TO THIS DIRECTORY. 11 | 12 | Read on: 13 | 14 | pybind11_conduit_v1.h — Type-safe interoperability between different 15 | independent Python/C++ bindings systems. 16 | -------------------------------------------------------------------------------- /include/pybind11/conduit/wrap_include_python_h.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Copyright (c) 2024 The pybind Community. 4 | 5 | // STRONG REQUIREMENT: 6 | // This header is a wrapper around `#include `, therefore it 7 | // MUST BE INCLUDED BEFORE ANY STANDARD HEADERS are included. 8 | // See also: 9 | // https://docs.python.org/3/c-api/intro.html#include-files 10 | // Quoting from there: 11 | // Note: Since Python may define some pre-processor definitions which affect 12 | // the standard headers on some systems, you must include Python.h before 13 | // any standard headers are included. 14 | 15 | // To maximize reusability: 16 | // DO NOT ADD CODE THAT REQUIRES C++ EXCEPTION HANDLING. 17 | 18 | // Disable linking to pythonX_d.lib on Windows in debug mode. 19 | #if defined(_MSC_VER) && defined(_DEBUG) && !defined(Py_DEBUG) 20 | // Workaround for a VS 2022 issue. 21 | // See https://github.com/pybind/pybind11/pull/3497 for full context. 22 | // NOTE: This workaround knowingly violates the Python.h include order 23 | // requirement (see above). 24 | # include 25 | # if _MSVC_STL_VERSION >= 143 26 | # include 27 | # endif 28 | # define PYBIND11_DEBUG_MARKER 29 | # undef _DEBUG 30 | #endif 31 | 32 | // Don't let Python.h #define (v)snprintf as macro because they are implemented 33 | // properly in Visual Studio since 2015. 34 | #if defined(_MSC_VER) 35 | # define HAVE_SNPRINTF 1 36 | #endif 37 | 38 | #if defined(_MSC_VER) 39 | # pragma warning(push) 40 | # pragma warning(disable : 4505) 41 | // C4505: 'PySlice_GetIndicesEx': unreferenced local function has been removed 42 | #endif 43 | 44 | #include 45 | #include 46 | #include 47 | 48 | #if defined(_MSC_VER) 49 | # pragma warning(pop) 50 | #endif 51 | 52 | #if defined(PYBIND11_DEBUG_MARKER) 53 | # define _DEBUG 1 54 | # undef PYBIND11_DEBUG_MARKER 55 | #endif 56 | 57 | // Python #defines overrides on all sorts of core functions, which 58 | // tends to wreak havok in C++ codebases that expect these to work 59 | // like regular functions (potentially with several overloads). 60 | #if defined(isalnum) 61 | # undef isalnum 62 | # undef isalpha 63 | # undef islower 64 | # undef isspace 65 | # undef isupper 66 | # undef tolower 67 | # undef toupper 68 | #endif 69 | 70 | #if defined(copysign) 71 | # undef copysign 72 | #endif 73 | -------------------------------------------------------------------------------- /include/pybind11/critical_section.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-2025 The Pybind Development Team. 2 | // All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | #pragma once 6 | 7 | #include "pytypes.h" 8 | 9 | PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) 10 | 11 | /// This does not do anything if there's a GIL. On free-threaded Python, 12 | /// it locks an object. This uses the CPython API, which has limits 13 | class scoped_critical_section { 14 | public: 15 | #ifdef Py_GIL_DISABLED 16 | explicit scoped_critical_section(handle obj) : has2(false) { 17 | PyCriticalSection_Begin(§ion, obj.ptr()); 18 | } 19 | 20 | scoped_critical_section(handle obj1, handle obj2) : has2(true) { 21 | PyCriticalSection2_Begin(§ion2, obj1.ptr(), obj2.ptr()); 22 | } 23 | 24 | ~scoped_critical_section() { 25 | if (has2) { 26 | PyCriticalSection2_End(§ion2); 27 | } else { 28 | PyCriticalSection_End(§ion); 29 | } 30 | } 31 | #else 32 | explicit scoped_critical_section(handle) {}; 33 | scoped_critical_section(handle, handle) {}; 34 | ~scoped_critical_section() = default; 35 | #endif 36 | 37 | scoped_critical_section(const scoped_critical_section &) = delete; 38 | scoped_critical_section &operator=(const scoped_critical_section &) = delete; 39 | 40 | private: 41 | #ifdef Py_GIL_DISABLED 42 | bool has2; 43 | union { 44 | PyCriticalSection section; 45 | PyCriticalSection2 section2; 46 | }; 47 | #endif 48 | }; 49 | 50 | PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) 51 | -------------------------------------------------------------------------------- /include/pybind11/detail/cpp_conduit.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 The pybind Community. 2 | 3 | #pragma once 4 | 5 | #include 6 | 7 | #include "common.h" 8 | #include "internals.h" 9 | 10 | #include 11 | 12 | PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) 13 | PYBIND11_NAMESPACE_BEGIN(detail) 14 | 15 | // Forward declaration needed here: Refactoring opportunity. 16 | extern "C" inline PyObject *pybind11_object_new(PyTypeObject *type, PyObject *, PyObject *); 17 | 18 | inline bool type_is_managed_by_our_internals(PyTypeObject *type_obj) { 19 | #if defined(PYPY_VERSION) 20 | auto &internals = get_internals(); 21 | return bool(internals.registered_types_py.find(type_obj) 22 | != internals.registered_types_py.end()); 23 | #else 24 | return bool(type_obj->tp_new == pybind11_object_new); 25 | #endif 26 | } 27 | 28 | inline bool is_instance_method_of_type(PyTypeObject *type_obj, PyObject *attr_name) { 29 | PyObject *descr = _PyType_Lookup(type_obj, attr_name); 30 | return bool((descr != nullptr) && PyInstanceMethod_Check(descr)); 31 | } 32 | 33 | inline object try_get_cpp_conduit_method(PyObject *obj) { 34 | if (PyType_Check(obj)) { 35 | return object(); 36 | } 37 | PyTypeObject *type_obj = Py_TYPE(obj); 38 | str attr_name("_pybind11_conduit_v1_"); 39 | bool assumed_to_be_callable = false; 40 | if (type_is_managed_by_our_internals(type_obj)) { 41 | if (!is_instance_method_of_type(type_obj, attr_name.ptr())) { 42 | return object(); 43 | } 44 | assumed_to_be_callable = true; 45 | } 46 | PyObject *method = PyObject_GetAttr(obj, attr_name.ptr()); 47 | if (method == nullptr) { 48 | PyErr_Clear(); 49 | return object(); 50 | } 51 | if (!assumed_to_be_callable && PyCallable_Check(method) == 0) { 52 | Py_DECREF(method); 53 | return object(); 54 | } 55 | return reinterpret_steal(method); 56 | } 57 | 58 | inline void *try_raw_pointer_ephemeral_from_cpp_conduit(handle src, 59 | const std::type_info *cpp_type_info) { 60 | object method = try_get_cpp_conduit_method(src.ptr()); 61 | if (method) { 62 | capsule cpp_type_info_capsule(const_cast(static_cast(cpp_type_info)), 63 | typeid(std::type_info).name()); 64 | object cpp_conduit = method(bytes(PYBIND11_PLATFORM_ABI_ID), 65 | cpp_type_info_capsule, 66 | bytes("raw_pointer_ephemeral")); 67 | if (isinstance(cpp_conduit)) { 68 | return reinterpret_borrow(cpp_conduit).get_pointer(); 69 | } 70 | } 71 | return nullptr; 72 | } 73 | 74 | PYBIND11_NAMESPACE_END(detail) 75 | PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) 76 | -------------------------------------------------------------------------------- /include/pybind11/detail/dynamic_raw_ptr_cast_if_possible.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 The Pybind Development Team. 2 | // All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | #pragma once 6 | 7 | #include "common.h" 8 | 9 | #include 10 | 11 | PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) 12 | PYBIND11_NAMESPACE_BEGIN(detail) 13 | 14 | template 15 | struct dynamic_raw_ptr_cast_is_possible : std::false_type {}; 16 | 17 | template 18 | struct dynamic_raw_ptr_cast_is_possible< 19 | To, 20 | From, 21 | detail::enable_if_t::value && std::is_polymorphic::value>> 22 | : std::true_type {}; 23 | 24 | template ::value, int> = 0> 27 | To *dynamic_raw_ptr_cast_if_possible(From * /*ptr*/) { 28 | return nullptr; 29 | } 30 | 31 | template ::value, int> = 0> 34 | To *dynamic_raw_ptr_cast_if_possible(From *ptr) { 35 | return dynamic_cast(ptr); 36 | } 37 | 38 | PYBIND11_NAMESPACE_END(detail) 39 | PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) 40 | -------------------------------------------------------------------------------- /include/pybind11/detail/exception_translation.h: -------------------------------------------------------------------------------- 1 | /* 2 | pybind11/detail/exception_translation.h: means to translate C++ exceptions to Python exceptions 3 | 4 | Copyright (c) 2024 The Pybind Development Team. 5 | 6 | All rights reserved. Use of this source code is governed by a 7 | BSD-style license that can be found in the LICENSE file. 8 | */ 9 | 10 | #pragma once 11 | 12 | #include "common.h" 13 | #include "internals.h" 14 | 15 | PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) 16 | PYBIND11_NAMESPACE_BEGIN(detail) 17 | 18 | // Apply all the extensions translators from a list 19 | // Return true if one of the translators completed without raising an exception 20 | // itself. Return of false indicates that if there are other translators 21 | // available, they should be tried. 22 | inline bool apply_exception_translators(std::forward_list &translators) { 23 | auto last_exception = std::current_exception(); 24 | 25 | for (auto &translator : translators) { 26 | try { 27 | translator(last_exception); 28 | return true; 29 | } catch (...) { 30 | last_exception = std::current_exception(); 31 | } 32 | } 33 | return false; 34 | } 35 | 36 | inline void try_translate_exceptions() { 37 | /* When an exception is caught, give each registered exception 38 | translator a chance to translate it to a Python exception. First 39 | all module-local translators will be tried in reverse order of 40 | registration. If none of the module-locale translators handle 41 | the exception (or there are no module-locale translators) then 42 | the global translators will be tried, also in reverse order of 43 | registration. 44 | 45 | A translator may choose to do one of the following: 46 | 47 | - catch the exception and call py::set_error() 48 | to set a standard (or custom) Python exception, or 49 | - do nothing and let the exception fall through to the next translator, or 50 | - delegate translation to the next translator by throwing a new type of exception. 51 | */ 52 | 53 | bool handled = with_exception_translators( 54 | [&](std::forward_list &exception_translators, 55 | std::forward_list &local_exception_translators) { 56 | if (detail::apply_exception_translators(local_exception_translators)) { 57 | return true; 58 | } 59 | if (detail::apply_exception_translators(exception_translators)) { 60 | return true; 61 | } 62 | return false; 63 | }); 64 | 65 | if (!handled) { 66 | set_error(PyExc_SystemError, "Exception escaped from default exception translator!"); 67 | } 68 | } 69 | 70 | PYBIND11_NAMESPACE_END(detail) 71 | PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) 72 | -------------------------------------------------------------------------------- /include/pybind11/detail/typeid.h: -------------------------------------------------------------------------------- 1 | /* 2 | pybind11/detail/typeid.h: Compiler-independent access to type identifiers 3 | 4 | Copyright (c) 2016 Wenzel Jakob 5 | 6 | All rights reserved. Use of this source code is governed by a 7 | BSD-style license that can be found in the LICENSE file. 8 | */ 9 | 10 | #pragma once 11 | 12 | #include 13 | #include 14 | 15 | #if defined(__GNUG__) 16 | # include 17 | #endif 18 | 19 | #include "common.h" 20 | 21 | PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) 22 | PYBIND11_NAMESPACE_BEGIN(detail) 23 | 24 | /// Erase all occurrences of a substring 25 | inline void erase_all(std::string &string, const std::string &search) { 26 | for (size_t pos = 0;;) { 27 | pos = string.find(search, pos); 28 | if (pos == std::string::npos) { 29 | break; 30 | } 31 | string.erase(pos, search.length()); 32 | } 33 | } 34 | 35 | PYBIND11_NOINLINE void clean_type_id(std::string &name) { 36 | #if defined(__GNUG__) 37 | int status = 0; 38 | std::unique_ptr res{ 39 | abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), std::free}; 40 | if (status == 0) { 41 | name = res.get(); 42 | } 43 | #else 44 | detail::erase_all(name, "class "); 45 | detail::erase_all(name, "struct "); 46 | detail::erase_all(name, "enum "); 47 | #endif 48 | detail::erase_all(name, "pybind11::"); 49 | } 50 | 51 | inline std::string clean_type_id(const char *typeid_name) { 52 | std::string name(typeid_name); 53 | detail::clean_type_id(name); 54 | return name; 55 | } 56 | 57 | PYBIND11_NAMESPACE_END(detail) 58 | 59 | /// Return a string representation of a C++ type 60 | template 61 | static std::string type_id() { 62 | return detail::clean_type_id(typeid(T).name()); 63 | } 64 | 65 | PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) 66 | -------------------------------------------------------------------------------- /include/pybind11/detail/using_smart_holder.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 The Pybind Development Team. 2 | // All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | #pragma once 6 | 7 | #include "common.h" 8 | #include "struct_smart_holder.h" 9 | 10 | #include 11 | 12 | PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) 13 | 14 | using pybind11::memory::smart_holder; 15 | 16 | PYBIND11_NAMESPACE_BEGIN(detail) 17 | 18 | template 19 | using is_smart_holder = std::is_same; 20 | 21 | PYBIND11_NAMESPACE_END(detail) 22 | PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) 23 | -------------------------------------------------------------------------------- /include/pybind11/eigen.h: -------------------------------------------------------------------------------- 1 | /* 2 | pybind11/eigen.h: Transparent conversion for dense and sparse Eigen matrices 3 | 4 | Copyright (c) 2016 Wenzel Jakob 5 | 6 | All rights reserved. Use of this source code is governed by a 7 | BSD-style license that can be found in the LICENSE file. 8 | */ 9 | 10 | #pragma once 11 | 12 | #include "eigen/matrix.h" 13 | -------------------------------------------------------------------------------- /include/pybind11/eigen/common.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 The pybind Community. 2 | 3 | #pragma once 4 | 5 | // Common message for `static_assert()`s, which are useful to easily 6 | // preempt much less obvious errors. 7 | #define PYBIND11_EIGEN_MESSAGE_POINTER_TYPES_ARE_NOT_SUPPORTED \ 8 | "Pointer types (in particular `PyObject *`) are not supported as scalar types for Eigen " \ 9 | "types." 10 | -------------------------------------------------------------------------------- /include/pybind11/gil_simple.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-2025 The Pybind Development Team. 2 | // All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | #pragma once 6 | 7 | #include "detail/common.h" 8 | 9 | #include 10 | 11 | PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) 12 | 13 | class gil_scoped_acquire_simple { 14 | PyGILState_STATE state; 15 | 16 | public: 17 | gil_scoped_acquire_simple() : state{PyGILState_Ensure()} {} 18 | gil_scoped_acquire_simple(const gil_scoped_acquire_simple &) = delete; 19 | gil_scoped_acquire_simple &operator=(const gil_scoped_acquire_simple &) = delete; 20 | ~gil_scoped_acquire_simple() { PyGILState_Release(state); } 21 | }; 22 | 23 | class gil_scoped_release_simple { 24 | PyThreadState *state; 25 | 26 | public: 27 | // PRECONDITION: The GIL must be held when this constructor is called. 28 | gil_scoped_release_simple() { 29 | assert(PyGILState_Check()); 30 | state = PyEval_SaveThread(); 31 | } 32 | gil_scoped_release_simple(const gil_scoped_release_simple &) = delete; 33 | gil_scoped_release_simple &operator=(const gil_scoped_release_simple &) = delete; 34 | ~gil_scoped_release_simple() { PyEval_RestoreThread(state); } 35 | }; 36 | 37 | PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) 38 | -------------------------------------------------------------------------------- /include/pybind11/native_enum.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022-2025 The pybind Community. 2 | // All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | #pragma once 6 | 7 | #include "detail/common.h" 8 | #include "detail/native_enum_data.h" 9 | #include "detail/type_caster_base.h" 10 | #include "cast.h" 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) 18 | 19 | /// Conversions between Python's native (stdlib) enum types and C++ enums. 20 | template 21 | class native_enum : public detail::native_enum_data { 22 | public: 23 | using Underlying = typename std::underlying_type::type; 24 | 25 | native_enum(const object &parent_scope, 26 | const char *name, 27 | const char *native_type_name, 28 | const char *class_doc = "") 29 | : detail::native_enum_data( 30 | parent_scope, name, native_type_name, class_doc, std::type_index(typeid(EnumType))) { 31 | if (detail::get_local_type_info(typeid(EnumType)) != nullptr 32 | || detail::get_global_type_info(typeid(EnumType)) != nullptr) { 33 | pybind11_fail( 34 | "pybind11::native_enum<...>(\"" + enum_name_encoded 35 | + "\") is already registered as a `pybind11::enum_` or `pybind11::class_`!"); 36 | } 37 | if (detail::global_internals_native_enum_type_map_contains(enum_type_index)) { 38 | pybind11_fail("pybind11::native_enum<...>(\"" + enum_name_encoded 39 | + "\") is already registered!"); 40 | } 41 | arm_finalize_check(); 42 | } 43 | 44 | /// Export enumeration entries into the parent scope 45 | native_enum &export_values() { 46 | assert(!export_values_flag); // Catch redundant calls. 47 | export_values_flag = true; 48 | return *this; 49 | } 50 | 51 | /// Add an enumeration entry 52 | native_enum &value(char const *name, EnumType value, const char *doc = nullptr) { 53 | // Disarm for the case that the native_enum_data dtor runs during exception unwinding. 54 | disarm_finalize_check("value after finalize"); 55 | members.append(make_tuple(name, static_cast(value))); 56 | if (doc) { 57 | member_docs.append(make_tuple(name, doc)); 58 | } 59 | arm_finalize_check(); // There was no exception. 60 | return *this; 61 | } 62 | 63 | native_enum(const native_enum &) = delete; 64 | native_enum &operator=(const native_enum &) = delete; 65 | }; 66 | 67 | PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) 68 | -------------------------------------------------------------------------------- /include/pybind11/options.h: -------------------------------------------------------------------------------- 1 | /* 2 | pybind11/options.h: global settings that are configurable at runtime. 3 | 4 | Copyright (c) 2016 Wenzel Jakob 5 | 6 | All rights reserved. Use of this source code is governed by a 7 | BSD-style license that can be found in the LICENSE file. 8 | */ 9 | 10 | #pragma once 11 | 12 | #include "detail/common.h" 13 | 14 | PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) 15 | 16 | class options { 17 | public: 18 | // Default RAII constructor, which leaves settings as they currently are. 19 | options() : previous_state(global_state()) {} 20 | 21 | // Class is non-copyable. 22 | options(const options &) = delete; 23 | options &operator=(const options &) = delete; 24 | 25 | // Destructor, which restores settings that were in effect before. 26 | ~options() { global_state() = previous_state; } 27 | 28 | // Setter methods (affect the global state): 29 | 30 | options &disable_user_defined_docstrings() & { 31 | global_state().show_user_defined_docstrings = false; 32 | return *this; 33 | } 34 | 35 | options &enable_user_defined_docstrings() & { 36 | global_state().show_user_defined_docstrings = true; 37 | return *this; 38 | } 39 | 40 | options &disable_function_signatures() & { 41 | global_state().show_function_signatures = false; 42 | return *this; 43 | } 44 | 45 | options &enable_function_signatures() & { 46 | global_state().show_function_signatures = true; 47 | return *this; 48 | } 49 | 50 | options &disable_enum_members_docstring() & { 51 | global_state().show_enum_members_docstring = false; 52 | return *this; 53 | } 54 | 55 | options &enable_enum_members_docstring() & { 56 | global_state().show_enum_members_docstring = true; 57 | return *this; 58 | } 59 | 60 | // Getter methods (return the global state): 61 | 62 | static bool show_user_defined_docstrings() { 63 | return global_state().show_user_defined_docstrings; 64 | } 65 | 66 | static bool show_function_signatures() { return global_state().show_function_signatures; } 67 | 68 | static bool show_enum_members_docstring() { 69 | return global_state().show_enum_members_docstring; 70 | } 71 | 72 | // This type is not meant to be allocated on the heap. 73 | void *operator new(size_t) = delete; 74 | 75 | private: 76 | struct state { 77 | bool show_user_defined_docstrings = true; //< Include user-supplied texts in docstrings. 78 | bool show_function_signatures = true; //< Include auto-generated function signatures 79 | // in docstrings. 80 | bool show_enum_members_docstring = true; //< Include auto-generated member list in enum 81 | // docstrings. 82 | }; 83 | 84 | static state &global_state() { 85 | static state instance; 86 | return instance; 87 | } 88 | 89 | state previous_state; 90 | }; 91 | 92 | PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) 93 | -------------------------------------------------------------------------------- /include/pybind11/trampoline_self_life_support.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 The Pybind Development Team. 2 | // All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | #pragma once 6 | 7 | #include "detail/common.h" 8 | #include "detail/using_smart_holder.h" 9 | #include "detail/value_and_holder.h" 10 | 11 | PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) 12 | 13 | PYBIND11_NAMESPACE_BEGIN(detail) 14 | // PYBIND11:REMINDER: Needs refactoring of existing pybind11 code. 15 | inline bool deregister_instance(instance *self, void *valptr, const type_info *tinfo); 16 | PYBIND11_NAMESPACE_END(detail) 17 | 18 | // The original core idea for this struct goes back to PyCLIF: 19 | // https://github.com/google/clif/blob/07f95d7e69dca2fcf7022978a55ef3acff506c19/clif/python/runtime.cc#L37 20 | // URL provided here mainly to give proper credit. 21 | struct trampoline_self_life_support { 22 | detail::value_and_holder v_h; 23 | 24 | trampoline_self_life_support() = default; 25 | 26 | void activate_life_support(const detail::value_and_holder &v_h_) { 27 | Py_INCREF((PyObject *) v_h_.inst); 28 | v_h = v_h_; 29 | } 30 | 31 | void deactivate_life_support() { 32 | Py_DECREF((PyObject *) v_h.inst); 33 | v_h = detail::value_and_holder(); 34 | } 35 | 36 | ~trampoline_self_life_support() { 37 | if (v_h.inst != nullptr && v_h.vh != nullptr) { 38 | void *value_void_ptr = v_h.value_ptr(); 39 | if (value_void_ptr != nullptr) { 40 | PyGILState_STATE threadstate = PyGILState_Ensure(); 41 | v_h.value_ptr() = nullptr; 42 | v_h.holder().release_disowned(); 43 | detail::deregister_instance(v_h.inst, value_void_ptr, v_h.type); 44 | Py_DECREF((PyObject *) v_h.inst); // Must be after deregister. 45 | PyGILState_Release(threadstate); 46 | } 47 | } 48 | } 49 | 50 | // For the next two, the default implementations generate undefined behavior (ASAN failures 51 | // manually verified). The reason is that v_h needs to be kept default-initialized. 52 | trampoline_self_life_support(const trampoline_self_life_support &) {} 53 | trampoline_self_life_support(trampoline_self_life_support &&) noexcept {} 54 | 55 | // These should never be needed (please provide test cases if you think they are). 56 | trampoline_self_life_support &operator=(const trampoline_self_life_support &) = delete; 57 | trampoline_self_life_support &operator=(trampoline_self_life_support &&) = delete; 58 | }; 59 | 60 | PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) 61 | -------------------------------------------------------------------------------- /include/pybind11/type_caster_pyobject_ptr.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 The pybind Community. 2 | 3 | #pragma once 4 | 5 | #include "detail/common.h" 6 | #include "detail/descr.h" 7 | #include "cast.h" 8 | #include "pytypes.h" 9 | 10 | PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) 11 | PYBIND11_NAMESPACE_BEGIN(detail) 12 | 13 | template <> 14 | class type_caster { 15 | public: 16 | static constexpr auto name = const_name("object"); // See discussion under PR #4601. 17 | 18 | // This overload is purely to guard against accidents. 19 | template ::value, int> = 0> 21 | static handle cast(T &&, return_value_policy, handle /*parent*/) { 22 | static_assert(is_same_ignoring_cvref::value, 23 | "Invalid C++ type T for to-Python conversion (type_caster)."); 24 | return nullptr; // Unreachable. 25 | } 26 | 27 | static handle cast(PyObject *src, return_value_policy policy, handle /*parent*/) { 28 | if (src == nullptr) { 29 | throw error_already_set(); 30 | } 31 | if (PyErr_Occurred()) { 32 | raise_from(PyExc_SystemError, "src != nullptr but PyErr_Occurred()"); 33 | throw error_already_set(); 34 | } 35 | if (policy == return_value_policy::take_ownership) { 36 | return src; 37 | } 38 | if (policy == return_value_policy::reference 39 | || policy == return_value_policy::automatic_reference) { 40 | return handle(src).inc_ref(); 41 | } 42 | pybind11_fail("type_caster::cast(): unsupported return_value_policy: " 43 | + std::to_string(static_cast(policy))); 44 | } 45 | 46 | bool load(handle src, bool) { 47 | value = reinterpret_borrow(src); 48 | return true; 49 | } 50 | 51 | template 52 | using cast_op_type = PyObject *; 53 | 54 | explicit operator PyObject *() { return value.ptr(); } 55 | 56 | private: 57 | object value; 58 | }; 59 | 60 | PYBIND11_NAMESPACE_END(detail) 61 | PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) 62 | -------------------------------------------------------------------------------- /include/pybind11/warnings.h: -------------------------------------------------------------------------------- 1 | /* 2 | pybind11/warnings.h: Python warnings wrappers. 3 | 4 | Copyright (c) 2024 Jan Iwaszkiewicz 5 | 6 | All rights reserved. Use of this source code is governed by a 7 | BSD-style license that can be found in the LICENSE file. 8 | */ 9 | 10 | #pragma once 11 | 12 | #include "pybind11.h" 13 | #include "detail/common.h" 14 | 15 | PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) 16 | 17 | PYBIND11_NAMESPACE_BEGIN(detail) 18 | 19 | inline bool PyWarning_Check(PyObject *obj) { 20 | int result = PyObject_IsSubclass(obj, PyExc_Warning); 21 | if (result == 1) { 22 | return true; 23 | } 24 | if (result == -1) { 25 | raise_from(PyExc_SystemError, 26 | "pybind11::detail::PyWarning_Check(): PyObject_IsSubclass() call failed."); 27 | throw error_already_set(); 28 | } 29 | return false; 30 | } 31 | 32 | PYBIND11_NAMESPACE_END(detail) 33 | 34 | PYBIND11_NAMESPACE_BEGIN(warnings) 35 | 36 | inline object 37 | new_warning_type(handle scope, const char *name, handle base = PyExc_RuntimeWarning) { 38 | if (!detail::PyWarning_Check(base.ptr())) { 39 | pybind11_fail("pybind11::warnings::new_warning_type(): cannot create custom warning, base " 40 | "must be a subclass of " 41 | "PyExc_Warning!"); 42 | } 43 | if (hasattr(scope, name)) { 44 | pybind11_fail("pybind11::warnings::new_warning_type(): an attribute with name \"" 45 | + std::string(name) + "\" exists already."); 46 | } 47 | std::string full_name = scope.attr("__name__").cast() + std::string(".") + name; 48 | handle h(PyErr_NewException(full_name.c_str(), base.ptr(), nullptr)); 49 | if (!h) { 50 | raise_from(PyExc_SystemError, 51 | "pybind11::warnings::new_warning_type(): PyErr_NewException() call failed."); 52 | throw error_already_set(); 53 | } 54 | auto obj = reinterpret_steal(h); 55 | scope.attr(name) = obj; 56 | return obj; 57 | } 58 | 59 | // Similar to Python `warnings.warn()` 60 | inline void 61 | warn(const char *message, handle category = PyExc_RuntimeWarning, int stack_level = 2) { 62 | if (!detail::PyWarning_Check(category.ptr())) { 63 | pybind11_fail( 64 | "pybind11::warnings::warn(): cannot raise warning, category must be a subclass of " 65 | "PyExc_Warning!"); 66 | } 67 | 68 | if (PyErr_WarnEx(category.ptr(), message, stack_level) == -1) { 69 | throw error_already_set(); 70 | } 71 | } 72 | 73 | PYBIND11_NAMESPACE_END(warnings) 74 | 75 | PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) 76 | -------------------------------------------------------------------------------- /pybind11/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import sys 4 | 5 | if sys.version_info < (3, 8): # noqa: UP036 6 | msg = "pybind11 does not support Python < 3.8. v2.13 was the last release supporting Python 3.7." 7 | raise ImportError(msg) 8 | 9 | 10 | from ._version import __version__, version_info 11 | from .commands import get_cmake_dir, get_include, get_pkgconfig_dir 12 | 13 | __all__ = ( 14 | "version_info", 15 | "__version__", 16 | "get_include", 17 | "get_cmake_dir", 18 | "get_pkgconfig_dir", 19 | ) 20 | -------------------------------------------------------------------------------- /pybind11/_version.py: -------------------------------------------------------------------------------- 1 | # This file will be replaced in the wheel with a hard-coded version. This only 2 | # exists to allow running directly from source without installing (not 3 | # recommended, but supported). 4 | 5 | from __future__ import annotations 6 | 7 | import re 8 | from pathlib import Path 9 | 10 | DIR = Path(__file__).parent.resolve() 11 | 12 | input_file = DIR.parent / "include/pybind11/detail/common.h" 13 | regex = re.compile( 14 | r""" 15 | \#define \s+ PYBIND11_VERSION_MAJOR \s+ (?P\d+) .*? 16 | \#define \s+ PYBIND11_VERSION_MINOR \s+ (?P\d+) .*? 17 | \#define \s+ PYBIND11_VERSION_PATCH \s+ (?P\S+) 18 | """, 19 | re.MULTILINE | re.DOTALL | re.VERBOSE, 20 | ) 21 | 22 | match = regex.search(input_file.read_text(encoding="utf-8")) 23 | assert match, "Unable to find version in pybind11/detail/common.h" 24 | __version__ = "{major}.{minor}.{patch}".format(**match.groupdict()) 25 | 26 | 27 | def _to_int(s: str) -> int | str: 28 | try: 29 | return int(s) 30 | except ValueError: 31 | return s 32 | 33 | 34 | version_info = tuple(_to_int(s) for s in __version__.split(".")) 35 | -------------------------------------------------------------------------------- /pybind11/commands.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import os 4 | 5 | DIR = os.path.abspath(os.path.dirname(__file__)) 6 | 7 | 8 | def get_include(user: bool = False) -> str: # noqa: ARG001 9 | """ 10 | Return the path to the pybind11 include directory. The historical "user" 11 | argument is unused, and may be removed. 12 | """ 13 | installed_path = os.path.join(DIR, "include") 14 | source_path = os.path.join(os.path.dirname(DIR), "include") 15 | return installed_path if os.path.exists(installed_path) else source_path 16 | 17 | 18 | def get_cmake_dir() -> str: 19 | """ 20 | Return the path to the pybind11 CMake module directory. 21 | """ 22 | cmake_installed_path = os.path.join(DIR, "share", "cmake", "pybind11") 23 | if os.path.exists(cmake_installed_path): 24 | return cmake_installed_path 25 | 26 | msg = "pybind11 not installed, installation required to access the CMake files" 27 | raise ImportError(msg) 28 | 29 | 30 | def get_pkgconfig_dir() -> str: 31 | """ 32 | Return the path to the pybind11 pkgconfig directory. 33 | """ 34 | pkgconfig_installed_path = os.path.join(DIR, "share", "pkgconfig") 35 | if os.path.exists(pkgconfig_installed_path): 36 | return pkgconfig_installed_path 37 | 38 | msg = "pybind11 not installed, installation required to access the pkgconfig files" 39 | raise ImportError(msg) 40 | -------------------------------------------------------------------------------- /pybind11/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pybind/pybind11/7da1d53df51814d19ebcead549e6c5e0171a3653/pybind11/py.typed -------------------------------------------------------------------------------- /tests/cross_module_interleaved_error_already_set.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2022 Google LLC 3 | 4 | All rights reserved. Use of this source code is governed by a 5 | BSD-style license that can be found in the LICENSE file. 6 | */ 7 | 8 | #include 9 | 10 | // This file mimics a DSO that makes pybind11 calls but does not define a PYBIND11_MODULE, 11 | // so that the first call of cross_module_error_already_set() triggers the first call of 12 | // pybind11::detail::get_internals(). 13 | 14 | namespace { 15 | 16 | namespace py = pybind11; 17 | 18 | void interleaved_error_already_set() { 19 | py::set_error(PyExc_RuntimeError, "1st error."); 20 | try { 21 | throw py::error_already_set(); 22 | } catch (const py::error_already_set &) { 23 | // The 2nd error could be conditional in a real application. 24 | py::set_error(PyExc_RuntimeError, "2nd error."); 25 | } // Here the 1st error is destroyed before the 2nd error is fetched. 26 | // The error_already_set dtor triggers a pybind11::detail::get_internals() 27 | // call via pybind11::gil_scoped_acquire. 28 | if (PyErr_Occurred()) { 29 | throw py::error_already_set(); 30 | } 31 | } 32 | 33 | constexpr char kModuleName[] = "cross_module_interleaved_error_already_set"; 34 | 35 | struct PyModuleDef moduledef = { 36 | PyModuleDef_HEAD_INIT, kModuleName, nullptr, 0, nullptr, nullptr, nullptr, nullptr, nullptr}; 37 | 38 | } // namespace 39 | 40 | extern "C" PYBIND11_EXPORT PyObject *PyInit_cross_module_interleaved_error_already_set() { 41 | PyObject *m = PyModule_Create(&moduledef); 42 | if (m != nullptr) { 43 | static_assert(sizeof(&interleaved_error_already_set) == sizeof(void *), 44 | "Function pointer must have the same size as void *"); 45 | #ifdef Py_GIL_DISABLED 46 | PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED); 47 | #endif 48 | PyModule_AddObject( 49 | m, 50 | "funcaddr", 51 | PyLong_FromVoidPtr(reinterpret_cast(&interleaved_error_already_set))); 52 | } 53 | return m; 54 | } 55 | -------------------------------------------------------------------------------- /tests/custom_exceptions.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | 4 | class PythonMyException7(Exception): 5 | def __init__(self, message): 6 | self.message = message 7 | super().__init__(message) 8 | 9 | def __str__(self): 10 | return "[PythonMyException7]: " + self.message.a 11 | -------------------------------------------------------------------------------- /tests/eigen_tensor_avoid_stl_array.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | tests/eigen_tensor.cpp -- automatic conversion of Eigen Tensor 3 | 4 | All rights reserved. Use of this source code is governed by a 5 | BSD-style license that can be found in the LICENSE file. 6 | */ 7 | 8 | #ifndef EIGEN_AVOID_STL_ARRAY 9 | # define EIGEN_AVOID_STL_ARRAY 10 | #endif 11 | 12 | #include "test_eigen_tensor.inl" 13 | 14 | PYBIND11_MODULE(eigen_tensor_avoid_stl_array, m, pybind11::mod_gil_not_used()) { 15 | eigen_tensor_test::test_module(m); 16 | } 17 | -------------------------------------------------------------------------------- /tests/env.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import platform 4 | import sys 5 | import sysconfig 6 | 7 | import pytest 8 | 9 | LINUX = sys.platform.startswith("linux") 10 | MACOS = sys.platform.startswith("darwin") 11 | WIN = sys.platform.startswith("win32") or sys.platform.startswith("cygwin") 12 | 13 | CPYTHON = platform.python_implementation() == "CPython" 14 | PYPY = platform.python_implementation() == "PyPy" 15 | GRAALPY = sys.implementation.name == "graalpy" 16 | _graalpy_version = ( 17 | sys.modules["__graalpython__"].get_graalvm_version() if GRAALPY else "0.0.0" 18 | ) 19 | GRAALPY_VERSION = tuple(int(t) for t in _graalpy_version.split("-")[0].split(".")[:3]) 20 | PY_GIL_DISABLED = bool(sysconfig.get_config_var("Py_GIL_DISABLED")) 21 | 22 | 23 | def deprecated_call(): 24 | """ 25 | pytest.deprecated_call() seems broken in pytest<3.9.x; concretely, it 26 | doesn't work on CPython 3.8.0 with pytest==3.3.2 on Ubuntu 18.04 (#2922). 27 | 28 | This is a narrowed reimplementation of the following PR :( 29 | https://github.com/pytest-dev/pytest/pull/4104 30 | """ 31 | # TODO: Remove this when testing requires pytest>=3.9. 32 | pieces = pytest.__version__.split(".") 33 | pytest_major_minor = (int(pieces[0]), int(pieces[1])) 34 | if pytest_major_minor < (3, 9): 35 | return pytest.warns((DeprecationWarning, PendingDeprecationWarning)) 36 | return pytest.deprecated_call() 37 | -------------------------------------------------------------------------------- /tests/exo_planet_c_api.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 The pybind Community. 2 | 3 | // In production situations it is totally fine to build with 4 | // C++ Exception Handling enabled. However, here we want to ensure that 5 | // C++ Exception Handling is not required. 6 | #if defined(_MSC_VER) || defined(__EMSCRIPTEN__) 7 | // Too much trouble making the required cmake changes (see PR #5375). 8 | #else 9 | # ifdef __cpp_exceptions 10 | // https://isocpp.org/std/standing-documents/sd-6-sg10-feature-test-recommendations#__cpp_exceptions 11 | # error This test is meant to be built with C++ Exception Handling disabled, but __cpp_exceptions is defined. 12 | # endif 13 | # ifdef __EXCEPTIONS 14 | // https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html 15 | # error This test is meant to be built with C++ Exception Handling disabled, but __EXCEPTIONS is defined. 16 | # endif 17 | #endif 18 | 19 | // THIS MUST STAY AT THE TOP! 20 | #include // VERY light-weight dependency. 21 | 22 | #include "test_cpp_conduit_traveler_types.h" 23 | 24 | #include 25 | 26 | namespace { 27 | 28 | extern "C" PyObject *wrapGetLuggage(PyObject * /*self*/, PyObject *traveler) { 29 | const auto *cpp_traveler = pybind11_conduit_v1::get_type_pointer_ephemeral< 30 | pybind11_tests::test_cpp_conduit::Traveler>(traveler); 31 | if (cpp_traveler == nullptr) { 32 | return nullptr; 33 | } 34 | return PyUnicode_FromString(cpp_traveler->luggage.c_str()); 35 | } 36 | 37 | extern "C" PyObject *wrapGetPoints(PyObject * /*self*/, PyObject *premium_traveler) { 38 | const auto *cpp_premium_traveler = pybind11_conduit_v1::get_type_pointer_ephemeral< 39 | pybind11_tests::test_cpp_conduit::PremiumTraveler>(premium_traveler); 40 | if (cpp_premium_traveler == nullptr) { 41 | return nullptr; 42 | } 43 | return PyLong_FromLong(static_cast(cpp_premium_traveler->points)); 44 | } 45 | 46 | PyMethodDef ThisMethodDef[] = {{"GetLuggage", wrapGetLuggage, METH_O, nullptr}, 47 | {"GetPoints", wrapGetPoints, METH_O, nullptr}, 48 | {nullptr, nullptr, 0, nullptr}}; 49 | 50 | struct PyModuleDef ThisModuleDef = { 51 | PyModuleDef_HEAD_INIT, // m_base 52 | "exo_planet_c_api", // m_name 53 | nullptr, // m_doc 54 | -1, // m_size 55 | ThisMethodDef, // m_methods 56 | nullptr, // m_slots 57 | nullptr, // m_traverse 58 | nullptr, // m_clear 59 | nullptr // m_free 60 | }; 61 | 62 | } // namespace 63 | 64 | #if defined(WIN32) || defined(_WIN32) 65 | # define EXO_PLANET_C_API_EXPORT __declspec(dllexport) 66 | #else 67 | # define EXO_PLANET_C_API_EXPORT __attribute__((visibility("default"))) 68 | #endif 69 | 70 | extern "C" EXO_PLANET_C_API_EXPORT PyObject *PyInit_exo_planet_c_api() { 71 | PyObject *m = PyModule_Create(&ThisModuleDef); 72 | if (m == nullptr) { 73 | return nullptr; 74 | } 75 | return m; 76 | } 77 | -------------------------------------------------------------------------------- /tests/exo_planet_pybind11.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 The pybind Community. 2 | 3 | #if defined(PYBIND11_INTERNALS_VERSION) 4 | # undef PYBIND11_INTERNALS_VERSION 5 | #endif 6 | #define PYBIND11_INTERNALS_VERSION 900000001 7 | 8 | #include "test_cpp_conduit_traveler_bindings.h" 9 | 10 | namespace pybind11_tests { 11 | namespace test_cpp_conduit { 12 | 13 | PYBIND11_MODULE(exo_planet_pybind11, m) { 14 | wrap_traveler(m); 15 | m.def("wrap_very_lonely_traveler", [m]() { wrap_very_lonely_traveler(m); }); 16 | } 17 | 18 | } // namespace test_cpp_conduit 19 | } // namespace pybind11_tests 20 | -------------------------------------------------------------------------------- /tests/extra_python_package/pytest.ini: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pybind/pybind11/7da1d53df51814d19ebcead549e6c5e0171a3653/tests/extra_python_package/pytest.ini -------------------------------------------------------------------------------- /tests/extra_setuptools/pytest.ini: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pybind/pybind11/7da1d53df51814d19ebcead549e6c5e0171a3653/tests/extra_setuptools/pytest.ini -------------------------------------------------------------------------------- /tests/home_planet_very_lonely_traveler.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 The pybind Community. 2 | 3 | #include "test_cpp_conduit_traveler_bindings.h" 4 | 5 | namespace pybind11_tests { 6 | namespace test_cpp_conduit { 7 | 8 | PYBIND11_MODULE(home_planet_very_lonely_traveler, m) { 9 | m.def("wrap_very_lonely_traveler", [m]() { wrap_very_lonely_traveler(m); }); 10 | } 11 | 12 | } // namespace test_cpp_conduit 13 | } // namespace pybind11_tests 14 | -------------------------------------------------------------------------------- /tests/local_bindings.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "pybind11_tests.h" 3 | 4 | #include 5 | 6 | /// Simple class used to test py::local: 7 | template 8 | class LocalBase { 9 | public: 10 | explicit LocalBase(int i) : i(i) {} 11 | int i = -1; 12 | }; 13 | 14 | /// Registered with py::module_local in both main and secondary modules: 15 | using LocalType = LocalBase<0>; 16 | /// Registered without py::module_local in both modules: 17 | using NonLocalType = LocalBase<1>; 18 | /// A second non-local type (for stl_bind tests): 19 | using NonLocal2 = LocalBase<2>; 20 | /// Tests within-module, different-compilation-unit local definition conflict: 21 | using LocalExternal = LocalBase<3>; 22 | /// Mixed: registered local first, then global 23 | using MixedLocalGlobal = LocalBase<4>; 24 | /// Mixed: global first, then local 25 | using MixedGlobalLocal = LocalBase<5>; 26 | 27 | /// Registered with py::module_local only in the secondary module: 28 | using ExternalType1 = LocalBase<6>; 29 | using ExternalType2 = LocalBase<7>; 30 | 31 | using LocalVec = std::vector; 32 | using LocalVec2 = std::vector; 33 | using LocalMap = std::unordered_map; 34 | using NonLocalVec = std::vector; 35 | using NonLocalVec2 = std::vector; 36 | using NonLocalMap = std::unordered_map; 37 | using NonLocalMap2 = std::unordered_map; 38 | 39 | // Exception that will be caught via the module local translator. 40 | class LocalException : public std::exception { 41 | public: 42 | explicit LocalException(const char *m) : message{m} {} 43 | const char *what() const noexcept override { return message.c_str(); } 44 | 45 | private: 46 | std::string message = ""; 47 | }; 48 | 49 | // Exception that will be registered with register_local_exception_translator 50 | class LocalSimpleException : public std::exception { 51 | public: 52 | explicit LocalSimpleException(const char *m) : message{m} {} 53 | const char *what() const noexcept override { return message.c_str(); } 54 | 55 | private: 56 | std::string message = ""; 57 | }; 58 | 59 | PYBIND11_MAKE_OPAQUE(LocalVec) 60 | PYBIND11_MAKE_OPAQUE(LocalVec2) 61 | PYBIND11_MAKE_OPAQUE(LocalMap) 62 | PYBIND11_MAKE_OPAQUE(NonLocalVec) 63 | // PYBIND11_MAKE_OPAQUE(NonLocalVec2) // same type as LocalVec2 64 | PYBIND11_MAKE_OPAQUE(NonLocalMap) 65 | PYBIND11_MAKE_OPAQUE(NonLocalMap2) 66 | 67 | // Simple bindings (used with the above): 68 | template 69 | py::class_ bind_local(Args &&...args) { 70 | return py::class_(std::forward(args)...).def(py::init()).def("get", [](T &i) { 71 | return i.i + Adjust; 72 | }); 73 | } 74 | 75 | // Simulate a foreign library base class (to match the example in the docs): 76 | namespace pets { 77 | class Pet { 78 | public: 79 | explicit Pet(std::string name) : name_(std::move(name)) {} 80 | std::string name_; 81 | const std::string &name() const { return name_; } 82 | }; 83 | } // namespace pets 84 | 85 | struct MixGL { 86 | int i; 87 | explicit MixGL(int i) : i{i} {} 88 | }; 89 | struct MixGL2 { 90 | int i; 91 | explicit MixGL2(int i) : i{i} {} 92 | }; 93 | -------------------------------------------------------------------------------- /tests/mod_per_interpreter_gil.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace py = pybind11; 4 | 5 | /* Simple test module/test class to check that the referenced internals data of external pybind11 6 | * modules are different across subinterpreters 7 | */ 8 | 9 | PYBIND11_MODULE(mod_per_interpreter_gil, 10 | m, 11 | py::mod_gil_not_used(), 12 | py::multiple_interpreters::per_interpreter_gil()) { 13 | m.def("internals_at", 14 | []() { return reinterpret_cast(&py::detail::get_internals()); }); 15 | m.attr("PYBIND11_HAS_SUBINTERPRETER_SUPPORT") = PYBIND11_HAS_SUBINTERPRETER_SUPPORT; 16 | } 17 | -------------------------------------------------------------------------------- /tests/mod_shared_interpreter_gil.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace py = pybind11; 4 | 5 | /* Simple test module/test class to check that the referenced internals data of external pybind11 6 | * modules are different across subinterpreters 7 | */ 8 | 9 | PYBIND11_MODULE(mod_shared_interpreter_gil, m, py::multiple_interpreters::shared_gil()) { 10 | m.def("internals_at", 11 | []() { return reinterpret_cast(&py::detail::get_internals()); }); 12 | m.attr("PYBIND11_HAS_SUBINTERPRETER_SUPPORT") = PYBIND11_HAS_SUBINTERPRETER_SUPPORT; 13 | } 14 | -------------------------------------------------------------------------------- /tests/pure_cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(Catch 2.13.10) 2 | 3 | if(CATCH_FOUND) 4 | message(STATUS "Building pure C++ tests (not depending on Python) using Catch v${CATCH_VERSION}") 5 | else() 6 | message(STATUS "Catch not detected. Interpreter tests will be skipped. Install Catch headers" 7 | " manually or use `cmake -DDOWNLOAD_CATCH=ON` to fetch them automatically.") 8 | return() 9 | endif() 10 | 11 | add_executable(smart_holder_poc_test smart_holder_poc_test.cpp) 12 | pybind11_enable_warnings(smart_holder_poc_test) 13 | target_link_libraries(smart_holder_poc_test PRIVATE pybind11::headers Catch2::Catch2) 14 | 15 | add_custom_target( 16 | test_pure_cpp 17 | COMMAND "$" 18 | WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") 19 | 20 | add_dependencies(check test_pure_cpp) 21 | -------------------------------------------------------------------------------- /tests/pure_cpp/smart_holder_poc.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2024 The Pybind Development Team. 2 | // All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | #pragma once 6 | 7 | #include "pybind11/detail/struct_smart_holder.h" 8 | 9 | PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) 10 | PYBIND11_NAMESPACE_BEGIN(memory) 11 | PYBIND11_NAMESPACE_BEGIN(smart_holder_poc) // Proof-of-Concept implementations. 12 | 13 | // NOLINTNEXTLINE(bugprone-incorrect-enable-shared-from-this) 14 | struct PrivateESFT : private std::enable_shared_from_this {}; 15 | static_assert(!type_has_shared_from_this(static_cast(nullptr)), 16 | "should detect inaccessible base"); 17 | 18 | template 19 | T &as_lvalue_ref(const smart_holder &hld) { 20 | static const char *context = "as_lvalue_ref"; 21 | hld.ensure_is_populated(context); 22 | hld.ensure_has_pointee(context); 23 | return *hld.as_raw_ptr_unowned(); 24 | } 25 | 26 | template 27 | T &&as_rvalue_ref(const smart_holder &hld) { 28 | static const char *context = "as_rvalue_ref"; 29 | hld.ensure_is_populated(context); 30 | hld.ensure_has_pointee(context); 31 | return std::move(*hld.as_raw_ptr_unowned()); 32 | } 33 | 34 | template 35 | T *as_raw_ptr_release_ownership(smart_holder &hld, 36 | const char *context = "as_raw_ptr_release_ownership") { 37 | hld.ensure_can_release_ownership(context); 38 | T *raw_ptr = hld.as_raw_ptr_unowned(); 39 | hld.release_ownership(); 40 | return raw_ptr; 41 | } 42 | 43 | template > 44 | std::unique_ptr as_unique_ptr(smart_holder &hld) { 45 | static const char *context = "as_unique_ptr"; 46 | hld.ensure_compatible_rtti_uqp_del(context); 47 | hld.ensure_use_count_1(context); 48 | T *raw_ptr = hld.as_raw_ptr_unowned(); 49 | hld.release_ownership(); 50 | // KNOWN DEFECT (see PR #4850): Does not copy the deleter. 51 | return std::unique_ptr(raw_ptr); 52 | } 53 | 54 | PYBIND11_NAMESPACE_END(smart_holder_poc) 55 | PYBIND11_NAMESPACE_END(memory) 56 | PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) 57 | -------------------------------------------------------------------------------- /tests/pyproject.toml: -------------------------------------------------------------------------------- 1 | # Warning: this is currently used for pyodide, and is not a general out-of-tree 2 | # builder for the tests (yet). Specifically, wheels can't be built from SDists. 3 | 4 | [build-system] 5 | requires = ["scikit-build-core"] 6 | build-backend = "scikit_build_core.build" 7 | 8 | [project] 9 | name = "pybind11_tests" 10 | version = "0.0.1" 11 | dependencies = ["pytest", "pytest-timeout"] 12 | 13 | 14 | [dependency-groups] 15 | numpy = ["numpy"] 16 | scipy = ["scipy"] 17 | 18 | 19 | [tool.scikit-build] 20 | build.verbose = true 21 | logging.level = "INFO" 22 | 23 | [tool.scikit-build.cmake.define] 24 | PYBIND11_FINDPYTHON = true 25 | 26 | 27 | [tool.cibuildwheel] 28 | test-sources = ["tests", "pyproject.toml"] 29 | test-command = "python -m pytest -o timeout=0 -p no:cacheprovider tests" 30 | environment.PIP_ONLY_BINARY = "numpy" 31 | environment.PIP_PREFER_BINARY = "1" 32 | pyodide.test-groups = ["numpy", "scipy"] 33 | ios.test-groups = ["numpy"] 34 | ios.xbuild-tools = ["cmake", "ninja"] 35 | ios.environment.PIP_EXTRA_INDEX_URL = "https://pypi.anaconda.org/beeware/simple" 36 | ios.config-settings."cmake.define.CMAKE_CXX_FLAGS" = "-DPYBIND11_HAS_SUBINTERPRETER_SUPPORT=0" 37 | -------------------------------------------------------------------------------- /tests/pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | minversion = 6 3 | norecursedirs = test_* extra_* 4 | xfail_strict = True 5 | addopts = 6 | # show summary of tests 7 | -ra 8 | # capture only Python print and C++ py::print, but not C output (low-level Python errors) 9 | --capture=sys 10 | # Show local info when a failure occurs 11 | --showlocals 12 | log_cli_level = info 13 | filterwarnings = 14 | # make warnings into errors but ignore certain third-party extension issues 15 | error 16 | # somehow, some DeprecationWarnings do not get turned into errors 17 | always::DeprecationWarning 18 | # importing scipy submodules on some version of Python 19 | ignore::ImportWarning 20 | # bogus numpy ABI warning (see numpy/#432) 21 | ignore:.*numpy.dtype size changed.*:RuntimeWarning 22 | ignore:.*numpy.ufunc size changed.*:RuntimeWarning 23 | -------------------------------------------------------------------------------- /tests/requirements.txt: -------------------------------------------------------------------------------- 1 | --extra-index-url=https://www.graalvm.org/python/wheels 2 | --only-binary=:all: 3 | build>=1 4 | numpy~=1.23.0; python_version=="3.8" and platform_python_implementation=="PyPy" 5 | numpy~=1.25.0; python_version=="3.9" and platform_python_implementation=="PyPy" 6 | numpy~=2.2.0; python_version=="3.10" and platform_python_implementation=="PyPy" 7 | numpy~=1.26.0; platform_python_implementation=="GraalVM" and sys_platform=="linux" 8 | numpy~=1.21.5; platform_python_implementation=="CPython" and python_version>="3.8" and python_version<"3.10" 9 | numpy~=1.22.2; platform_python_implementation=="CPython" and python_version=="3.10" 10 | numpy~=1.26.0; platform_python_implementation=="CPython" and python_version>="3.11" and python_version<"3.13" 11 | numpy~=2.2.0; platform_python_implementation=="CPython" and python_version=="3.13" 12 | pytest>=6 13 | pytest-timeout 14 | scipy~=1.5.4; platform_python_implementation=="CPython" and python_version<"3.10" 15 | scipy~=1.8.0; platform_python_implementation=="CPython" and python_version=="3.10" and sys_platform!="win32" 16 | scipy~=1.11.1; platform_python_implementation=="CPython" and python_version>="3.11" and python_version<"3.13" and sys_platform!="win32" 17 | scipy~=1.15.2; platform_python_implementation=="CPython" and python_version=="3.13" and sys_platform!="win32" 18 | tomlkit 19 | -------------------------------------------------------------------------------- /tests/test_async.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | tests/test_async.cpp -- __await__ support 3 | 4 | Copyright (c) 2019 Google Inc. 5 | 6 | All rights reserved. Use of this source code is governed by a 7 | BSD-style license that can be found in the LICENSE file. 8 | */ 9 | 10 | #include "pybind11_tests.h" 11 | 12 | TEST_SUBMODULE(async_module, m) { 13 | struct DoesNotSupportAsync {}; 14 | py::class_(m, "DoesNotSupportAsync").def(py::init<>()); 15 | struct SupportsAsync {}; 16 | py::class_(m, "SupportsAsync") 17 | .def(py::init<>()) 18 | .def("__await__", [](const SupportsAsync &self) -> py::object { 19 | static_cast(self); 20 | py::object loop = py::module_::import("asyncio.events").attr("get_event_loop")(); 21 | py::object f = loop.attr("create_future")(); 22 | f.attr("set_result")(5); 23 | return f.attr("__await__")(); 24 | }); 25 | } 26 | -------------------------------------------------------------------------------- /tests/test_async.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import sys 4 | 5 | import pytest 6 | 7 | asyncio = pytest.importorskip("asyncio") 8 | m = pytest.importorskip("pybind11_tests.async_module") 9 | 10 | if sys.platform.startswith("emscripten"): 11 | pytest.skip("Can't run a new event_loop in pyodide", allow_module_level=True) 12 | 13 | 14 | @pytest.fixture 15 | def event_loop(): 16 | loop = asyncio.new_event_loop() 17 | yield loop 18 | loop.close() 19 | 20 | 21 | async def get_await_result(x): 22 | return await x 23 | 24 | 25 | def test_await(event_loop): 26 | assert event_loop.run_until_complete(get_await_result(m.SupportsAsync())) == 5 27 | 28 | 29 | def test_await_missing(event_loop): 30 | with pytest.raises(TypeError): 31 | event_loop.run_until_complete(get_await_result(m.DoesNotSupportAsync())) 32 | -------------------------------------------------------------------------------- /tests/test_class_release_gil_before_calling_cpp_dtor.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "pybind11_tests.h" 4 | 5 | #include 6 | #include 7 | 8 | namespace pybind11_tests { 9 | namespace class_release_gil_before_calling_cpp_dtor { 10 | 11 | using RegistryType = std::unordered_map; 12 | 13 | static RegistryType &PyGILState_Check_Results() { 14 | static RegistryType singleton; // Local static variables have thread-safe initialization. 15 | return singleton; 16 | } 17 | 18 | template // Using int as a trick to easily generate a series of types. 19 | struct ProbeType { 20 | private: 21 | std::string unique_key; 22 | 23 | public: 24 | explicit ProbeType(const std::string &unique_key) : unique_key{unique_key} {} 25 | 26 | ~ProbeType() { 27 | RegistryType ® = PyGILState_Check_Results(); 28 | assert(reg.count(unique_key) == 0); 29 | reg[unique_key] = PyGILState_Check(); 30 | } 31 | }; 32 | 33 | } // namespace class_release_gil_before_calling_cpp_dtor 34 | } // namespace pybind11_tests 35 | 36 | TEST_SUBMODULE(class_release_gil_before_calling_cpp_dtor, m) { 37 | using namespace pybind11_tests::class_release_gil_before_calling_cpp_dtor; 38 | 39 | py::class_>(m, "ProbeType0").def(py::init()); 40 | 41 | py::class_>(m, "ProbeType1", py::release_gil_before_calling_cpp_dtor()) 42 | .def(py::init()); 43 | 44 | m.def("PopPyGILState_Check_Result", [](const std::string &unique_key) -> std::string { 45 | RegistryType ® = PyGILState_Check_Results(); 46 | if (reg.count(unique_key) == 0) { 47 | return "MISSING"; 48 | } 49 | int res = reg[unique_key]; 50 | reg.erase(unique_key); 51 | return std::to_string(res); 52 | }); 53 | } 54 | -------------------------------------------------------------------------------- /tests/test_class_release_gil_before_calling_cpp_dtor.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import gc 4 | 5 | import pytest 6 | 7 | from pybind11_tests import class_release_gil_before_calling_cpp_dtor as m 8 | 9 | 10 | @pytest.mark.parametrize( 11 | ("probe_type", "unique_key", "expected_result"), 12 | [ 13 | (m.ProbeType0, "without_manipulating_gil", "1"), 14 | (m.ProbeType1, "release_gil_before_calling_cpp_dtor", "0"), 15 | ], 16 | ) 17 | def test_gil_state_check_results(probe_type, unique_key, expected_result): 18 | probe_type(unique_key) 19 | gc.collect() 20 | result = m.PopPyGILState_Check_Result(unique_key) 21 | assert result == expected_result 22 | -------------------------------------------------------------------------------- /tests/test_class_sh_disowning.cpp: -------------------------------------------------------------------------------- 1 | #include "pybind11_tests.h" 2 | 3 | #include 4 | 5 | namespace pybind11_tests { 6 | namespace class_sh_disowning { 7 | 8 | template // Using int as a trick to easily generate a series of types. 9 | struct Atype { 10 | int val = 0; 11 | explicit Atype(int val_) : val{val_} {} 12 | int get() const { return val * 10 + SerNo; } 13 | }; 14 | 15 | int same_twice(std::unique_ptr> at1a, std::unique_ptr> at1b) { 16 | return at1a->get() * 100 + at1b->get() * 10; 17 | } 18 | 19 | int mixed(std::unique_ptr> at1, std::unique_ptr> at2) { 20 | return at1->get() * 200 + at2->get() * 20; 21 | } 22 | 23 | int overloaded(std::unique_ptr> at1, int i) { return at1->get() * 30 + i; } 24 | int overloaded(std::unique_ptr> at2, int i) { return at2->get() * 40 + i; } 25 | 26 | } // namespace class_sh_disowning 27 | } // namespace pybind11_tests 28 | 29 | TEST_SUBMODULE(class_sh_disowning, m) { 30 | using namespace pybind11_tests::class_sh_disowning; 31 | 32 | py::classh>(m, "Atype1").def(py::init()).def("get", &Atype<1>::get); 33 | py::classh>(m, "Atype2").def(py::init()).def("get", &Atype<2>::get); 34 | 35 | m.def("same_twice", same_twice); 36 | 37 | m.def("mixed", mixed); 38 | 39 | m.def("overloaded", (int (*)(std::unique_ptr>, int)) &overloaded); 40 | m.def("overloaded", (int (*)(std::unique_ptr>, int)) &overloaded); 41 | } 42 | -------------------------------------------------------------------------------- /tests/test_class_sh_disowning.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pytest 4 | 5 | from pybind11_tests import class_sh_disowning as m 6 | 7 | 8 | def is_disowned(obj): 9 | try: 10 | obj.get() 11 | except ValueError: 12 | return True 13 | return False 14 | 15 | 16 | def test_same_twice(): 17 | while True: 18 | obj1a = m.Atype1(57) 19 | obj1b = m.Atype1(62) 20 | assert m.same_twice(obj1a, obj1b) == (57 * 10 + 1) * 100 + (62 * 10 + 1) * 10 21 | assert is_disowned(obj1a) 22 | assert is_disowned(obj1b) 23 | obj1c = m.Atype1(0) 24 | with pytest.raises(ValueError): 25 | # Disowning works for one argument, but not both. 26 | m.same_twice(obj1c, obj1c) 27 | assert is_disowned(obj1c) 28 | return # Comment out for manual leak checking (use `top` command). 29 | 30 | 31 | def test_mixed(): 32 | first_pass = True 33 | while True: 34 | obj1a = m.Atype1(90) 35 | obj2a = m.Atype2(25) 36 | assert m.mixed(obj1a, obj2a) == (90 * 10 + 1) * 200 + (25 * 10 + 2) * 20 37 | assert is_disowned(obj1a) 38 | assert is_disowned(obj2a) 39 | 40 | # The C++ order of evaluation of function arguments is (unfortunately) unspecified: 41 | # https://en.cppreference.com/w/cpp/language/eval_order 42 | # Read on. 43 | obj1b = m.Atype1(0) 44 | with pytest.raises(ValueError): 45 | # If the 1st argument is evaluated first, obj1b is disowned before the conversion for 46 | # the already disowned obj2a fails as expected. 47 | m.mixed(obj1b, obj2a) 48 | obj2b = m.Atype2(0) 49 | with pytest.raises(ValueError): 50 | # If the 2nd argument is evaluated first, obj2b is disowned before the conversion for 51 | # the already disowned obj1a fails as expected. 52 | m.mixed(obj1a, obj2b) 53 | 54 | # Either obj1b or obj2b was disowned in the expected failed m.mixed() calls above, but not 55 | # both. 56 | is_disowned_results = (is_disowned(obj1b), is_disowned(obj2b)) 57 | assert is_disowned_results.count(True) == 1 58 | if first_pass: 59 | first_pass = False 60 | ix = is_disowned_results.index(True) + 1 61 | print(f"\nC++ function argument {ix} is evaluated first.") 62 | 63 | return # Comment out for manual leak checking (use `top` command). 64 | 65 | 66 | def test_overloaded(): 67 | while True: 68 | obj1 = m.Atype1(81) 69 | obj2 = m.Atype2(60) 70 | with pytest.raises(TypeError): 71 | m.overloaded(obj1, "NotInt") 72 | assert obj1.get() == 81 * 10 + 1 # Not disowned. 73 | assert m.overloaded(obj1, 3) == (81 * 10 + 1) * 30 + 3 74 | with pytest.raises(TypeError): 75 | m.overloaded(obj2, "NotInt") 76 | assert obj2.get() == 60 * 10 + 2 # Not disowned. 77 | assert m.overloaded(obj2, 2) == (60 * 10 + 2) * 40 + 2 78 | return # Comment out for manual leak checking (use `top` command). 79 | -------------------------------------------------------------------------------- /tests/test_class_sh_disowning_mi.cpp: -------------------------------------------------------------------------------- 1 | #include "pybind11_tests.h" 2 | 3 | #include 4 | 5 | namespace pybind11_tests { 6 | namespace class_sh_disowning_mi { 7 | 8 | // Diamond inheritance (copied from test_multiple_inheritance.cpp). 9 | struct B { 10 | int val_b = 10; 11 | B() = default; 12 | B(const B &) = default; 13 | virtual ~B() = default; 14 | }; 15 | 16 | struct C0 : public virtual B { 17 | int val_c0 = 20; 18 | }; 19 | 20 | struct C1 : public virtual B { 21 | int val_c1 = 21; 22 | }; 23 | 24 | struct D : public C0, public C1 { 25 | int val_d = 30; 26 | }; 27 | 28 | void disown_b(std::unique_ptr) {} 29 | 30 | // test_multiple_inheritance_python 31 | struct Base1 { 32 | explicit Base1(int i) : i(i) {} 33 | int foo() const { return i; } 34 | int i; 35 | }; 36 | 37 | struct Base2 { 38 | explicit Base2(int j) : j(j) {} 39 | int bar() const { return j; } 40 | int j; 41 | }; 42 | 43 | int disown_base1(std::unique_ptr b1) { return b1->i * 2000 + 1; } 44 | int disown_base2(std::unique_ptr b2) { return b2->j * 2000 + 2; } 45 | 46 | } // namespace class_sh_disowning_mi 47 | } // namespace pybind11_tests 48 | 49 | TEST_SUBMODULE(class_sh_disowning_mi, m) { 50 | using namespace pybind11_tests::class_sh_disowning_mi; 51 | 52 | py::classh(m, "B") 53 | .def(py::init<>()) 54 | .def_readonly("val_b", &D::val_b) 55 | .def("b", [](B *self) { return self; }) 56 | .def("get", [](const B &self) { return self.val_b; }); 57 | 58 | py::classh(m, "C0") 59 | .def(py::init<>()) 60 | .def_readonly("val_c0", &D::val_c0) 61 | .def("c0", [](C0 *self) { return self; }) 62 | .def("get", [](const C0 &self) { return self.val_b * 100 + self.val_c0; }); 63 | 64 | py::classh(m, "C1") 65 | .def(py::init<>()) 66 | .def_readonly("val_c1", &D::val_c1) 67 | .def("c1", [](C1 *self) { return self; }) 68 | .def("get", [](const C1 &self) { return self.val_b * 100 + self.val_c1; }); 69 | 70 | py::classh(m, "D") 71 | .def(py::init<>()) 72 | .def_readonly("val_d", &D::val_d) 73 | .def("d", [](D *self) { return self; }) 74 | .def("get", [](const D &self) { 75 | return self.val_b * 1000000 + self.val_c0 * 10000 + self.val_c1 * 100 + self.val_d; 76 | }); 77 | 78 | m.def("disown_b", disown_b); 79 | 80 | // test_multiple_inheritance_python 81 | py::classh(m, "Base1").def(py::init()).def("foo", &Base1::foo); 82 | py::classh(m, "Base2").def(py::init()).def("bar", &Base2::bar); 83 | m.def("disown_base1", disown_base1); 84 | m.def("disown_base2", disown_base2); 85 | } 86 | -------------------------------------------------------------------------------- /tests/test_class_sh_factory_constructors.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pytest 4 | 5 | from pybind11_tests import class_sh_factory_constructors as m 6 | 7 | 8 | def test_atyp_factories(): 9 | assert m.atyp_valu().get_mtxt() == "Valu" 10 | assert m.atyp_rref().get_mtxt() == "Rref" 11 | # sert m.atyp_cref().get_mtxt() == "Cref" 12 | # sert m.atyp_mref().get_mtxt() == "Mref" 13 | # sert m.atyp_cptr().get_mtxt() == "Cptr" 14 | assert m.atyp_mptr().get_mtxt() == "Mptr" 15 | assert m.atyp_shmp().get_mtxt() == "Shmp" 16 | # sert m.atyp_shcp().get_mtxt() == "Shcp" 17 | assert m.atyp_uqmp().get_mtxt() == "Uqmp" 18 | # sert m.atyp_uqcp().get_mtxt() == "Uqcp" 19 | assert m.atyp_udmp().get_mtxt() == "Udmp" 20 | # sert m.atyp_udcp().get_mtxt() == "Udcp" 21 | 22 | 23 | @pytest.mark.parametrize( 24 | ("init_args", "expected"), 25 | [ 26 | ((3,), 300), 27 | ((5, 7), 570), 28 | ((9, 11, 13), 1023), 29 | ], 30 | ) 31 | def test_with_alias_success(init_args, expected): 32 | assert m.with_alias(*init_args).val == expected 33 | 34 | 35 | @pytest.mark.parametrize( 36 | ("num_init_args", "smart_ptr"), 37 | [ 38 | (4, "std::unique_ptr"), 39 | (5, "std::shared_ptr"), 40 | ], 41 | ) 42 | def test_with_alias_invalid(num_init_args, smart_ptr): 43 | class PyDrvdWithAlias(m.with_alias): 44 | pass 45 | 46 | with pytest.raises(TypeError) as excinfo: 47 | PyDrvdWithAlias(*((0,) * num_init_args)) 48 | assert ( 49 | str(excinfo.value) 50 | == "pybind11::init(): construction failed: returned " 51 | + smart_ptr 52 | + " pointee is not an alias instance" 53 | ) 54 | -------------------------------------------------------------------------------- /tests/test_class_sh_inheritance.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from pybind11_tests import class_sh_inheritance as m 4 | 5 | 6 | def test_rtrn_mptr_drvd_pass_cptr_base(): 7 | d = m.rtrn_mptr_drvd() 8 | i = m.pass_cptr_base(d) # load_impl Case 2a 9 | assert i == 2 * 100 + 11 10 | 11 | 12 | def test_rtrn_shmp_drvd_pass_shcp_base(): 13 | d = m.rtrn_shmp_drvd() 14 | i = m.pass_shcp_base(d) # load_impl Case 2a 15 | assert i == 2 * 100 + 21 16 | 17 | 18 | def test_rtrn_mptr_drvd_up_cast_pass_cptr_drvd(): 19 | b = m.rtrn_mptr_drvd_up_cast() 20 | # the base return is down-cast immediately. 21 | assert b.__class__.__name__ == "drvd" 22 | i = m.pass_cptr_drvd(b) 23 | assert i == 2 * 100 + 12 24 | 25 | 26 | def test_rtrn_shmp_drvd_up_cast_pass_shcp_drvd(): 27 | b = m.rtrn_shmp_drvd_up_cast() 28 | # the base return is down-cast immediately. 29 | assert b.__class__.__name__ == "drvd" 30 | i = m.pass_shcp_drvd(b) 31 | assert i == 2 * 100 + 22 32 | 33 | 34 | def test_rtrn_mptr_drvd2_pass_cptr_bases(): 35 | d = m.rtrn_mptr_drvd2() 36 | i1 = m.pass_cptr_base1(d) # load_impl Case 2c 37 | assert i1 == 3 * 110 + 4 * 120 + 21 38 | i2 = m.pass_cptr_base2(d) 39 | assert i2 == 3 * 110 + 4 * 120 + 22 40 | 41 | 42 | def test_rtrn_mptr_drvd2_up_casts_pass_cptr_drvd2(): 43 | b1 = m.rtrn_mptr_drvd2_up_cast1() 44 | assert b1.__class__.__name__ == "drvd2" 45 | i1 = m.pass_cptr_drvd2(b1) 46 | assert i1 == 3 * 110 + 4 * 120 + 23 47 | b2 = m.rtrn_mptr_drvd2_up_cast2() 48 | assert b2.__class__.__name__ == "drvd2" 49 | i2 = m.pass_cptr_drvd2(b2) 50 | assert i2 == 3 * 110 + 4 * 120 + 23 51 | 52 | 53 | def test_python_drvd2(): 54 | class Drvd2(m.base1, m.base2): 55 | def __init__(self): 56 | m.base1.__init__(self) 57 | m.base2.__init__(self) 58 | 59 | d = Drvd2() 60 | i1 = m.pass_cptr_base1(d) # load_impl Case 2b 61 | assert i1 == 110 + 21 62 | i2 = m.pass_cptr_base2(d) 63 | assert i2 == 120 + 22 64 | -------------------------------------------------------------------------------- /tests/test_class_sh_mi_thunks.cpp: -------------------------------------------------------------------------------- 1 | #include "pybind11_tests.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace test_class_sh_mi_thunks { 8 | 9 | // For general background: https://shaharmike.com/cpp/vtable-part2/ 10 | // C++ vtables - Part 2 - Multiple Inheritance 11 | // ... the compiler creates a 'thunk' method that corrects `this` ... 12 | 13 | struct Base0 { 14 | virtual ~Base0() = default; 15 | Base0() = default; 16 | Base0(const Base0 &) = delete; 17 | }; 18 | 19 | struct Base1 { 20 | virtual ~Base1() = default; 21 | // Using `vector` here because it is known to make this test very sensitive to bugs. 22 | std::vector vec = {1, 2, 3, 4, 5}; 23 | Base1() = default; 24 | Base1(const Base1 &) = delete; 25 | }; 26 | 27 | struct Derived : Base1, Base0 { 28 | ~Derived() override = default; 29 | Derived() = default; 30 | Derived(const Derived &) = delete; 31 | }; 32 | 33 | } // namespace test_class_sh_mi_thunks 34 | 35 | TEST_SUBMODULE(class_sh_mi_thunks, m) { 36 | using namespace test_class_sh_mi_thunks; 37 | 38 | m.def("ptrdiff_drvd_base0", []() { 39 | auto drvd = std::unique_ptr(new Derived); 40 | auto *base0 = dynamic_cast(drvd.get()); 41 | return std::ptrdiff_t(reinterpret_cast(drvd.get()) 42 | - reinterpret_cast(base0)); 43 | }); 44 | 45 | py::classh(m, "Base0"); 46 | py::classh(m, "Base1"); 47 | py::classh(m, "Derived"); 48 | 49 | m.def( 50 | "get_drvd_as_base0_raw_ptr", 51 | []() { 52 | auto *drvd = new Derived; 53 | auto *base0 = dynamic_cast(drvd); 54 | return base0; 55 | }, 56 | py::return_value_policy::take_ownership); 57 | 58 | m.def("get_drvd_as_base0_shared_ptr", []() { 59 | auto drvd = std::make_shared(); 60 | auto base0 = std::dynamic_pointer_cast(drvd); 61 | return base0; 62 | }); 63 | 64 | m.def("get_drvd_as_base0_unique_ptr", []() { 65 | auto drvd = std::unique_ptr(new Derived); 66 | auto base0 = std::unique_ptr(std::move(drvd)); 67 | return base0; 68 | }); 69 | 70 | m.def("vec_size_base0_raw_ptr", [](const Base0 *obj) { 71 | const auto *obj_der = dynamic_cast(obj); 72 | if (obj_der == nullptr) { 73 | return std::size_t(0); 74 | } 75 | return obj_der->vec.size(); 76 | }); 77 | 78 | m.def("vec_size_base0_shared_ptr", [](const std::shared_ptr &obj) -> std::size_t { 79 | const auto obj_der = std::dynamic_pointer_cast(obj); 80 | if (!obj_der) { 81 | return std::size_t(0); 82 | } 83 | return obj_der->vec.size(); 84 | }); 85 | 86 | m.def("vec_size_base0_unique_ptr", [](std::unique_ptr obj) -> std::size_t { 87 | const auto *obj_der = dynamic_cast(obj.get()); 88 | if (obj_der == nullptr) { 89 | return std::size_t(0); 90 | } 91 | return obj_der->vec.size(); 92 | }); 93 | } 94 | -------------------------------------------------------------------------------- /tests/test_class_sh_mi_thunks.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pytest 4 | 5 | from pybind11_tests import class_sh_mi_thunks as m 6 | 7 | 8 | def test_ptrdiff_drvd_base0(): 9 | ptrdiff = m.ptrdiff_drvd_base0() 10 | # A failure here does not (necessarily) mean that there is a bug, but that 11 | # test_class_sh_mi_thunks is not exercising what it is supposed to. 12 | # If this ever fails on some platforms: use pytest.skip() 13 | # If this ever fails on all platforms: don't know, seems extremely unlikely. 14 | assert ptrdiff != 0 15 | 16 | 17 | @pytest.mark.parametrize( 18 | "vec_size_fn", 19 | [ 20 | m.vec_size_base0_raw_ptr, 21 | m.vec_size_base0_shared_ptr, 22 | ], 23 | ) 24 | @pytest.mark.parametrize( 25 | "get_fn", 26 | [ 27 | m.get_drvd_as_base0_raw_ptr, 28 | m.get_drvd_as_base0_shared_ptr, 29 | m.get_drvd_as_base0_unique_ptr, 30 | ], 31 | ) 32 | def test_get_vec_size_raw_shared(get_fn, vec_size_fn): 33 | obj = get_fn() 34 | assert vec_size_fn(obj) == 5 35 | 36 | 37 | @pytest.mark.parametrize( 38 | "get_fn", [m.get_drvd_as_base0_raw_ptr, m.get_drvd_as_base0_unique_ptr] 39 | ) 40 | def test_get_vec_size_unique(get_fn): 41 | obj = get_fn() 42 | assert m.vec_size_base0_unique_ptr(obj) == 5 43 | with pytest.raises(ValueError, match="Python instance was disowned"): 44 | m.vec_size_base0_unique_ptr(obj) 45 | 46 | 47 | def test_get_shared_vec_size_unique(): 48 | obj = m.get_drvd_as_base0_shared_ptr() 49 | with pytest.raises(ValueError) as exc_info: 50 | m.vec_size_base0_unique_ptr(obj) 51 | assert ( 52 | str(exc_info.value) == "Cannot disown external shared_ptr (load_as_unique_ptr)." 53 | ) 54 | -------------------------------------------------------------------------------- /tests/test_class_sh_property_non_owning.cpp: -------------------------------------------------------------------------------- 1 | #include "pybind11_tests.h" 2 | 3 | #include 4 | #include 5 | 6 | namespace test_class_sh_property_non_owning { 7 | 8 | struct CoreField { 9 | explicit CoreField(int int_value = -99) : int_value{int_value} {} 10 | int int_value; 11 | }; 12 | 13 | struct DataField { 14 | DataField(int i_value, int i_shared, int i_unique) 15 | : core_fld_value{i_value}, core_fld_shared_ptr{new CoreField{i_shared}}, 16 | core_fld_raw_ptr{core_fld_shared_ptr.get()}, 17 | core_fld_unique_ptr{new CoreField{i_unique}} {} 18 | CoreField core_fld_value; 19 | std::shared_ptr core_fld_shared_ptr; 20 | CoreField *core_fld_raw_ptr; 21 | std::unique_ptr core_fld_unique_ptr; 22 | }; 23 | 24 | struct DataFieldsHolder { 25 | private: 26 | std::vector vec; 27 | 28 | public: 29 | explicit DataFieldsHolder(std::size_t vec_size) { 30 | for (std::size_t i = 0; i < vec_size; i++) { 31 | int i11 = static_cast(i) * 11; 32 | vec.emplace_back(13 + i11, 14 + i11, 15 + i11); 33 | } 34 | } 35 | 36 | DataField *vec_at(std::size_t index) { 37 | if (index >= vec.size()) { 38 | return nullptr; 39 | } 40 | return &vec[index]; 41 | } 42 | }; 43 | 44 | } // namespace test_class_sh_property_non_owning 45 | 46 | using namespace test_class_sh_property_non_owning; 47 | 48 | TEST_SUBMODULE(class_sh_property_non_owning, m) { 49 | py::classh(m, "CoreField").def_readwrite("int_value", &CoreField::int_value); 50 | 51 | py::classh(m, "DataField") 52 | .def_readonly("core_fld_value_ro", &DataField::core_fld_value) 53 | .def_readwrite("core_fld_value_rw", &DataField::core_fld_value) 54 | .def_readonly("core_fld_shared_ptr_ro", &DataField::core_fld_shared_ptr) 55 | .def_readwrite("core_fld_shared_ptr_rw", &DataField::core_fld_shared_ptr) 56 | .def_readonly("core_fld_raw_ptr_ro", &DataField::core_fld_raw_ptr) 57 | .def_readwrite("core_fld_raw_ptr_rw", &DataField::core_fld_raw_ptr) 58 | .def_readwrite("core_fld_unique_ptr_rw", &DataField::core_fld_unique_ptr); 59 | 60 | py::classh(m, "DataFieldsHolder") 61 | .def(py::init()) 62 | .def("vec_at", &DataFieldsHolder::vec_at, py::return_value_policy::reference_internal); 63 | } 64 | -------------------------------------------------------------------------------- /tests/test_class_sh_property_non_owning.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pytest 4 | 5 | from pybind11_tests import class_sh_property_non_owning as m 6 | 7 | 8 | @pytest.mark.parametrize("persistent_holder", [True, False]) 9 | @pytest.mark.parametrize( 10 | ("core_fld", "expected"), 11 | [ 12 | ("core_fld_value_ro", (13, 24)), 13 | ("core_fld_value_rw", (13, 24)), 14 | ("core_fld_shared_ptr_ro", (14, 25)), 15 | ("core_fld_shared_ptr_rw", (14, 25)), 16 | ("core_fld_raw_ptr_ro", (14, 25)), 17 | ("core_fld_raw_ptr_rw", (14, 25)), 18 | ("core_fld_unique_ptr_rw", (15, 26)), 19 | ], 20 | ) 21 | def test_core_fld_common(core_fld, expected, persistent_holder): 22 | if persistent_holder: 23 | h = m.DataFieldsHolder(2) 24 | for i, exp in enumerate(expected): 25 | c = getattr(h.vec_at(i), core_fld) 26 | assert c.int_value == exp 27 | else: 28 | for i, exp in enumerate(expected): 29 | c = getattr(m.DataFieldsHolder(2).vec_at(i), core_fld) 30 | assert c.int_value == exp 31 | -------------------------------------------------------------------------------- /tests/test_class_sh_shared_ptr_copy_move.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from pybind11_tests import class_sh_shared_ptr_copy_move as m 4 | 5 | 6 | def test_shptr_copy(): 7 | txt = m.test_ShPtr_copy()[0].get_history() 8 | assert txt == "FooShPtr_copy" 9 | 10 | 11 | def test_smhld_copy(): 12 | txt = m.test_SmHld_copy()[0].get_history() 13 | assert txt == "FooSmHld_copy" 14 | 15 | 16 | def test_shptr_move(): 17 | txt = m.test_ShPtr_move()[0].get_history() 18 | assert txt == "FooShPtr_move" 19 | 20 | 21 | def test_smhld_move(): 22 | txt = m.test_SmHld_move()[0].get_history() 23 | assert txt == "FooSmHld_move" 24 | 25 | 26 | def _check_property(foo_typ, prop_typ, policy): 27 | o = m.Outer() 28 | name = f"{foo_typ}_{prop_typ}_{policy}" 29 | history = f"Foo{foo_typ}_Outer" 30 | f = getattr(o, name) 31 | assert f.get_history() == history 32 | # and try again to check that o did not get changed 33 | f = getattr(o, name) 34 | assert f.get_history() == history 35 | 36 | 37 | def test_properties(): 38 | for prop_typ in ("readonly", "readwrite", "property_readonly"): 39 | for foo_typ in ("ShPtr", "SmHld"): 40 | for policy in ("default", "copy", "move"): 41 | _check_property(foo_typ, prop_typ, policy) 42 | -------------------------------------------------------------------------------- /tests/test_class_sh_trampoline_basic.cpp: -------------------------------------------------------------------------------- 1 | #include "pybind11_tests.h" 2 | 3 | #include 4 | 5 | namespace pybind11_tests { 6 | namespace class_sh_trampoline_basic { 7 | 8 | struct Abase { 9 | int val = 0; 10 | virtual ~Abase() = default; 11 | explicit Abase(int val_) : val{val_} {} 12 | int Get() const { return val * 10 + 3; } 13 | virtual int Add(int other_val) const = 0; 14 | 15 | // Some compilers complain about implicitly defined versions of some of the following: 16 | Abase(const Abase &) = default; 17 | Abase(Abase &&) noexcept = default; 18 | Abase &operator=(const Abase &) = default; 19 | Abase &operator=(Abase &&) noexcept = default; 20 | }; 21 | 22 | struct AbaseAlias : Abase, py::trampoline_self_life_support { 23 | using Abase::Abase; 24 | 25 | int Add(int other_val) const override { 26 | PYBIND11_OVERRIDE_PURE(int, /* Return type */ 27 | Abase, /* Parent class */ 28 | Add, /* Name of function in C++ (must match Python name) */ 29 | other_val); 30 | } 31 | }; 32 | 33 | int AddInCppRawPtr(const Abase *obj, int other_val) { return obj->Add(other_val) * 10 + 7; } 34 | 35 | int AddInCppSharedPtr(const std::shared_ptr &obj, int other_val) { 36 | return obj->Add(other_val) * 100 + 11; 37 | } 38 | 39 | int AddInCppUniquePtr(std::unique_ptr obj, int other_val) { 40 | return obj->Add(other_val) * 100 + 13; 41 | } 42 | 43 | } // namespace class_sh_trampoline_basic 44 | } // namespace pybind11_tests 45 | 46 | using namespace pybind11_tests::class_sh_trampoline_basic; 47 | 48 | TEST_SUBMODULE(class_sh_trampoline_basic, m) { 49 | py::classh(m, "Abase") 50 | .def(py::init(), py::arg("val")) 51 | .def("Get", &Abase::Get) 52 | .def("Add", &Abase::Add, py::arg("other_val")); 53 | 54 | m.def("AddInCppRawPtr", AddInCppRawPtr, py::arg("obj"), py::arg("other_val")); 55 | m.def("AddInCppSharedPtr", AddInCppSharedPtr, py::arg("obj"), py::arg("other_val")); 56 | m.def("AddInCppUniquePtr", AddInCppUniquePtr, py::arg("obj"), py::arg("other_val")); 57 | } 58 | -------------------------------------------------------------------------------- /tests/test_class_sh_trampoline_basic.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from pybind11_tests import class_sh_trampoline_basic as m 4 | 5 | 6 | class PyDrvd(m.Abase): 7 | def __init__(self, val): 8 | super().__init__(val) 9 | 10 | def Add(self, other_val): 11 | return self.Get() * 100 + other_val 12 | 13 | 14 | def test_drvd_add(): 15 | drvd = PyDrvd(74) 16 | assert drvd.Add(38) == (74 * 10 + 3) * 100 + 38 17 | 18 | 19 | def test_drvd_add_in_cpp_raw_ptr(): 20 | drvd = PyDrvd(52) 21 | assert m.AddInCppRawPtr(drvd, 27) == ((52 * 10 + 3) * 100 + 27) * 10 + 7 22 | 23 | 24 | def test_drvd_add_in_cpp_shared_ptr(): 25 | while True: 26 | drvd = PyDrvd(36) 27 | assert m.AddInCppSharedPtr(drvd, 56) == ((36 * 10 + 3) * 100 + 56) * 100 + 11 28 | return # Comment out for manual leak checking (use `top` command). 29 | 30 | 31 | def test_drvd_add_in_cpp_unique_ptr(): 32 | while True: 33 | drvd = PyDrvd(25) 34 | assert m.AddInCppUniquePtr(drvd, 83) == ((25 * 10 + 3) * 100 + 83) * 100 + 13 35 | return # Comment out for manual leak checking (use `top` command). 36 | -------------------------------------------------------------------------------- /tests/test_class_sh_trampoline_self_life_support.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pytest 4 | 5 | import pybind11_tests.class_sh_trampoline_self_life_support as m 6 | 7 | 8 | class PyBig5(m.Big5): 9 | pass 10 | 11 | 12 | def test_m_big5(): 13 | obj = m.Big5("Seed") 14 | assert obj.history == "Seed" 15 | o1, o2 = m.action(obj, 0) 16 | assert o1 is not obj 17 | assert o1.history == "Seed" 18 | with pytest.raises(ValueError) as excinfo: 19 | _ = obj.history 20 | assert "Python instance was disowned" in str(excinfo.value) 21 | assert o2 is None 22 | 23 | 24 | @pytest.mark.parametrize( 25 | ("action_id", "expected_history"), 26 | [ 27 | (0, "Seed_CpCtor"), 28 | (1, "Seed_MvCtor"), 29 | (2, "Seed_OpEqLv"), 30 | (3, "Seed_OpEqRv"), 31 | ], 32 | ) 33 | def test_py_big5(action_id, expected_history): 34 | obj = PyBig5("Seed") 35 | assert obj.history == "Seed" 36 | o1, o2 = m.action(obj, action_id) 37 | assert o1 is obj 38 | assert o2.history == expected_history 39 | -------------------------------------------------------------------------------- /tests/test_class_sh_trampoline_unique_ptr.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 The Pybind Development Team. 2 | // All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | #include "pybind11/trampoline_self_life_support.h" 6 | #include "pybind11_tests.h" 7 | 8 | #include 9 | 10 | namespace pybind11_tests { 11 | namespace class_sh_trampoline_unique_ptr { 12 | 13 | class Class { 14 | public: 15 | virtual ~Class() = default; 16 | 17 | void setVal(std::uint64_t val) { val_ = val; } 18 | std::uint64_t getVal() const { return val_; } 19 | 20 | virtual std::unique_ptr clone() const = 0; 21 | virtual int foo() const = 0; 22 | 23 | protected: 24 | Class() = default; 25 | 26 | // Some compilers complain about implicitly defined versions of some of the following: 27 | Class(const Class &) = default; 28 | 29 | private: 30 | std::uint64_t val_ = 0; 31 | }; 32 | 33 | } // namespace class_sh_trampoline_unique_ptr 34 | } // namespace pybind11_tests 35 | 36 | namespace pybind11_tests { 37 | namespace class_sh_trampoline_unique_ptr { 38 | 39 | class PyClass : public Class, public py::trampoline_self_life_support { 40 | public: 41 | std::unique_ptr clone() const override { 42 | PYBIND11_OVERRIDE_PURE(std::unique_ptr, Class, clone); 43 | } 44 | 45 | int foo() const override { PYBIND11_OVERRIDE_PURE(int, Class, foo); } 46 | }; 47 | 48 | } // namespace class_sh_trampoline_unique_ptr 49 | } // namespace pybind11_tests 50 | 51 | TEST_SUBMODULE(class_sh_trampoline_unique_ptr, m) { 52 | using namespace pybind11_tests::class_sh_trampoline_unique_ptr; 53 | 54 | py::classh(m, "Class") 55 | .def(py::init<>()) 56 | .def("set_val", &Class::setVal) 57 | .def("get_val", &Class::getVal) 58 | .def("clone", &Class::clone) 59 | .def("foo", &Class::foo); 60 | 61 | m.def("clone", [](const Class &obj) { return obj.clone(); }); 62 | m.def("clone_and_foo", [](const Class &obj) { return obj.clone()->foo(); }); 63 | } 64 | -------------------------------------------------------------------------------- /tests/test_class_sh_trampoline_unique_ptr.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pybind11_tests.class_sh_trampoline_unique_ptr as m 4 | 5 | 6 | class MyClass(m.Class): 7 | def foo(self): 8 | return 10 + self.get_val() 9 | 10 | def clone(self): 11 | cloned = MyClass() 12 | cloned.set_val(self.get_val() + 3) 13 | return cloned 14 | 15 | 16 | def test_m_clone(): 17 | obj = MyClass() 18 | while True: 19 | obj.set_val(5) 20 | obj = m.clone(obj) 21 | assert obj.get_val() == 5 + 3 22 | assert obj.foo() == 10 + 5 + 3 23 | return # Comment out for manual leak checking (use `top` command). 24 | 25 | 26 | def test_m_clone_and_foo(): 27 | obj = MyClass() 28 | obj.set_val(7) 29 | while True: 30 | assert m.clone_and_foo(obj) == 10 + 7 + 3 31 | return # Comment out for manual leak checking (use `top` command). 32 | -------------------------------------------------------------------------------- /tests/test_class_sh_unique_ptr_custom_deleter.cpp: -------------------------------------------------------------------------------- 1 | #include "pybind11_tests.h" 2 | 3 | #include 4 | 5 | namespace pybind11_tests { 6 | namespace class_sh_unique_ptr_custom_deleter { 7 | 8 | // Reduced from a PyCLIF use case in the wild by @wangxf123456. 9 | class Pet { 10 | public: 11 | using Ptr = std::unique_ptr>; 12 | 13 | std::string name; 14 | 15 | static Ptr New(const std::string &name) { 16 | return Ptr(new Pet(name), std::default_delete()); 17 | } 18 | 19 | private: 20 | explicit Pet(const std::string &name) : name(name) {} 21 | }; 22 | 23 | TEST_SUBMODULE(class_sh_unique_ptr_custom_deleter, m) { 24 | py::classh(m, "Pet").def_readwrite("name", &Pet::name); 25 | 26 | m.def("create", &Pet::New); 27 | } 28 | 29 | } // namespace class_sh_unique_ptr_custom_deleter 30 | } // namespace pybind11_tests 31 | -------------------------------------------------------------------------------- /tests/test_class_sh_unique_ptr_custom_deleter.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from pybind11_tests import class_sh_unique_ptr_custom_deleter as m 4 | 5 | 6 | def test_create(): 7 | pet = m.create("abc") 8 | assert pet.name == "abc" 9 | -------------------------------------------------------------------------------- /tests/test_class_sh_unique_ptr_member.cpp: -------------------------------------------------------------------------------- 1 | #include "pybind11_tests.h" 2 | 3 | #include 4 | 5 | namespace pybind11_tests { 6 | namespace class_sh_unique_ptr_member { 7 | 8 | class pointee { // NOT copyable. 9 | public: 10 | pointee() = default; 11 | 12 | int get_int() const { return 213; } 13 | 14 | pointee(const pointee &) = delete; 15 | pointee(pointee &&) = delete; 16 | pointee &operator=(const pointee &) = delete; 17 | pointee &operator=(pointee &&) = delete; 18 | }; 19 | 20 | inline std::unique_ptr make_unique_pointee() { 21 | return std::unique_ptr(new pointee); 22 | } 23 | 24 | class ptr_owner { 25 | public: 26 | explicit ptr_owner(std::unique_ptr ptr) : ptr_(std::move(ptr)) {} 27 | 28 | bool is_owner() const { return bool(ptr_); } 29 | 30 | std::unique_ptr give_up_ownership_via_unique_ptr() { return std::move(ptr_); } 31 | std::shared_ptr give_up_ownership_via_shared_ptr() { return std::move(ptr_); } 32 | 33 | private: 34 | std::unique_ptr ptr_; 35 | }; 36 | 37 | TEST_SUBMODULE(class_sh_unique_ptr_member, m) { 38 | py::classh(m, "pointee").def(py::init<>()).def("get_int", &pointee::get_int); 39 | 40 | m.def("make_unique_pointee", make_unique_pointee); 41 | 42 | py::class_(m, "ptr_owner") 43 | .def(py::init>(), py::arg("ptr")) 44 | .def("is_owner", &ptr_owner::is_owner) 45 | .def("give_up_ownership_via_unique_ptr", &ptr_owner::give_up_ownership_via_unique_ptr) 46 | .def("give_up_ownership_via_shared_ptr", &ptr_owner::give_up_ownership_via_shared_ptr); 47 | } 48 | 49 | } // namespace class_sh_unique_ptr_member 50 | } // namespace pybind11_tests 51 | -------------------------------------------------------------------------------- /tests/test_class_sh_unique_ptr_member.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pytest 4 | 5 | from pybind11_tests import class_sh_unique_ptr_member as m 6 | 7 | 8 | def test_make_unique_pointee(): 9 | obj = m.make_unique_pointee() 10 | assert obj.get_int() == 213 11 | 12 | 13 | @pytest.mark.parametrize( 14 | "give_up_ownership_via", 15 | ["give_up_ownership_via_unique_ptr", "give_up_ownership_via_shared_ptr"], 16 | ) 17 | def test_pointee_and_ptr_owner(give_up_ownership_via): 18 | obj = m.pointee() 19 | assert obj.get_int() == 213 20 | owner = m.ptr_owner(obj) 21 | with pytest.raises(ValueError, match="Python instance was disowned"): 22 | obj.get_int() 23 | assert owner.is_owner() 24 | reclaimed = getattr(owner, give_up_ownership_via)() 25 | assert not owner.is_owner() 26 | assert reclaimed.get_int() == 213 27 | -------------------------------------------------------------------------------- /tests/test_class_sh_virtual_py_cpp_mix.cpp: -------------------------------------------------------------------------------- 1 | #include "pybind11_tests.h" 2 | 3 | #include 4 | 5 | namespace pybind11_tests { 6 | namespace class_sh_virtual_py_cpp_mix { 7 | 8 | class Base { 9 | public: 10 | virtual ~Base() = default; 11 | virtual int get() const { return 101; } 12 | 13 | // Some compilers complain about implicitly defined versions of some of the following: 14 | Base() = default; 15 | Base(const Base &) = default; 16 | }; 17 | 18 | class CppDerivedPlain : public Base { 19 | public: 20 | int get() const override { return 202; } 21 | }; 22 | 23 | class CppDerived : public Base { 24 | public: 25 | int get() const override { return 212; } 26 | }; 27 | 28 | int get_from_cpp_plainc_ptr(const Base *b) { return b->get() + 4000; } 29 | 30 | int get_from_cpp_unique_ptr(std::unique_ptr b) { return b->get() + 5000; } 31 | 32 | struct BaseVirtualOverrider : Base, py::trampoline_self_life_support { 33 | using Base::Base; 34 | 35 | int get() const override { PYBIND11_OVERRIDE(int, Base, get); } 36 | }; 37 | 38 | struct CppDerivedVirtualOverrider : CppDerived, py::trampoline_self_life_support { 39 | using CppDerived::CppDerived; 40 | 41 | int get() const override { PYBIND11_OVERRIDE(int, CppDerived, get); } 42 | }; 43 | 44 | } // namespace class_sh_virtual_py_cpp_mix 45 | } // namespace pybind11_tests 46 | 47 | using namespace pybind11_tests::class_sh_virtual_py_cpp_mix; 48 | 49 | TEST_SUBMODULE(class_sh_virtual_py_cpp_mix, m) { 50 | py::classh(m, "Base").def(py::init<>()).def("get", &Base::get); 51 | 52 | py::classh(m, "CppDerivedPlain").def(py::init<>()); 53 | 54 | py::classh(m, "CppDerived").def(py::init<>()); 55 | 56 | m.def("get_from_cpp_plainc_ptr", get_from_cpp_plainc_ptr, py::arg("b")); 57 | m.def("get_from_cpp_unique_ptr", get_from_cpp_unique_ptr, py::arg("b")); 58 | } 59 | -------------------------------------------------------------------------------- /tests/test_class_sh_virtual_py_cpp_mix.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pytest 4 | 5 | from pybind11_tests import class_sh_virtual_py_cpp_mix as m 6 | 7 | 8 | class PyBase(m.Base): # Avoiding name PyDerived, for more systematic naming. 9 | def __init__(self): 10 | m.Base.__init__(self) 11 | 12 | def get(self): 13 | return 323 14 | 15 | 16 | class PyCppDerived(m.CppDerived): 17 | def __init__(self): 18 | m.CppDerived.__init__(self) 19 | 20 | def get(self): 21 | return 434 22 | 23 | 24 | @pytest.mark.parametrize( 25 | ("ctor", "expected"), 26 | [ 27 | (m.Base, 101), 28 | (PyBase, 323), 29 | (m.CppDerivedPlain, 202), 30 | (m.CppDerived, 212), 31 | (PyCppDerived, 434), 32 | ], 33 | ) 34 | def test_base_get(ctor, expected): 35 | obj = ctor() 36 | assert obj.get() == expected 37 | 38 | 39 | @pytest.mark.parametrize( 40 | ("ctor", "expected"), 41 | [ 42 | (m.Base, 4101), 43 | (PyBase, 4323), 44 | (m.CppDerivedPlain, 4202), 45 | (m.CppDerived, 4212), 46 | (PyCppDerived, 4434), 47 | ], 48 | ) 49 | def test_get_from_cpp_plainc_ptr(ctor, expected): 50 | obj = ctor() 51 | assert m.get_from_cpp_plainc_ptr(obj) == expected 52 | 53 | 54 | @pytest.mark.parametrize( 55 | ("ctor", "expected"), 56 | [ 57 | (m.Base, 5101), 58 | (PyBase, 5323), 59 | (m.CppDerivedPlain, 5202), 60 | (m.CppDerived, 5212), 61 | (PyCppDerived, 5434), 62 | ], 63 | ) 64 | def test_get_from_cpp_unique_ptr(ctor, expected): 65 | obj = ctor() 66 | assert m.get_from_cpp_unique_ptr(obj) == expected 67 | -------------------------------------------------------------------------------- /tests/test_cmake_build/embed.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | namespace py = pybind11; 3 | 4 | PYBIND11_EMBEDDED_MODULE(test_cmake_build, m) { 5 | m.def("add", [](int i, int j) { return i + j; }); 6 | } 7 | 8 | int main(int argc, char *argv[]) { 9 | if (argc != 2) { 10 | throw std::runtime_error("Expected test.py file as the first argument"); 11 | } 12 | auto *test_py_file = argv[1]; 13 | 14 | py::scoped_interpreter guard{}; 15 | 16 | auto m = py::module_::import("test_cmake_build"); 17 | if (m.attr("add")(1, 2).cast() != 3) { 18 | throw std::runtime_error("embed.cpp failed"); 19 | } 20 | 21 | py::module_::import("sys").attr("argv") = py::make_tuple("test.py", "embed.cpp"); 22 | py::eval_file(test_py_file, py::globals()); 23 | } 24 | -------------------------------------------------------------------------------- /tests/test_cmake_build/installed_embed/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15...4.0) 2 | 3 | project(test_installed_embed CXX) 4 | 5 | find_package(pybind11 CONFIG REQUIRED) 6 | message(STATUS "Found pybind11 v${pybind11_VERSION}: ${pybind11_INCLUDE_DIRS}") 7 | 8 | add_executable(test_installed_embed ../embed.cpp) 9 | target_link_libraries(test_installed_embed PRIVATE pybind11::embed) 10 | set_target_properties(test_installed_embed PROPERTIES OUTPUT_NAME test_cmake_build) 11 | 12 | # Do not treat includes from IMPORTED target as SYSTEM (Python headers in pybind11::embed). 13 | # This may be needed to resolve header conflicts, e.g. between Python release and debug headers. 14 | set_target_properties(test_installed_embed PROPERTIES NO_SYSTEM_FROM_IMPORTED ON) 15 | 16 | add_custom_target( 17 | check_installed_embed 18 | $ ${PROJECT_SOURCE_DIR}/../test.py 19 | DEPENDS test_installed_embed) 20 | -------------------------------------------------------------------------------- /tests/test_cmake_build/installed_function/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15...4.0) 2 | 3 | project(test_installed_function CXX) 4 | 5 | find_package(pybind11 CONFIG REQUIRED) 6 | message( 7 | STATUS "Found pybind11 v${pybind11_VERSION} ${pybind11_VERSION_TYPE}: ${pybind11_INCLUDE_DIRS}") 8 | 9 | pybind11_add_module(test_installed_function SHARED NO_EXTRAS ../main.cpp) 10 | set_target_properties(test_installed_function PROPERTIES OUTPUT_NAME test_cmake_build) 11 | 12 | if(DEFINED Python_EXECUTABLE) 13 | set(_Python_EXECUTABLE "${Python_EXECUTABLE}") 14 | elseif(DEFINED PYTHON_EXECUTABLE) 15 | set(_Python_EXECUTABLE "${PYTHON_EXECUTABLE}") 16 | else() 17 | message(FATAL_ERROR "No Python executable defined (should not be possible at this stage)") 18 | endif() 19 | 20 | add_custom_target( 21 | check_installed_function 22 | ${CMAKE_COMMAND} 23 | -E 24 | env 25 | PYTHONPATH=$ 26 | ${_Python_EXECUTABLE} 27 | ${PROJECT_SOURCE_DIR}/../test.py 28 | ${PROJECT_NAME} 29 | DEPENDS test_installed_function) 30 | -------------------------------------------------------------------------------- /tests/test_cmake_build/installed_target/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15...4.0) 2 | 3 | project(test_installed_target CXX) 4 | 5 | find_package(pybind11 CONFIG REQUIRED) 6 | message(STATUS "Found pybind11 v${pybind11_VERSION}: ${pybind11_INCLUDE_DIRS}") 7 | 8 | add_library(test_installed_target MODULE ../main.cpp) 9 | 10 | target_link_libraries(test_installed_target PRIVATE pybind11::module) 11 | set_target_properties(test_installed_target PROPERTIES OUTPUT_NAME test_cmake_build) 12 | 13 | # Make sure result is, for example, test_installed_target.so, not libtest_installed_target.dylib 14 | pybind11_extension(test_installed_target) 15 | 16 | # Do not treat includes from IMPORTED target as SYSTEM (Python headers in pybind11::module). 17 | # This may be needed to resolve header conflicts, e.g. between Python release and debug headers. 18 | set_target_properties(test_installed_target PROPERTIES NO_SYSTEM_FROM_IMPORTED ON) 19 | 20 | if(DEFINED Python_EXECUTABLE) 21 | set(_Python_EXECUTABLE "${Python_EXECUTABLE}") 22 | elseif(DEFINED PYTHON_EXECUTABLE) 23 | set(_Python_EXECUTABLE "${PYTHON_EXECUTABLE}") 24 | else() 25 | message(FATAL_ERROR "No Python executable defined (should not be possible at this stage)") 26 | endif() 27 | 28 | add_custom_target( 29 | check_installed_target 30 | ${CMAKE_COMMAND} 31 | -E 32 | env 33 | PYTHONPATH=$ 34 | ${_Python_EXECUTABLE} 35 | ${PROJECT_SOURCE_DIR}/../test.py 36 | ${PROJECT_NAME} 37 | DEPENDS test_installed_target) 38 | -------------------------------------------------------------------------------- /tests/test_cmake_build/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | namespace py = pybind11; 3 | 4 | PYBIND11_MODULE(test_cmake_build, m, py::mod_gil_not_used()) { 5 | m.def("add", [](int i, int j) { return i + j; }); 6 | } 7 | -------------------------------------------------------------------------------- /tests/test_cmake_build/subdirectory_embed/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15...4.0) 2 | 3 | project(test_subdirectory_embed CXX) 4 | 5 | set(PYBIND11_INSTALL 6 | ON 7 | CACHE BOOL "") 8 | set(PYBIND11_EXPORT_NAME test_export) 9 | 10 | # Allow PYTHON_EXECUTABLE if in FINDPYTHON mode and building pybind11's tests 11 | # (makes transition easier while we support both modes). 12 | if(DEFINED PYTHON_EXECUTABLE AND NOT DEFINED Python_EXECUTABLE) 13 | set(Python_EXECUTABLE "${PYTHON_EXECUTABLE}") 14 | endif() 15 | 16 | add_subdirectory("${pybind11_SOURCE_DIR}" pybind11) 17 | 18 | # Test basic target functionality 19 | add_executable(test_subdirectory_embed ../embed.cpp) 20 | target_link_libraries(test_subdirectory_embed PRIVATE pybind11::embed) 21 | set_target_properties(test_subdirectory_embed PROPERTIES OUTPUT_NAME test_cmake_build) 22 | 23 | add_custom_target( 24 | check_subdirectory_embed 25 | $ "${PROJECT_SOURCE_DIR}/../test.py" 26 | DEPENDS test_subdirectory_embed) 27 | 28 | # Test custom export group -- PYBIND11_EXPORT_NAME 29 | add_library(test_embed_lib ../embed.cpp) 30 | target_link_libraries(test_embed_lib PRIVATE pybind11::embed) 31 | 32 | install( 33 | TARGETS test_embed_lib 34 | EXPORT test_export 35 | ARCHIVE DESTINATION bin 36 | LIBRARY DESTINATION lib 37 | RUNTIME DESTINATION lib) 38 | install(EXPORT test_export DESTINATION lib/cmake/test_export/test_export-Targets.cmake) 39 | -------------------------------------------------------------------------------- /tests/test_cmake_build/subdirectory_function/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15...4.0) 2 | 3 | project(test_subdirectory_function CXX) 4 | 5 | # Allow PYTHON_EXECUTABLE if in FINDPYTHON mode and building pybind11's tests 6 | # (makes transition easier while we support both modes). 7 | if(DEFINED PYTHON_EXECUTABLE AND NOT DEFINED Python_EXECUTABLE) 8 | set(Python_EXECUTABLE "${PYTHON_EXECUTABLE}") 9 | endif() 10 | 11 | add_subdirectory("${pybind11_SOURCE_DIR}" pybind11) 12 | pybind11_add_module(test_subdirectory_function ../main.cpp) 13 | set_target_properties(test_subdirectory_function PROPERTIES OUTPUT_NAME test_cmake_build) 14 | 15 | if(DEFINED Python_EXECUTABLE) 16 | set(_Python_EXECUTABLE "${Python_EXECUTABLE}") 17 | elseif(DEFINED PYTHON_EXECUTABLE) 18 | set(_Python_EXECUTABLE "${PYTHON_EXECUTABLE}") 19 | else() 20 | message(FATAL_ERROR "No Python executable defined (should not be possible at this stage)") 21 | endif() 22 | 23 | add_custom_target( 24 | check_subdirectory_function 25 | ${CMAKE_COMMAND} 26 | -E 27 | env 28 | PYTHONPATH=$ 29 | ${_Python_EXECUTABLE} 30 | ${PROJECT_SOURCE_DIR}/../test.py 31 | ${PROJECT_NAME} 32 | DEPENDS test_subdirectory_function) 33 | -------------------------------------------------------------------------------- /tests/test_cmake_build/subdirectory_target/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15...4.0) 2 | 3 | project(test_subdirectory_target CXX) 4 | 5 | # Allow PYTHON_EXECUTABLE if in FINDPYTHON mode and building pybind11's tests 6 | # (makes transition easier while we support both modes). 7 | if(DEFINED PYTHON_EXECUTABLE AND NOT DEFINED Python_EXECUTABLE) 8 | set(Python_EXECUTABLE "${PYTHON_EXECUTABLE}") 9 | endif() 10 | 11 | add_subdirectory("${pybind11_SOURCE_DIR}" pybind11) 12 | 13 | add_library(test_subdirectory_target MODULE ../main.cpp) 14 | set_target_properties(test_subdirectory_target PROPERTIES OUTPUT_NAME test_cmake_build) 15 | 16 | target_link_libraries(test_subdirectory_target PRIVATE pybind11::module) 17 | 18 | # Make sure result is, for example, test_installed_target.so, not libtest_installed_target.dylib 19 | pybind11_extension(test_subdirectory_target) 20 | 21 | if(DEFINED Python_EXECUTABLE) 22 | set(_Python_EXECUTABLE "${Python_EXECUTABLE}") 23 | elseif(DEFINED PYTHON_EXECUTABLE) 24 | set(_Python_EXECUTABLE "${PYTHON_EXECUTABLE}") 25 | else() 26 | message(FATAL_ERROR "No Python executable defined (should not be possible at this stage)") 27 | endif() 28 | 29 | add_custom_target( 30 | check_subdirectory_target 31 | ${CMAKE_COMMAND} 32 | -E 33 | env 34 | PYTHONPATH=$ 35 | ${_Python_EXECUTABLE} 36 | ${PROJECT_SOURCE_DIR}/../test.py 37 | ${PROJECT_NAME} 38 | DEPENDS test_subdirectory_target) 39 | -------------------------------------------------------------------------------- /tests/test_cmake_build/test.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import sys 4 | 5 | import test_cmake_build 6 | 7 | assert isinstance(__file__, str) # Test this is properly set 8 | 9 | assert test_cmake_build.add(1, 2) == 3 10 | print(f"{sys.argv[1]} imports, runs, and adds: 1 + 2 = 3") 11 | -------------------------------------------------------------------------------- /tests/test_const_name.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pytest 4 | 5 | from pybind11_tests import const_name as m 6 | 7 | 8 | @pytest.mark.parametrize("func", [m.const_name_tests, m.underscore_tests]) 9 | @pytest.mark.parametrize( 10 | ("selector", "expected"), 11 | enumerate( 12 | ( 13 | "", 14 | "A", 15 | "Bd", 16 | "Cef", 17 | "%", 18 | "%", 19 | "T1", 20 | "U2", 21 | "D1", 22 | "E2", 23 | "KeepAtEnd", 24 | ) 25 | ), 26 | ) 27 | def test_const_name(func, selector, expected): 28 | if isinstance(func, str): 29 | pytest.skip(func) 30 | text = func(selector) 31 | assert text == expected 32 | -------------------------------------------------------------------------------- /tests/test_constants_and_functions.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pytest 4 | 5 | m = pytest.importorskip("pybind11_tests.constants_and_functions") 6 | 7 | 8 | def test_constants(): 9 | assert m.some_constant == 14 10 | 11 | 12 | def test_function_overloading(): 13 | assert m.test_function() == "test_function()" 14 | assert m.test_function(7) == "test_function(7)" 15 | assert m.test_function(m.MyEnum.EFirstEntry) == "test_function(enum=1)" 16 | assert m.test_function(m.MyEnum.ESecondEntry) == "test_function(enum=2)" 17 | 18 | assert m.test_function() == "test_function()" 19 | assert m.test_function("abcd") == "test_function(char *)" 20 | assert m.test_function(1, 1.0) == "test_function(int, float)" 21 | assert m.test_function(1, 1.0) == "test_function(int, float)" 22 | assert m.test_function(2.0, 2) == "test_function(float, int)" 23 | 24 | 25 | def test_bytes(): 26 | assert m.print_bytes(m.return_bytes()) == "bytes[1 0 2 0]" 27 | 28 | 29 | def test_exception_specifiers(): 30 | c = m.C() 31 | assert c.m1(2) == 1 32 | assert c.m2(3) == 1 33 | assert c.m3(5) == 2 34 | assert c.m4(7) == 3 35 | assert c.m5(10) == 5 36 | assert c.m6(14) == 8 37 | assert c.m7(20) == 13 38 | assert c.m8(29) == 21 39 | 40 | assert m.f1(33) == 34 41 | assert m.f2(53) == 55 42 | assert m.f3(86) == 89 43 | assert m.f4(140) == 144 44 | 45 | 46 | def test_function_record_leaks(): 47 | class RaisingRepr: 48 | def __repr__(self): 49 | raise RuntimeError("Surprise!") 50 | 51 | with pytest.raises(RuntimeError): 52 | m.register_large_capture_with_invalid_arguments(m) 53 | with pytest.raises(RuntimeError): 54 | m.register_with_raising_repr(m, RaisingRepr()) 55 | 56 | 57 | def test_noexcept_lambda(): 58 | assert m.l1() == 0 59 | -------------------------------------------------------------------------------- /tests/test_cpp_conduit.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 The pybind Community. 2 | 3 | #include "pybind11_tests.h" 4 | #include "test_cpp_conduit_traveler_bindings.h" 5 | 6 | #include 7 | 8 | namespace pybind11_tests { 9 | namespace test_cpp_conduit { 10 | 11 | TEST_SUBMODULE(cpp_conduit, m) { 12 | m.attr("PYBIND11_PLATFORM_ABI_ID") = py::bytes(PYBIND11_PLATFORM_ABI_ID); 13 | m.attr("cpp_type_info_capsule_Traveler") 14 | = py::capsule(&typeid(Traveler), typeid(std::type_info).name()); 15 | m.attr("cpp_type_info_capsule_int") = py::capsule(&typeid(int), typeid(std::type_info).name()); 16 | 17 | wrap_traveler(m); 18 | wrap_lonely_traveler(m); 19 | } 20 | 21 | } // namespace test_cpp_conduit 22 | } // namespace pybind11_tests 23 | -------------------------------------------------------------------------------- /tests/test_cpp_conduit_traveler_bindings.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 The pybind Community. 2 | 3 | #pragma once 4 | 5 | #include 6 | 7 | #include "test_cpp_conduit_traveler_types.h" 8 | 9 | #include 10 | 11 | namespace pybind11_tests { 12 | namespace test_cpp_conduit { 13 | 14 | namespace py = pybind11; 15 | 16 | inline void wrap_traveler(py::module_ m) { 17 | py::class_(m, "Traveler") 18 | .def(py::init()) 19 | .def_readwrite("luggage", &Traveler::luggage) 20 | // See issue #3788: 21 | .def("__getattr__", [](const Traveler &self, const std::string &key) { 22 | return "Traveler GetAttr: " + key + " luggage: " + self.luggage; 23 | }); 24 | 25 | m.def("get_luggage", [](const Traveler &person) { return person.luggage; }); 26 | 27 | py::class_(m, "PremiumTraveler") 28 | .def(py::init()) 29 | .def_readwrite("points", &PremiumTraveler::points) 30 | // See issue #3788: 31 | .def("__getattr__", [](const PremiumTraveler &self, const std::string &key) { 32 | return "PremiumTraveler GetAttr: " + key + " points: " + std::to_string(self.points); 33 | }); 34 | 35 | m.def("get_points", [](const PremiumTraveler &person) { return person.points; }); 36 | } 37 | 38 | inline void wrap_lonely_traveler(py::module_ m) { 39 | py::class_(std::move(m), "LonelyTraveler"); 40 | } 41 | 42 | inline void wrap_very_lonely_traveler(py::module_ m) { 43 | py::class_(std::move(m), "VeryLonelyTraveler"); 44 | } 45 | 46 | } // namespace test_cpp_conduit 47 | } // namespace pybind11_tests 48 | -------------------------------------------------------------------------------- /tests/test_cpp_conduit_traveler_types.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 The pybind Community. 2 | 3 | #pragma once 4 | 5 | #include 6 | 7 | namespace pybind11_tests { 8 | namespace test_cpp_conduit { 9 | 10 | struct Traveler { 11 | explicit Traveler(const std::string &luggage) : luggage(luggage) {} 12 | std::string luggage; 13 | }; 14 | 15 | struct PremiumTraveler : Traveler { 16 | explicit PremiumTraveler(const std::string &luggage, int points) 17 | : Traveler(luggage), points(points) {} 18 | int points; 19 | }; 20 | 21 | struct LonelyTraveler {}; 22 | struct VeryLonelyTraveler : LonelyTraveler {}; 23 | 24 | } // namespace test_cpp_conduit 25 | } // namespace pybind11_tests 26 | -------------------------------------------------------------------------------- /tests/test_custom_type_setup.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | tests/test_custom_type_setup.cpp -- Tests `pybind11::custom_type_setup` 3 | 4 | Copyright (c) Google LLC 5 | 6 | All rights reserved. Use of this source code is governed by a 7 | BSD-style license that can be found in the LICENSE file. 8 | */ 9 | 10 | #include 11 | 12 | #include "pybind11_tests.h" 13 | 14 | namespace py = pybind11; 15 | 16 | namespace { 17 | 18 | struct OwnsPythonObjects { 19 | py::object value = py::none(); 20 | }; 21 | } // namespace 22 | 23 | TEST_SUBMODULE(custom_type_setup, m) { 24 | py::class_ cls( 25 | m, "OwnsPythonObjects", py::custom_type_setup([](PyHeapTypeObject *heap_type) { 26 | auto *type = &heap_type->ht_type; 27 | type->tp_flags |= Py_TPFLAGS_HAVE_GC; 28 | type->tp_traverse = [](PyObject *self_base, visitproc visit, void *arg) { 29 | // https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_traverse 30 | #if PY_VERSION_HEX >= 0x03090000 31 | Py_VISIT(Py_TYPE(self_base)); 32 | #endif 33 | if (py::detail::is_holder_constructed(self_base)) { 34 | auto &self = py::cast(py::handle(self_base)); 35 | Py_VISIT(self.value.ptr()); 36 | } 37 | return 0; 38 | }; 39 | type->tp_clear = [](PyObject *self_base) { 40 | if (py::detail::is_holder_constructed(self_base)) { 41 | auto &self = py::cast(py::handle(self_base)); 42 | self.value = py::none(); 43 | } 44 | return 0; 45 | }; 46 | })); 47 | cls.def(py::init<>()); 48 | cls.def_readwrite("value", &OwnsPythonObjects::value); 49 | } 50 | -------------------------------------------------------------------------------- /tests/test_custom_type_setup.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import gc 4 | import weakref 5 | 6 | import pytest 7 | 8 | import env # noqa: F401 9 | from pybind11_tests import custom_type_setup as m 10 | 11 | 12 | @pytest.fixture 13 | def gc_tester(): 14 | """Tests that an object is garbage collected. 15 | 16 | Assumes that any unreferenced objects are fully collected after calling 17 | `gc.collect()`. That is true on CPython, but does not appear to reliably 18 | hold on PyPy. 19 | """ 20 | 21 | weak_refs = [] 22 | 23 | def add_ref(obj): 24 | # PyPy does not support `gc.is_tracked`. 25 | if hasattr(gc, "is_tracked"): 26 | assert gc.is_tracked(obj) 27 | weak_refs.append(weakref.ref(obj)) 28 | 29 | yield add_ref 30 | 31 | gc.collect() 32 | for ref in weak_refs: 33 | assert ref() is None 34 | 35 | 36 | # PyPy does not seem to reliably garbage collect. 37 | @pytest.mark.skipif("env.PYPY or env.GRAALPY") 38 | def test_self_cycle(gc_tester): 39 | obj = m.OwnsPythonObjects() 40 | obj.value = obj 41 | gc_tester(obj) 42 | 43 | 44 | # PyPy does not seem to reliably garbage collect. 45 | @pytest.mark.skipif("env.PYPY or env.GRAALPY") 46 | def test_indirect_cycle(gc_tester): 47 | obj = m.OwnsPythonObjects() 48 | obj_list = [obj] 49 | obj.value = obj_list 50 | gc_tester(obj) 51 | -------------------------------------------------------------------------------- /tests/test_docs_advanced_cast_custom.cpp: -------------------------------------------------------------------------------- 1 | // ######################################################################### 2 | // PLEASE UPDATE docs/advanced/cast/custom.rst IF ANY CHANGES ARE MADE HERE. 3 | // ######################################################################### 4 | 5 | #include "pybind11_tests.h" 6 | 7 | namespace user_space { 8 | 9 | struct Point2D { 10 | double x; 11 | double y; 12 | }; 13 | 14 | Point2D negate(const Point2D &point) { return Point2D{-point.x, -point.y}; } 15 | 16 | } // namespace user_space 17 | 18 | namespace pybind11 { 19 | namespace detail { 20 | 21 | template <> 22 | struct type_caster { 23 | // This macro inserts a lot of boilerplate code and sets the type hint. 24 | // `io_name` is used to specify different type hints for arguments and return values. 25 | // The signature of our negate function would then look like: 26 | // `negate(collections.abc.Sequence[float]) -> tuple[float, float]` 27 | PYBIND11_TYPE_CASTER(user_space::Point2D, 28 | io_name("collections.abc.Sequence[float]", "tuple[float, float]")); 29 | 30 | // C++ -> Python: convert `Point2D` to `tuple[float, float]`. The second and third arguments 31 | // are used to indicate the return value policy and parent object (for 32 | // return_value_policy::reference_internal) and are often ignored by custom casters. 33 | // The return value should reflect the type hint specified by the second argument of `io_name`. 34 | static handle 35 | cast(const user_space::Point2D &number, return_value_policy /*policy*/, handle /*parent*/) { 36 | return py::make_tuple(number.x, number.y).release(); 37 | } 38 | 39 | // Python -> C++: convert a `PyObject` into a `Point2D` and return false upon failure. The 40 | // second argument indicates whether implicit conversions should be allowed. 41 | // The accepted types should reflect the type hint specified by the first argument of 42 | // `io_name`. 43 | bool load(handle src, bool /*convert*/) { 44 | // Check if handle is a Sequence 45 | if (!py::isinstance(src)) { 46 | return false; 47 | } 48 | auto seq = py::reinterpret_borrow(src); 49 | // Check if exactly two values are in the Sequence 50 | if (seq.size() != 2) { 51 | return false; 52 | } 53 | // Check if each element is either a float or an int 54 | for (auto item : seq) { 55 | if (!py::isinstance(item) && !py::isinstance(item)) { 56 | return false; 57 | } 58 | } 59 | value.x = seq[0].cast(); 60 | value.y = seq[1].cast(); 61 | return true; 62 | } 63 | }; 64 | 65 | } // namespace detail 66 | } // namespace pybind11 67 | 68 | // Bind the negate function 69 | TEST_SUBMODULE(docs_advanced_cast_custom, m) { m.def("negate", user_space::negate); } 70 | -------------------------------------------------------------------------------- /tests/test_docs_advanced_cast_custom.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import TYPE_CHECKING, Sequence 4 | 5 | if TYPE_CHECKING: 6 | from conftest import SanitizedString 7 | 8 | from pybind11_tests import docs_advanced_cast_custom as m 9 | 10 | 11 | def assert_negate_function( 12 | input_sequence: Sequence[float], 13 | target: tuple[float, float], 14 | ) -> None: 15 | output = m.negate(input_sequence) 16 | assert isinstance(output, tuple) 17 | assert len(output) == 2 18 | assert isinstance(output[0], float) 19 | assert isinstance(output[1], float) 20 | assert output == target 21 | 22 | 23 | def test_negate(doc: SanitizedString) -> None: 24 | assert ( 25 | doc(m.negate) 26 | == "negate(arg0: collections.abc.Sequence[float]) -> tuple[float, float]" 27 | ) 28 | assert_negate_function([1.0, -1.0], (-1.0, 1.0)) 29 | assert_negate_function((1.0, -1.0), (-1.0, 1.0)) 30 | assert_negate_function([1, -1], (-1.0, 1.0)) 31 | assert_negate_function((1, -1), (-1.0, 1.0)) 32 | 33 | 34 | def test_docs() -> None: 35 | ########################################################################### 36 | # PLEASE UPDATE docs/advanced/cast/custom.rst IF ANY CHANGES ARE MADE HERE. 37 | ########################################################################### 38 | point1 = [1.0, -1.0] 39 | point2 = m.negate(point1) 40 | assert point2 == (-1.0, 1.0) 41 | -------------------------------------------------------------------------------- /tests/test_docstring_options.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from pybind11_tests import docstring_options as m 4 | 5 | 6 | def test_docstring_options(): 7 | # options.disable_function_signatures() 8 | assert not m.test_function1.__doc__ 9 | 10 | assert m.test_function2.__doc__ == "A custom docstring" 11 | 12 | # docstring specified on just the first overload definition: 13 | assert m.test_overloaded1.__doc__ == "Overload docstring" 14 | 15 | # docstring on both overloads: 16 | assert m.test_overloaded2.__doc__ == "overload docstring 1\noverload docstring 2" 17 | 18 | # docstring on only second overload: 19 | assert m.test_overloaded3.__doc__ == "Overload docstr" 20 | 21 | # options.enable_function_signatures() 22 | assert m.test_function3.__doc__.startswith( 23 | "test_function3(a: typing.SupportsInt, b: typing.SupportsInt) -> None" 24 | ) 25 | 26 | assert m.test_function4.__doc__.startswith( 27 | "test_function4(a: typing.SupportsInt, b: typing.SupportsInt) -> None" 28 | ) 29 | assert m.test_function4.__doc__.endswith("A custom docstring\n") 30 | 31 | # options.disable_function_signatures() 32 | # options.disable_user_defined_docstrings() 33 | assert not m.test_function5.__doc__ 34 | 35 | # nested options.enable_user_defined_docstrings() 36 | assert m.test_function6.__doc__ == "A custom docstring" 37 | 38 | # RAII destructor 39 | assert m.test_function7.__doc__.startswith( 40 | "test_function7(a: typing.SupportsInt, b: typing.SupportsInt) -> None" 41 | ) 42 | assert m.test_function7.__doc__.endswith("A custom docstring\n") 43 | 44 | # when all options are disabled, no docstring (instead of an empty one) should be generated 45 | assert m.test_function8.__doc__ is None 46 | 47 | # Suppression of user-defined docstrings for non-function objects 48 | assert not m.DocstringTestFoo.__doc__ 49 | assert not m.DocstringTestFoo.value_prop.__doc__ 50 | 51 | # Check existig behaviour of enum docstings 52 | assert ( 53 | m.DocstringTestEnum1.__doc__ 54 | == "Enum docstring\n\nMembers:\n\n Member1\n\n Member2" 55 | ) 56 | 57 | # options.enable_enum_members_docstring() 58 | assert ( 59 | m.DocstringTestEnum2.__doc__ 60 | == "Enum docstring\n\nMembers:\n\n Member1\n\n Member2" 61 | ) 62 | 63 | # options.disable_enum_members_docstring() 64 | assert m.DocstringTestEnum3.__doc__ == "Enum docstring" 65 | 66 | # options.disable_user_defined_docstrings() 67 | assert m.DocstringTestEnum4.__doc__ == "Members:\n\n Member1\n\n Member2" 68 | 69 | # options.disable_user_defined_docstrings() 70 | # options.disable_enum_members_docstring() 71 | # When all options are disabled, no docstring (instead of an empty one) should be generated 72 | assert m.DocstringTestEnum5.__doc__ is None 73 | -------------------------------------------------------------------------------- /tests/test_eigen_tensor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | tests/eigen_tensor.cpp -- automatic conversion of Eigen Tensor 3 | 4 | All rights reserved. Use of this source code is governed by a 5 | BSD-style license that can be found in the LICENSE file. 6 | */ 7 | 8 | #define PYBIND11_TEST_EIGEN_TENSOR_NAMESPACE eigen_tensor 9 | 10 | #ifdef EIGEN_AVOID_STL_ARRAY 11 | # undef EIGEN_AVOID_STL_ARRAY 12 | #endif 13 | 14 | #include "test_eigen_tensor.inl" 15 | 16 | #include "pybind11_tests.h" 17 | 18 | test_initializer egien_tensor("eigen_tensor", eigen_tensor_test::test_module); 19 | -------------------------------------------------------------------------------- /tests/test_embed/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | possibly_uninitialized(PYTHON_MODULE_EXTENSION Python_INTERPRETER_ID) 2 | 3 | if("${PYTHON_MODULE_EXTENSION}" MATCHES "pypy" 4 | OR "${Python_INTERPRETER_ID}" STREQUAL "PyPy" 5 | OR "${PYTHON_MODULE_EXTENSION}" MATCHES "graalpy") 6 | message(STATUS "Skipping embed test on PyPy or GraalPy") 7 | add_custom_target(cpptest) # Dummy target on PyPy or GraalPy. Embedding is not supported. 8 | set(_suppress_unused_variable_warning "${DOWNLOAD_CATCH}") 9 | return() 10 | endif() 11 | 12 | if(TARGET Python::Module AND NOT TARGET Python::Python) 13 | message(STATUS "Skipping embed test since no embed libs found") 14 | add_custom_target(cpptest) # Dummy target since embedding is not supported. 15 | set(_suppress_unused_variable_warning "${DOWNLOAD_CATCH}") 16 | return() 17 | endif() 18 | 19 | find_package(Catch 2.13.10) 20 | 21 | if(CATCH_FOUND) 22 | message(STATUS "Building interpreter tests using Catch v${CATCH_VERSION}") 23 | else() 24 | message(STATUS "Catch not detected. Interpreter tests will be skipped. Install Catch headers" 25 | " manually or use `cmake -DDOWNLOAD_CATCH=ON` to fetch them automatically.") 26 | return() 27 | endif() 28 | 29 | find_package(Threads REQUIRED) 30 | 31 | if(PYBIND11_TEST_SMART_HOLDER) 32 | add_compile_definitions( 33 | -DPYBIND11_RUN_TESTING_WITH_SMART_HOLDER_AS_DEFAULT_BUT_NEVER_USE_IN_PRODUCTION_PLEASE) 34 | endif() 35 | 36 | add_executable(test_embed catch.cpp test_interpreter.cpp test_subinterpreter.cpp) 37 | pybind11_enable_warnings(test_embed) 38 | 39 | target_link_libraries(test_embed PRIVATE pybind11::embed Catch2::Catch2 Threads::Threads) 40 | 41 | if(NOT CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR) 42 | file(COPY test_interpreter.py test_trampoline.py DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") 43 | endif() 44 | 45 | add_custom_target( 46 | cpptest 47 | COMMAND "$" 48 | DEPENDS test_embed 49 | WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") 50 | 51 | pybind11_add_module(external_module THIN_LTO external_module.cpp) 52 | set_target_properties(external_module PROPERTIES LIBRARY_OUTPUT_DIRECTORY 53 | "${CMAKE_CURRENT_BINARY_DIR}") 54 | foreach(config ${CMAKE_CONFIGURATION_TYPES}) 55 | string(TOUPPER ${config} config) 56 | set_target_properties(external_module PROPERTIES LIBRARY_OUTPUT_DIRECTORY_${config} 57 | "${CMAKE_CURRENT_BINARY_DIR}") 58 | endforeach() 59 | add_dependencies(cpptest external_module) 60 | 61 | add_dependencies(check cpptest) 62 | -------------------------------------------------------------------------------- /tests/test_embed/catch.cpp: -------------------------------------------------------------------------------- 1 | // The Catch implementation is compiled here. This is a standalone 2 | // translation unit to avoid recompiling it for every test change. 3 | 4 | #include 5 | 6 | // Silence MSVC C++17 deprecation warning from Catch regarding std::uncaught_exceptions (up to 7 | // catch 2.0.1; this should be fixed in the next catch release after 2.0.1). 8 | PYBIND11_WARNING_DISABLE_MSVC(4996) 9 | 10 | // Catch uses _ internally, which breaks gettext style defines 11 | #ifdef _ 12 | # undef _ 13 | #endif 14 | 15 | #define CATCH_CONFIG_RUNNER 16 | #include 17 | 18 | namespace py = pybind11; 19 | 20 | int main(int argc, char *argv[]) { 21 | // Setup for TEST_CASE in test_interpreter.cpp, tagging on a large random number: 22 | std::string updated_pythonpath("pybind11_test_embed_PYTHONPATH_2099743835476552"); 23 | const char *preexisting_pythonpath = getenv("PYTHONPATH"); 24 | if (preexisting_pythonpath != nullptr) { 25 | #if defined(_WIN32) 26 | updated_pythonpath += ';'; 27 | #else 28 | updated_pythonpath += ':'; 29 | #endif 30 | updated_pythonpath += preexisting_pythonpath; 31 | } 32 | #if defined(_WIN32) 33 | _putenv_s("PYTHONPATH", updated_pythonpath.c_str()); 34 | #else 35 | setenv("PYTHONPATH", updated_pythonpath.c_str(), /*replace=*/1); 36 | #endif 37 | 38 | py::scoped_interpreter guard{}; 39 | 40 | auto result = Catch::Session().run(argc, argv); 41 | 42 | return result < 0xff ? result : 0xff; 43 | } 44 | -------------------------------------------------------------------------------- /tests/test_embed/external_module.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace py = pybind11; 4 | 5 | /* Simple test module/test class to check that the referenced internals data of external pybind11 6 | * modules aren't preserved over a finalize/initialize. 7 | */ 8 | 9 | PYBIND11_MODULE(external_module, 10 | m, 11 | py::mod_gil_not_used(), 12 | py::multiple_interpreters::per_interpreter_gil()) { 13 | 14 | class A { 15 | public: 16 | explicit A(int value) : v{value} {}; 17 | int v; 18 | }; 19 | 20 | py::class_(m, "A").def(py::init()).def_readwrite("value", &A::v); 21 | 22 | m.def("internals_at", 23 | []() { return reinterpret_cast(&py::detail::get_internals()); }); 24 | } 25 | -------------------------------------------------------------------------------- /tests/test_embed/test_interpreter.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import sys 4 | 5 | from widget_module import Widget 6 | 7 | 8 | class DerivedWidget(Widget): 9 | def __init__(self, message): 10 | super().__init__(message) 11 | 12 | def the_answer(self): 13 | return 42 14 | 15 | def argv0(self): 16 | return sys.argv[0] 17 | -------------------------------------------------------------------------------- /tests/test_embed/test_trampoline.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import trampoline_module 4 | 5 | 6 | def func(): 7 | class Test(trampoline_module.test_override_cache_helper): 8 | def func(self): 9 | return 42 10 | 11 | return Test() 12 | 13 | 14 | def func2(): 15 | class Test(trampoline_module.test_override_cache_helper): 16 | pass 17 | 18 | return Test() 19 | -------------------------------------------------------------------------------- /tests/test_eval.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import os 4 | 5 | import pytest 6 | 7 | import env # noqa: F401 8 | from pybind11_tests import eval_ as m 9 | 10 | 11 | def test_evals(capture): 12 | with capture: 13 | assert m.test_eval_statements() 14 | assert capture == "Hello World!" 15 | 16 | assert m.test_eval() 17 | assert m.test_eval_single_statement() 18 | 19 | assert m.test_eval_failure() 20 | 21 | 22 | @pytest.mark.xfail("env.PYPY or env.GRAALPY", raises=RuntimeError) 23 | def test_eval_file(): 24 | filename = os.path.join(os.path.dirname(__file__), "test_eval_call.py") 25 | assert m.test_eval_file(filename) 26 | 27 | assert m.test_eval_file_failure() 28 | 29 | 30 | def test_eval_empty_globals(): 31 | assert "__builtins__" in m.eval_empty_globals(None) 32 | 33 | g = {} 34 | assert "__builtins__" in m.eval_empty_globals(g) 35 | assert "__builtins__" in g 36 | 37 | 38 | def test_eval_closure(): 39 | global_, local = m.test_eval_closure() 40 | 41 | assert global_["closure_value"] == 42 42 | assert local["closure_value"] == 0 43 | 44 | assert "local_value" not in global_ 45 | assert local["local_value"] == 0 46 | 47 | assert "func_global" not in global_ 48 | assert local["func_global"]() == 42 49 | 50 | assert "func_local" not in global_ 51 | with pytest.raises(NameError): 52 | local["func_local"]() 53 | -------------------------------------------------------------------------------- /tests/test_eval_call.py: -------------------------------------------------------------------------------- 1 | # This file is called from 'test_eval.py' 2 | from __future__ import annotations 3 | 4 | if "call_test2" in locals(): 5 | call_test2(y) # noqa: F821 undefined name 6 | -------------------------------------------------------------------------------- /tests/test_exceptions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "pybind11_tests.h" 3 | 4 | #include 5 | 6 | // shared exceptions for cross_module_tests 7 | 8 | class PYBIND11_EXPORT_EXCEPTION shared_exception : public pybind11::builtin_exception { 9 | public: 10 | using builtin_exception::builtin_exception; 11 | explicit shared_exception() : shared_exception("") {} 12 | void set_error() const override { py::set_error(PyExc_RuntimeError, what()); } 13 | }; 14 | -------------------------------------------------------------------------------- /tests/test_opaque_types.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | tests/test_opaque_types.cpp -- opaque types, passing void pointers 3 | 4 | Copyright (c) 2016 Wenzel Jakob 5 | 6 | All rights reserved. Use of this source code is governed by a 7 | BSD-style license that can be found in the LICENSE file. 8 | */ 9 | 10 | #include 11 | 12 | #include "pybind11_tests.h" 13 | 14 | #include 15 | 16 | // IMPORTANT: Disable internal pybind11 translation mechanisms for STL data structures 17 | // 18 | // This also deliberately doesn't use the below StringList type alias to test 19 | // that MAKE_OPAQUE can handle a type containing a `,`. (The `std::allocator` 20 | // bit is just the default `std::vector` allocator). 21 | PYBIND11_MAKE_OPAQUE(std::vector>) 22 | 23 | using StringList = std::vector>; 24 | 25 | TEST_SUBMODULE(opaque_types, m) { 26 | // test_string_list 27 | py::class_(m, "StringList") 28 | .def(py::init<>()) 29 | .def("pop_back", &StringList::pop_back) 30 | /* There are multiple versions of push_back(), etc. Select the right ones. */ 31 | .def("push_back", (void (StringList::*)(const std::string &)) &StringList::push_back) 32 | .def("back", (std::string & (StringList::*) ()) & StringList::back) 33 | .def("__len__", [](const StringList &v) { return v.size(); }) 34 | .def( 35 | "__iter__", 36 | [](StringList &v) { return py::make_iterator(v.begin(), v.end()); }, 37 | py::keep_alive<0, 1>()); 38 | 39 | class ClassWithSTLVecProperty { 40 | public: 41 | StringList stringList; 42 | }; 43 | py::class_(m, "ClassWithSTLVecProperty") 44 | .def(py::init<>()) 45 | .def_readwrite("stringList", &ClassWithSTLVecProperty::stringList); 46 | 47 | m.def("print_opaque_list", [](const StringList &l) { 48 | std::string ret = "Opaque list: ["; 49 | bool first = true; 50 | for (const auto &entry : l) { 51 | if (!first) { 52 | ret += ", "; 53 | } 54 | ret += entry; 55 | first = false; 56 | } 57 | return ret + "]"; 58 | }); 59 | 60 | // test_pointers 61 | m.def("return_void_ptr", []() { return (void *) 0x1234; }); 62 | m.def("get_void_ptr_value", [](void *ptr) { return reinterpret_cast(ptr); }); 63 | m.def("return_null_str", []() { return (char *) nullptr; }); 64 | m.def("get_null_str_value", [](char *ptr) { return reinterpret_cast(ptr); }); 65 | 66 | m.def("return_unique_ptr", []() -> std::unique_ptr { 67 | auto *result = new StringList(); 68 | result->emplace_back("some value"); 69 | return std::unique_ptr(result); 70 | }); 71 | 72 | // test unions 73 | py::class_(m, "IntFloat") 74 | .def(py::init<>()) 75 | .def_readwrite("i", &IntFloat::i) 76 | .def_readwrite("f", &IntFloat::f); 77 | } 78 | -------------------------------------------------------------------------------- /tests/test_opaque_types.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pytest 4 | 5 | import env 6 | from pybind11_tests import ConstructorStats, UserType 7 | from pybind11_tests import opaque_types as m 8 | 9 | 10 | def test_string_list(): 11 | lst = m.StringList() 12 | lst.push_back("Element 1") 13 | lst.push_back("Element 2") 14 | assert m.print_opaque_list(lst) == "Opaque list: [Element 1, Element 2]" 15 | assert lst.back() == "Element 2" 16 | 17 | for i, k in enumerate(lst, start=1): 18 | assert k == f"Element {i}" 19 | lst.pop_back() 20 | assert m.print_opaque_list(lst) == "Opaque list: [Element 1]" 21 | 22 | cvp = m.ClassWithSTLVecProperty() 23 | assert m.print_opaque_list(cvp.stringList) == "Opaque list: []" 24 | 25 | cvp.stringList = lst 26 | cvp.stringList.push_back("Element 3") 27 | assert m.print_opaque_list(cvp.stringList) == "Opaque list: [Element 1, Element 3]" 28 | 29 | 30 | def test_pointers(msg, backport_typehints): 31 | living_before = ConstructorStats.get(UserType).alive() 32 | assert m.get_void_ptr_value(m.return_void_ptr()) == 0x1234 33 | assert m.get_void_ptr_value(UserType()) # Should also work for other C++ types 34 | 35 | if not env.GRAALPY: 36 | assert ConstructorStats.get(UserType).alive() == living_before 37 | 38 | with pytest.raises(TypeError) as excinfo: 39 | m.get_void_ptr_value([1, 2, 3]) # This should not work 40 | 41 | assert ( 42 | backport_typehints(msg(excinfo.value)) 43 | == """ 44 | get_void_ptr_value(): incompatible function arguments. The following argument types are supported: 45 | 1. (arg0: types.CapsuleType) -> int 46 | 47 | Invoked with: [1, 2, 3] 48 | """ 49 | ) 50 | 51 | assert m.return_null_str() is None 52 | assert m.get_null_str_value(m.return_null_str()) is not None 53 | 54 | ptr = m.return_unique_ptr() 55 | assert "StringList" in repr(ptr) 56 | assert m.print_opaque_list(ptr) == "Opaque list: [some value]" 57 | 58 | 59 | def test_unions(): 60 | int_float_union = m.IntFloat() 61 | int_float_union.i = 42 62 | assert int_float_union.i == 42 63 | int_float_union.f = 3.0 64 | assert int_float_union.f == 3.0 65 | -------------------------------------------------------------------------------- /tests/test_python_multiple_inheritance.cpp: -------------------------------------------------------------------------------- 1 | #include "pybind11_tests.h" 2 | 3 | namespace test_python_multiple_inheritance { 4 | 5 | // Copied from: 6 | // https://github.com/google/clif/blob/5718e4d0807fd3b6a8187dde140069120b81ecef/clif/testing/python_multiple_inheritance.h 7 | 8 | struct CppBase { 9 | explicit CppBase(int value) : base_value(value) {} 10 | int get_base_value() const { return base_value; } 11 | void reset_base_value(int new_value) { base_value = new_value; } 12 | 13 | private: 14 | int base_value; 15 | }; 16 | 17 | struct CppDrvd : CppBase { 18 | explicit CppDrvd(int value) : CppBase(value), drvd_value(value * 3) {} 19 | int get_drvd_value() const { return drvd_value; } 20 | void reset_drvd_value(int new_value) { drvd_value = new_value; } 21 | 22 | int get_base_value_from_drvd() const { return get_base_value(); } 23 | void reset_base_value_from_drvd(int new_value) { reset_base_value(new_value); } 24 | 25 | private: 26 | int drvd_value; 27 | }; 28 | 29 | } // namespace test_python_multiple_inheritance 30 | 31 | TEST_SUBMODULE(python_multiple_inheritance, m) { 32 | using namespace test_python_multiple_inheritance; 33 | 34 | py::class_(m, "CppBase") 35 | .def(py::init()) 36 | .def("get_base_value", &CppBase::get_base_value) 37 | .def("reset_base_value", &CppBase::reset_base_value); 38 | 39 | py::class_(m, "CppDrvd") 40 | .def(py::init()) 41 | .def("get_drvd_value", &CppDrvd::get_drvd_value) 42 | .def("reset_drvd_value", &CppDrvd::reset_drvd_value) 43 | .def("get_base_value_from_drvd", &CppDrvd::get_base_value_from_drvd) 44 | .def("reset_base_value_from_drvd", &CppDrvd::reset_base_value_from_drvd); 45 | } 46 | -------------------------------------------------------------------------------- /tests/test_python_multiple_inheritance.py: -------------------------------------------------------------------------------- 1 | # Adapted from: 2 | # https://github.com/google/clif/blob/5718e4d0807fd3b6a8187dde140069120b81ecef/clif/testing/python/python_multiple_inheritance_test.py 3 | from __future__ import annotations 4 | 5 | from pybind11_tests import python_multiple_inheritance as m 6 | 7 | 8 | class PC(m.CppBase): 9 | pass 10 | 11 | 12 | class PPCC(PC, m.CppDrvd): 13 | pass 14 | 15 | 16 | def test_PC(): 17 | d = PC(11) 18 | assert d.get_base_value() == 11 19 | d.reset_base_value(13) 20 | assert d.get_base_value() == 13 21 | 22 | 23 | def test_PPCC(): 24 | d = PPCC(11) 25 | assert d.get_drvd_value() == 33 26 | d.reset_drvd_value(55) 27 | assert d.get_drvd_value() == 55 28 | 29 | assert d.get_base_value() == 11 30 | assert d.get_base_value_from_drvd() == 11 31 | d.reset_base_value(20) 32 | assert d.get_base_value() == 20 33 | assert d.get_base_value_from_drvd() == 20 34 | d.reset_base_value_from_drvd(30) 35 | assert d.get_base_value() == 30 36 | assert d.get_base_value_from_drvd() == 30 37 | -------------------------------------------------------------------------------- /tests/test_tagbased_polymorphic.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from pybind11_tests import tagbased_polymorphic as m 4 | 5 | 6 | def test_downcast(): 7 | zoo = m.create_zoo() 8 | assert [type(animal) for animal in zoo] == [ 9 | m.Labrador, 10 | m.Dog, 11 | m.Chihuahua, 12 | m.Cat, 13 | m.Panther, 14 | ] 15 | assert [animal.name for animal in zoo] == [ 16 | "Fido", 17 | "Ginger", 18 | "Hertzl", 19 | "Tiger", 20 | "Leo", 21 | ] 22 | zoo[1].sound = "woooooo" 23 | assert [dog.bark() for dog in zoo[:3]] == [ 24 | "Labrador Fido goes WOOF!", 25 | "Dog Ginger goes woooooo", 26 | "Chihuahua Hertzl goes iyiyiyiyiyi and runs in circles", 27 | ] 28 | assert [cat.purr() for cat in zoo[3:]] == ["mrowr", "mrrrRRRRRR"] 29 | zoo[0].excitement -= 1000 30 | assert zoo[0].excitement == 14000 31 | -------------------------------------------------------------------------------- /tests/test_thread.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | tests/test_thread.cpp -- call pybind11 bound methods in threads 3 | 4 | Copyright (c) 2021 Laramie Leavitt (Google LLC) 5 | 6 | All rights reserved. Use of this source code is governed by a 7 | BSD-style license that can be found in the LICENSE file. 8 | */ 9 | 10 | #include 11 | #include 12 | 13 | #include "pybind11_tests.h" 14 | 15 | #include 16 | #include 17 | 18 | namespace py = pybind11; 19 | 20 | namespace { 21 | 22 | struct IntStruct { 23 | explicit IntStruct(int v) : value(v) {}; 24 | ~IntStruct() { value = -value; } 25 | IntStruct(const IntStruct &) = default; 26 | IntStruct &operator=(const IntStruct &) = default; 27 | 28 | int value; 29 | }; 30 | 31 | struct EmptyStruct {}; 32 | EmptyStruct SharedInstance; 33 | 34 | } // namespace 35 | 36 | TEST_SUBMODULE(thread, m) { 37 | 38 | py::class_(m, "IntStruct").def(py::init([](const int i) { return IntStruct(i); })); 39 | 40 | // implicitly_convertible uses loader_life_support when an implicit 41 | // conversion is required in order to lifetime extend the reference. 42 | // 43 | // This test should be run with ASAN for better effectiveness. 44 | py::implicitly_convertible(); 45 | 46 | m.def("test", [](int expected, const IntStruct &in) { 47 | { 48 | py::gil_scoped_release release; 49 | std::this_thread::sleep_for(std::chrono::milliseconds(5)); 50 | } 51 | 52 | if (in.value != expected) { 53 | throw std::runtime_error("Value changed!!"); 54 | } 55 | }); 56 | 57 | m.def( 58 | "test_no_gil", 59 | [](int expected, const IntStruct &in) { 60 | std::this_thread::sleep_for(std::chrono::milliseconds(5)); 61 | if (in.value != expected) { 62 | throw std::runtime_error("Value changed!!"); 63 | } 64 | }, 65 | py::call_guard()); 66 | 67 | py::class_(m, "EmptyStruct") 68 | .def_readonly_static("SharedInstance", &SharedInstance); 69 | 70 | // NOTE: std::string_view also uses loader_life_support to ensure that 71 | // the string contents remain alive, but that's a C++ 17 feature. 72 | } 73 | -------------------------------------------------------------------------------- /tests/test_thread.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import sys 4 | import threading 5 | 6 | import pytest 7 | 8 | from pybind11_tests import thread as m 9 | 10 | 11 | class Thread(threading.Thread): 12 | def __init__(self, fn): 13 | super().__init__() 14 | self.fn = fn 15 | self.e = None 16 | 17 | def run(self): 18 | try: 19 | for i in range(10): 20 | self.fn(i, i) 21 | except Exception as e: 22 | self.e = e 23 | 24 | def join(self): 25 | super().join() 26 | if self.e: 27 | raise self.e 28 | 29 | 30 | @pytest.mark.skipif(sys.platform.startswith("emscripten"), reason="Requires threads") 31 | def test_implicit_conversion(): 32 | a = Thread(m.test) 33 | b = Thread(m.test) 34 | c = Thread(m.test) 35 | for x in [a, b, c]: 36 | x.start() 37 | for x in [c, b, a]: 38 | x.join() 39 | 40 | 41 | @pytest.mark.skipif(sys.platform.startswith("emscripten"), reason="Requires threads") 42 | def test_implicit_conversion_no_gil(): 43 | a = Thread(m.test_no_gil) 44 | b = Thread(m.test_no_gil) 45 | c = Thread(m.test_no_gil) 46 | for x in [a, b, c]: 47 | x.start() 48 | for x in [c, b, a]: 49 | x.join() 50 | 51 | 52 | @pytest.mark.skipif(sys.platform.startswith("emscripten"), reason="Requires threads") 53 | def test_bind_shared_instance(): 54 | nb_threads = 4 55 | b = threading.Barrier(nb_threads) 56 | 57 | def access_shared_instance(): 58 | b.wait() 59 | for _ in range(1000): 60 | m.EmptyStruct.SharedInstance # noqa: B018 61 | 62 | threads = [ 63 | threading.Thread(target=access_shared_instance) for _ in range(nb_threads) 64 | ] 65 | for thread in threads: 66 | thread.start() 67 | for thread in threads: 68 | thread.join() 69 | -------------------------------------------------------------------------------- /tests/test_type_caster_std_function_specializations.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "pybind11_tests.h" 5 | 6 | namespace py = pybind11; 7 | 8 | namespace { 9 | 10 | struct SpecialReturn { 11 | int value = 99; 12 | }; 13 | 14 | } // namespace 15 | 16 | namespace pybind11 { 17 | namespace detail { 18 | namespace type_caster_std_function_specializations { 19 | 20 | template 21 | struct func_wrapper : func_wrapper_base { 22 | using func_wrapper_base::func_wrapper_base; 23 | SpecialReturn operator()(Args... args) const { 24 | gil_scoped_acquire acq; 25 | SpecialReturn result; 26 | try { 27 | result = hfunc.f(std::forward(args)...).template cast(); 28 | } catch (error_already_set &) { 29 | result.value += 1; 30 | } 31 | result.value += 100; 32 | return result; 33 | } 34 | }; 35 | 36 | } // namespace type_caster_std_function_specializations 37 | } // namespace detail 38 | } // namespace pybind11 39 | 40 | TEST_SUBMODULE(type_caster_std_function_specializations, m) { 41 | py::class_(m, "SpecialReturn") 42 | .def(py::init<>()) 43 | .def_readwrite("value", &SpecialReturn::value); 44 | m.def("call_callback_with_special_return", 45 | [](const std::function &func) { return func(); }); 46 | } 47 | -------------------------------------------------------------------------------- /tests/test_type_caster_std_function_specializations.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from pybind11_tests import type_caster_std_function_specializations as m 4 | 5 | 6 | def test_callback_with_special_return(): 7 | def return_special(): 8 | return m.SpecialReturn() 9 | 10 | def raise_exception(): 11 | raise ValueError("called raise_exception.") 12 | 13 | assert return_special().value == 99 14 | assert m.call_callback_with_special_return(return_special).value == 199 15 | assert m.call_callback_with_special_return(raise_exception).value == 200 16 | -------------------------------------------------------------------------------- /tests/test_union.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | tests/test_class.cpp -- test py::class_ definitions and basic functionality 3 | 4 | Copyright (c) 2019 Roland Dreier 5 | 6 | All rights reserved. Use of this source code is governed by a 7 | BSD-style license that can be found in the LICENSE file. 8 | */ 9 | 10 | #include "pybind11_tests.h" 11 | 12 | TEST_SUBMODULE(union_, m) { 13 | union TestUnion { 14 | int value_int; 15 | unsigned value_uint; 16 | }; 17 | 18 | py::class_(m, "TestUnion") 19 | .def(py::init<>()) 20 | .def_readonly("as_int", &TestUnion::value_int) 21 | .def_readwrite("as_uint", &TestUnion::value_uint); 22 | } 23 | -------------------------------------------------------------------------------- /tests/test_union.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from pybind11_tests import union_ as m 4 | 5 | 6 | def test_union(): 7 | instance = m.TestUnion() 8 | 9 | instance.as_uint = 10 10 | assert instance.as_int == 10 11 | -------------------------------------------------------------------------------- /tests/test_unnamed_namespace_a.cpp: -------------------------------------------------------------------------------- 1 | #include "pybind11_tests.h" 2 | 3 | namespace { 4 | struct any_struct {}; 5 | } // namespace 6 | 7 | TEST_SUBMODULE(unnamed_namespace_a, m) { 8 | if (py::detail::get_type_info(typeid(any_struct)) == nullptr) { 9 | py::class_(m, "unnamed_namespace_a_any_struct"); 10 | } else { 11 | m.attr("unnamed_namespace_a_any_struct") = py::none(); 12 | } 13 | m.attr("defined_WIN32_or__WIN32") = 14 | #if defined(WIN32) || defined(_WIN32) 15 | true; 16 | #else 17 | false; 18 | #endif 19 | m.attr("defined___clang__") = 20 | #if defined(__clang__) 21 | true; 22 | #else 23 | false; 24 | #endif 25 | m.attr("defined__LIBCPP_VERSION") = 26 | #if defined(_LIBCPP_VERSION) 27 | true; 28 | #else 29 | false; 30 | #endif 31 | m.attr("defined___GLIBCXX__") = 32 | #if defined(__GLIBCXX__) 33 | true; 34 | #else 35 | false; 36 | #endif 37 | } 38 | -------------------------------------------------------------------------------- /tests/test_unnamed_namespace_a.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pytest 4 | 5 | from pybind11_tests import unnamed_namespace_a as m 6 | from pybind11_tests import unnamed_namespace_b as mb 7 | 8 | XFAIL_CONDITION = "not m.defined_WIN32_or__WIN32 and (m.defined___clang__ or m.defined__LIBCPP_VERSION)" 9 | XFAIL_REASON = "Known issues: https://github.com/pybind/pybind11/pull/4319" 10 | 11 | 12 | @pytest.mark.xfail(XFAIL_CONDITION, reason=XFAIL_REASON, strict=False) 13 | @pytest.mark.parametrize( 14 | "any_struct", [m.unnamed_namespace_a_any_struct, mb.unnamed_namespace_b_any_struct] 15 | ) 16 | def test_have_class_any_struct(any_struct): 17 | assert any_struct is not None 18 | 19 | 20 | def test_have_at_least_one_class_any_struct(): 21 | assert ( 22 | m.unnamed_namespace_a_any_struct is not None 23 | or mb.unnamed_namespace_b_any_struct is not None 24 | ) 25 | 26 | 27 | @pytest.mark.xfail(XFAIL_CONDITION, reason=XFAIL_REASON, strict=True) 28 | def test_have_both_class_any_struct(): 29 | assert m.unnamed_namespace_a_any_struct is not None 30 | assert mb.unnamed_namespace_b_any_struct is not None 31 | -------------------------------------------------------------------------------- /tests/test_unnamed_namespace_b.cpp: -------------------------------------------------------------------------------- 1 | #include "pybind11_tests.h" 2 | 3 | namespace { 4 | struct any_struct {}; 5 | } // namespace 6 | 7 | TEST_SUBMODULE(unnamed_namespace_b, m) { 8 | if (py::detail::get_type_info(typeid(any_struct)) == nullptr) { 9 | py::class_(m, "unnamed_namespace_b_any_struct"); 10 | } else { 11 | m.attr("unnamed_namespace_b_any_struct") = py::none(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tests/test_unnamed_namespace_b.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from pybind11_tests import unnamed_namespace_b as m 4 | 5 | 6 | def test_have_attr_any_struct(): 7 | assert hasattr(m, "unnamed_namespace_b_any_struct") 8 | -------------------------------------------------------------------------------- /tests/test_vector_unique_ptr_member.cpp: -------------------------------------------------------------------------------- 1 | #include "pybind11_tests.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace pybind11_tests { 8 | namespace vector_unique_ptr_member { 9 | 10 | struct DataType {}; 11 | 12 | // Reduced from a use case in the wild. 13 | struct VectorOwner { 14 | static std::unique_ptr Create(std::size_t num_elems) { 15 | return std::unique_ptr( 16 | new VectorOwner(std::vector>(num_elems))); 17 | } 18 | 19 | std::size_t data_size() const { return data_.size(); } 20 | 21 | private: 22 | explicit VectorOwner(std::vector> data) : data_(std::move(data)) {} 23 | 24 | const std::vector> data_; 25 | }; 26 | 27 | } // namespace vector_unique_ptr_member 28 | } // namespace pybind11_tests 29 | 30 | namespace pybind11 { 31 | namespace detail { 32 | 33 | template <> 34 | struct is_copy_constructible 35 | : std::false_type {}; 36 | 37 | template <> 38 | struct is_move_constructible 39 | : std::false_type {}; 40 | 41 | } // namespace detail 42 | } // namespace pybind11 43 | 44 | using namespace pybind11_tests::vector_unique_ptr_member; 45 | 46 | py::object py_cast_VectorOwner_ptr(VectorOwner *ptr) { return py::cast(ptr); } 47 | 48 | TEST_SUBMODULE(vector_unique_ptr_member, m) { 49 | py::class_(m, "VectorOwner") 50 | .def_static("Create", &VectorOwner::Create) 51 | .def("data_size", &VectorOwner::data_size); 52 | 53 | m.def("py_cast_VectorOwner_ptr", py_cast_VectorOwner_ptr); 54 | } 55 | -------------------------------------------------------------------------------- /tests/test_vector_unique_ptr_member.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pytest 4 | 5 | from pybind11_tests import vector_unique_ptr_member as m 6 | 7 | 8 | @pytest.mark.parametrize("num_elems", range(3)) 9 | def test_create(num_elems): 10 | vo = m.VectorOwner.Create(num_elems) 11 | assert vo.data_size() == num_elems 12 | 13 | 14 | def test_cast(): 15 | vo = m.VectorOwner.Create(0) 16 | assert m.py_cast_VectorOwner_ptr(vo) is vo 17 | -------------------------------------------------------------------------------- /tests/test_warnings.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | tests/test_warnings.cpp -- usage of warnings::warn() and warnings categories. 3 | 4 | Copyright (c) 2024 Jan Iwaszkiewicz 5 | 6 | All rights reserved. Use of this source code is governed by a 7 | BSD-style license that can be found in the LICENSE file. 8 | */ 9 | 10 | #include 11 | 12 | #include "pybind11_tests.h" 13 | 14 | #include 15 | 16 | TEST_SUBMODULE(warnings_, m) { 17 | 18 | // Test warning mechanism base 19 | m.def("warn_and_return_value", []() { 20 | std::string message = "This is simple warning"; 21 | py::warnings::warn(message.c_str(), PyExc_Warning); 22 | return 21; 23 | }); 24 | 25 | m.def("warn_with_default_category", []() { py::warnings::warn("This is RuntimeWarning"); }); 26 | 27 | m.def("warn_with_different_category", 28 | []() { py::warnings::warn("This is FutureWarning", PyExc_FutureWarning); }); 29 | 30 | m.def("warn_with_invalid_category", 31 | []() { py::warnings::warn("Invalid category", PyExc_Exception); }); 32 | 33 | // Test custom warnings 34 | PYBIND11_CONSTINIT static py::gil_safe_call_once_and_store ex_storage; 35 | ex_storage.call_once_and_store_result([&]() { 36 | return py::warnings::new_warning_type(m, "CustomWarning", PyExc_DeprecationWarning); 37 | }); 38 | 39 | m.def("warn_with_custom_type", []() { 40 | py::warnings::warn("This is CustomWarning", ex_storage.get_stored()); 41 | return 37; 42 | }); 43 | 44 | m.def("register_duplicate_warning", 45 | [m]() { py::warnings::new_warning_type(m, "CustomWarning", PyExc_RuntimeWarning); }); 46 | } 47 | -------------------------------------------------------------------------------- /tests/test_warnings.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import warnings 4 | 5 | import pytest 6 | 7 | import pybind11_tests # noqa: F401 8 | from pybind11_tests import warnings_ as m 9 | 10 | 11 | @pytest.mark.parametrize( 12 | ("expected_category", "expected_message", "expected_value", "module_function"), 13 | [ 14 | (Warning, "This is simple warning", 21, m.warn_and_return_value), 15 | (RuntimeWarning, "This is RuntimeWarning", None, m.warn_with_default_category), 16 | (FutureWarning, "This is FutureWarning", None, m.warn_with_different_category), 17 | ], 18 | ) 19 | def test_warning_simple( 20 | expected_category, expected_message, expected_value, module_function 21 | ): 22 | with pytest.warns(Warning) as excinfo: 23 | value = module_function() 24 | 25 | assert issubclass(excinfo[0].category, expected_category) 26 | assert str(excinfo[0].message) == expected_message 27 | assert value == expected_value 28 | 29 | 30 | def test_warning_wrong_subclass_fail(): 31 | with pytest.raises(Exception) as excinfo: 32 | m.warn_with_invalid_category() 33 | 34 | assert issubclass(excinfo.type, RuntimeError) 35 | assert ( 36 | str(excinfo.value) 37 | == "pybind11::warnings::warn(): cannot raise warning, category must be a subclass of PyExc_Warning!" 38 | ) 39 | 40 | 41 | def test_warning_double_register_fail(): 42 | with pytest.raises(Exception) as excinfo: 43 | m.register_duplicate_warning() 44 | 45 | assert issubclass(excinfo.type, RuntimeError) 46 | assert ( 47 | str(excinfo.value) 48 | == 'pybind11::warnings::new_warning_type(): an attribute with name "CustomWarning" exists already.' 49 | ) 50 | 51 | 52 | def test_warning_register(): 53 | assert m.CustomWarning is not None 54 | 55 | with pytest.warns(m.CustomWarning) as excinfo: 56 | warnings.warn("This is warning from Python!", m.CustomWarning, stacklevel=1) 57 | 58 | assert issubclass(excinfo[0].category, DeprecationWarning) 59 | assert str(excinfo[0].message) == "This is warning from Python!" 60 | 61 | 62 | def test_warning_custom(): 63 | with pytest.warns(m.CustomWarning) as excinfo: 64 | value = m.warn_with_custom_type() 65 | 66 | assert issubclass(excinfo[0].category, DeprecationWarning) 67 | assert str(excinfo[0].message) == "This is CustomWarning" 68 | assert value == 37 69 | -------------------------------------------------------------------------------- /tools/FindCatch.cmake: -------------------------------------------------------------------------------- 1 | # - Find the Catch test framework or download it (single header) 2 | # 3 | # This is a quick module for internal use. It assumes that Catch is 4 | # REQUIRED and that a minimum version is provided (not EXACT). If 5 | # a suitable version isn't found locally, the single header file 6 | # will be downloaded and placed in the build dir: PROJECT_BINARY_DIR. 7 | # 8 | # This code sets the following variables: 9 | # CATCH_INCLUDE_DIR - path to catch.hpp 10 | # CATCH_VERSION - version number 11 | 12 | option(DOWNLOAD_CATCH "Download catch2 if not found") 13 | 14 | if(NOT Catch_FIND_VERSION) 15 | message(FATAL_ERROR "A version number must be specified.") 16 | elseif(Catch_FIND_REQUIRED) 17 | message(FATAL_ERROR "This module assumes Catch is not required.") 18 | elseif(Catch_FIND_VERSION_EXACT) 19 | message(FATAL_ERROR "Exact version numbers are not supported, only minimum.") 20 | endif() 21 | 22 | # Extract the version number from catch.hpp 23 | function(_get_catch_version) 24 | file( 25 | STRINGS "${CATCH_INCLUDE_DIR}/catch.hpp" version_line 26 | REGEX "Catch v.*" 27 | LIMIT_COUNT 1) 28 | if(version_line MATCHES "Catch v([0-9]+)\\.([0-9]+)\\.([0-9]+)") 29 | set(CATCH_VERSION 30 | "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}" 31 | PARENT_SCOPE) 32 | endif() 33 | endfunction() 34 | 35 | # Download the single-header version of Catch 36 | function(_download_catch version destination_dir) 37 | message(STATUS "Downloading catch v${version}...") 38 | set(url https://github.com/philsquared/Catch/releases/download/v${version}/catch.hpp) 39 | file( 40 | DOWNLOAD ${url} "${destination_dir}/catch.hpp" 41 | STATUS status 42 | LOG log) 43 | list(GET status 0 error) 44 | if(error) 45 | string(REPLACE "\n" "\n " log " ${log}") 46 | message(FATAL_ERROR "Could not download URL:\n" " ${url}\n" "Log:\n" "${log}") 47 | endif() 48 | set(CATCH_INCLUDE_DIR 49 | "${destination_dir}" 50 | CACHE INTERNAL "") 51 | endfunction() 52 | 53 | # Look for catch locally 54 | find_path( 55 | CATCH_INCLUDE_DIR 56 | NAMES catch.hpp 57 | PATH_SUFFIXES catch2) 58 | if(CATCH_INCLUDE_DIR) 59 | _get_catch_version() 60 | endif() 61 | 62 | # Download the header if it wasn't found or if it's outdated 63 | if(NOT CATCH_VERSION OR CATCH_VERSION VERSION_LESS ${Catch_FIND_VERSION}) 64 | if(DOWNLOAD_CATCH) 65 | _download_catch(${Catch_FIND_VERSION} "${PROJECT_BINARY_DIR}/catch/") 66 | _get_catch_version() 67 | else() 68 | set(CATCH_FOUND FALSE) 69 | return() 70 | endif() 71 | endif() 72 | 73 | add_library(Catch2::Catch2 IMPORTED INTERFACE) 74 | set_property(TARGET Catch2::Catch2 PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${CATCH_INCLUDE_DIR}") 75 | 76 | set(CATCH_FOUND TRUE) 77 | -------------------------------------------------------------------------------- /tools/JoinPaths.cmake: -------------------------------------------------------------------------------- 1 | # This module provides function for joining paths 2 | # known from most languages 3 | # 4 | # SPDX-License-Identifier: (MIT OR CC0-1.0) 5 | # Copyright 2020 Jan Tojnar 6 | # https://github.com/jtojnar/cmake-snips 7 | # 8 | # Modelled after Python’s os.path.join 9 | # https://docs.python.org/3.7/library/os.path.html#os.path.join 10 | # Windows not supported 11 | function(join_paths joined_path first_path_segment) 12 | set(temp_path "${first_path_segment}") 13 | foreach(current_segment IN LISTS ARGN) 14 | if(NOT ("${current_segment}" STREQUAL "")) 15 | if(IS_ABSOLUTE "${current_segment}") 16 | set(temp_path "${current_segment}") 17 | else() 18 | set(temp_path "${temp_path}/${current_segment}") 19 | endif() 20 | endif() 21 | endforeach() 22 | set(${joined_path} "${temp_path}" PARENT_SCOPE) 23 | endfunction() 24 | -------------------------------------------------------------------------------- /tools/check-style.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Script to check include/test code for common pybind11 code style errors. 4 | # 5 | # This script currently checks for 6 | # 7 | # 1. missing space between keyword and parenthesis, e.g.: for(, if(, while( 8 | # 2. Missing space between right parenthesis and brace, e.g. 'for (...){' 9 | # 3. opening brace on its own line. It should always be on the same line as the 10 | # if/while/for/do statement. 11 | # 12 | # Invoke as: tools/check-style.sh 13 | # 14 | 15 | check_style_errors=0 16 | IFS=$'\n' 17 | 18 | 19 | found="$(grep '\<\(if\|for\|while\|catch\)(\|){' "$@" -rn --color=always)" 20 | if [ -n "$found" ]; then 21 | echo -e '\033[31;01mError: found the following coding style problems:\033[0m' 22 | check_style_errors=1 23 | echo "${found//^/ /}" 24 | fi 25 | 26 | found="$(awk ' 27 | function prefix(filename, lineno) { 28 | return " \033[35m" filename "\033[36m:\033[32m" lineno "\033[36m:\033[0m" 29 | } 30 | function mark(pattern, string) { sub(pattern, "\033[01;31m&\033[0m", string); return string } 31 | last && /^\s*{/ { 32 | print prefix(FILENAME, FNR-1) mark("\\)\\s*$", last) 33 | print prefix(FILENAME, FNR) mark("^\\s*{", $0) 34 | last="" 35 | } 36 | { last = /(if|for|while|catch|switch)\s*\(.*\)\s*$/ ? $0 : "" } 37 | ' "$(find include -type f)" "$@")" 38 | if [ -n "$found" ]; then 39 | check_style_errors=1 40 | echo -e '\033[31;01mError: braces should occur on the same line as the if/while/.. statement. Found issues in the following files:\033[0m' 41 | echo "$found" 42 | fi 43 | 44 | exit $check_style_errors 45 | -------------------------------------------------------------------------------- /tools/cmake_uninstall.cmake.in: -------------------------------------------------------------------------------- 1 | # Source: https://gitlab.kitware.com/cmake/community/-/wikis/FAQ#can-i-do-make-uninstall-with-cmake 2 | 3 | if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt") 4 | message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt") 5 | endif() 6 | 7 | file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files) 8 | string(REGEX REPLACE "\n" ";" files "${files}") 9 | foreach(file ${files}) 10 | message(STATUS "Uninstalling $ENV{DESTDIR}${file}") 11 | if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") 12 | exec_program( 13 | "@CMAKE_COMMAND@" ARGS 14 | "-E remove \"$ENV{DESTDIR}${file}\"" 15 | OUTPUT_VARIABLE rm_out 16 | RETURN_VALUE rm_retval) 17 | if(NOT "${rm_retval}" STREQUAL 0) 18 | message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") 19 | endif() 20 | else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") 21 | message(STATUS "File $ENV{DESTDIR}${file} does not exist.") 22 | endif() 23 | endforeach() 24 | -------------------------------------------------------------------------------- /tools/codespell_ignore_lines_from_errors.py: -------------------------------------------------------------------------------- 1 | """Simple script for rebuilding .codespell-ignore-lines 2 | 3 | Usage: 4 | 5 | cat < /dev/null > .codespell-ignore-lines 6 | pre-commit run --all-files codespell >& /tmp/codespell_errors.txt 7 | python3 tools/codespell_ignore_lines_from_errors.py /tmp/codespell_errors.txt > .codespell-ignore-lines 8 | 9 | git diff to review changes, then commit, push. 10 | """ 11 | 12 | from __future__ import annotations 13 | 14 | import sys 15 | 16 | 17 | def run(args: list[str]) -> None: 18 | assert len(args) == 1, "codespell_errors.txt" 19 | cache = {} 20 | done = set() 21 | with open(args[0]) as f: 22 | lines = f.read().splitlines() 23 | 24 | for line in sorted(lines): 25 | i = line.find(" ==> ") 26 | if i > 0: 27 | flds = line[:i].split(":") 28 | if len(flds) >= 2: 29 | filename, line_num = flds[:2] 30 | if filename not in cache: 31 | with open(filename) as f: 32 | cache[filename] = f.read().splitlines() 33 | supp = cache[filename][int(line_num) - 1] 34 | if supp not in done: 35 | print(supp) 36 | done.add(supp) 37 | 38 | 39 | if __name__ == "__main__": 40 | run(args=sys.argv[1:]) 41 | -------------------------------------------------------------------------------- /tools/libsize.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import os 4 | import sys 5 | 6 | # Internal build script for generating debugging test .so size. 7 | # Usage: 8 | # python libsize.py file.so save.txt -- displays the size of file.so and, if save.txt exists, compares it to the 9 | # size in it, then overwrites save.txt with the new size for future runs. 10 | 11 | if len(sys.argv) != 3: 12 | sys.exit("Invalid arguments: usage: python libsize.py file.so save.txt") 13 | 14 | lib = sys.argv[1] 15 | save = sys.argv[2] 16 | 17 | if not os.path.exists(lib): 18 | sys.exit(f"Error: requested file ({lib}) does not exist") 19 | 20 | libsize = os.path.getsize(lib) 21 | 22 | print("------", os.path.basename(lib), "file size:", libsize, end="") 23 | 24 | if os.path.exists(save): 25 | with open(save) as sf: 26 | oldsize = int(sf.readline()) 27 | 28 | if oldsize > 0: 29 | change = libsize - oldsize 30 | if change == 0: 31 | print(" (no change)") 32 | else: 33 | print(f" (change of {change:+} bytes = {change / oldsize:+.2%})") 34 | else: 35 | print() 36 | 37 | with open(save, "w") as sf: 38 | sf.write(str(libsize)) 39 | -------------------------------------------------------------------------------- /tools/make_global.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S uv run -q 2 | 3 | # /// script 4 | # dependencies = ["tomlkit"] 5 | # /// 6 | from __future__ import annotations 7 | 8 | from pathlib import Path 9 | 10 | import tomlkit 11 | 12 | DIR = Path(__file__).parent.resolve() 13 | PYPROJECT = DIR.parent / "pyproject.toml" 14 | 15 | 16 | def get_global() -> str: 17 | pyproject = tomlkit.parse(PYPROJECT.read_text()) 18 | del pyproject["tool"]["scikit-build"]["generate"] 19 | del pyproject["project"]["entry-points"] 20 | del pyproject["project"]["scripts"] 21 | del pyproject["tool"]["scikit-build"]["metadata"]["optional-dependencies"] 22 | pyproject["project"]["name"] = "pybind11-global" 23 | pyproject["tool"]["scikit-build"]["experimental"] = True 24 | pyproject["tool"]["scikit-build"]["wheel"]["install-dir"] = "/data" 25 | pyproject["tool"]["scikit-build"]["wheel"]["packages"] = [] 26 | 27 | result = tomlkit.dumps(pyproject) 28 | assert isinstance(result, str) 29 | return result 30 | 31 | 32 | if __name__ == "__main__": 33 | print(get_global()) 34 | -------------------------------------------------------------------------------- /tools/pybind11.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix_for_pc_file@ 2 | includedir=@includedir_for_pc_file@ 3 | 4 | Name: @PROJECT_NAME@ 5 | Description: Seamless operability between C++11 and Python 6 | Version: @PROJECT_VERSION@ 7 | Cflags: -I${includedir} 8 | --------------------------------------------------------------------------------