├── .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 │ ├── emscripten.yaml │ ├── format.yml │ ├── labeler.yml │ └── upstream.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .readthedocs.yml ├── CMakeLists.txt ├── CONTRIBUTING.md ├── LICENSE ├── MANIFEST.in ├── README.rst ├── README_smart_holder.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 │ ├── 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.rst ├── 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 │ ├── detail │ ├── class.h │ ├── common.h │ ├── cpp_conduit.h │ ├── descr.h │ ├── dynamic_raw_ptr_cast_if_possible.h │ ├── function_record_pyobject.h │ ├── init.h │ ├── internals.h │ ├── native_enum_data.h │ ├── struct_smart_holder.h │ ├── try_as_void_ptr_capsule_get_pointer.h │ ├── type_caster_base.h │ ├── type_caster_odr_guard.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 │ ├── iostream.h │ ├── native_enum.h │ ├── numpy.h │ ├── operators.h │ ├── options.h │ ├── pybind11.h │ ├── pytypes.h │ ├── smart_holder.h │ ├── stl.h │ ├── stl │ └── filesystem.h │ ├── stl_bind.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 ├── setup.cfg ├── setup.py ├── tests ├── CMakeLists.txt ├── conftest.py ├── constructor_stats.h ├── cross_module_gil_utils.cpp ├── cross_module_interleaved_error_already_set.cpp ├── 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 ├── local_bindings.h ├── namespace_visibility.inl ├── namespace_visibility_1.cpp ├── namespace_visibility_1s.cpp ├── namespace_visibility_2.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_property_stl.cpp ├── test_class_sh_property_stl.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_class_sh_void_ptr_capsule.cpp ├── test_class_sh_void_ptr_capsule.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_descr_src_loc.cpp ├── test_descr_src_loc.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_trampoline.py ├── test_enum.cpp ├── test_enum.py ├── test_eval.cpp ├── test_eval.py ├── test_eval_call.py ├── test_exc_namespace_visibility.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_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_python_multiple_inheritance.cpp ├── test_python_multiple_inheritance.py ├── test_pytypes.cpp ├── test_pytypes.py ├── test_return_value_policy_override.cpp ├── test_return_value_policy_override.py ├── test_return_value_policy_pack.cpp ├── test_return_value_policy_pack.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_odr_guard_1.cpp ├── test_type_caster_odr_guard_1.py ├── test_type_caster_odr_guard_2.cpp ├── test_type_caster_odr_guard_2.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 ├── pybind11.pc.in ├── pybind11Common.cmake ├── pybind11Config.cmake.in ├── pybind11GuessPythonExtSuffix.cmake ├── pybind11NewTools.cmake ├── pybind11Tools.cmake ├── pyproject.toml ├── setup_global.py.in ├── setup_main.py.in └── test-pybind11GuessPythonExtSuffix.cmake └── ubench ├── holder_comparison.cpp ├── holder_comparison.py ├── holder_comparison_extract_sheet_data.py ├── number_bucket.h └── python └── number_bucket.clif /.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 | 67 | CheckOptions: 68 | - key: modernize-use-equals-default.IgnoreMacros 69 | value: false 70 | - key: performance-for-range-copy.WarnOnAllAutoCopies 71 | value: true 72 | - key: performance-inefficient-string-concatenation.StrictMode 73 | value: true 74 | - key: performance-unnecessary-value-param.AllowedTypes 75 | value: 'exception_ptr$;' 76 | - key: readability-implicit-bool-conversion.AllowPointerConditions 77 | value: true 78 | 79 | HeaderFilterRegex: 'pybind11/.*h' 80 | -------------------------------------------------------------------------------- /.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.rst' 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.rst" 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 | ```rst 16 | 17 | ``` 18 | 19 | 20 | -------------------------------------------------------------------------------- /.github/workflows/configure.yml: -------------------------------------------------------------------------------- 1 | name: Config 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | push: 7 | branches: 8 | - master 9 | - stable 10 | - smart_holder 11 | - v* 12 | 13 | permissions: 14 | contents: read 15 | 16 | env: 17 | PIP_BREAK_SYSTEM_PACKAGES: 1 18 | # For cmake: 19 | VERBOSE: 1 20 | 21 | jobs: 22 | # This tests various versions of CMake in various combinations, to make sure 23 | # the configure step passes. 24 | cmake: 25 | strategy: 26 | fail-fast: false 27 | matrix: 28 | runs-on: [ubuntu-20.04, macos-13, windows-latest] 29 | arch: [x64] 30 | cmake: ["3.26"] 31 | 32 | include: 33 | - runs-on: ubuntu-20.04 34 | arch: x64 35 | cmake: "3.15" 36 | 37 | - runs-on: ubuntu-20.04 38 | arch: x64 39 | cmake: "3.29" 40 | 41 | - runs-on: macos-13 42 | arch: x64 43 | cmake: "3.15" 44 | 45 | - runs-on: windows-2019 46 | arch: x64 # x86 compilers seem to be missing on 2019 image 47 | cmake: "3.18" 48 | 49 | name: 🐍 3.8 • CMake ${{ matrix.cmake }} • ${{ matrix.runs-on }} 50 | runs-on: ${{ matrix.runs-on }} 51 | 52 | steps: 53 | - uses: actions/checkout@v4 54 | 55 | - name: Setup Python 3.8 56 | uses: actions/setup-python@v5 57 | with: 58 | python-version: 3.8 59 | architecture: ${{ matrix.arch }} 60 | 61 | - name: Prepare env 62 | run: python -m pip install -r tests/requirements.txt 63 | 64 | # An action for adding a specific version of CMake: 65 | # https://github.com/jwlawson/actions-setup-cmake 66 | - name: Setup CMake ${{ matrix.cmake }} 67 | uses: jwlawson/actions-setup-cmake@v2.0 68 | with: 69 | cmake-version: ${{ matrix.cmake }} 70 | 71 | # These steps use a directory with a space in it intentionally 72 | - name: Make build directories 73 | run: mkdir "build dir" 74 | 75 | - name: Configure 76 | working-directory: build dir 77 | shell: bash 78 | run: > 79 | cmake .. 80 | -DPYBIND11_WERROR=ON 81 | -DDOWNLOAD_CATCH=ON 82 | -DPYTHON_EXECUTABLE=$(python -c "import sys; print(sys.executable)") 83 | 84 | # Only build and test if this was manually triggered in the GitHub UI 85 | - name: Build 86 | working-directory: build dir 87 | if: github.event_name == 'workflow_dispatch' 88 | run: cmake --build . --config Release 89 | 90 | - name: Test 91 | working-directory: build dir 92 | if: github.event_name == 'workflow_dispatch' 93 | run: cmake --build . --config Release --target check 94 | -------------------------------------------------------------------------------- /.github/workflows/emscripten.yaml: -------------------------------------------------------------------------------- 1 | name: WASM 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | branches: 7 | - master 8 | - stable 9 | - smart_holder 10 | - v* 11 | 12 | concurrency: 13 | group: ${{ github.workflow }}-${{ github.ref }} 14 | cancel-in-progress: true 15 | 16 | jobs: 17 | build-wasm-emscripten: 18 | name: Pyodide wheel 19 | runs-on: ubuntu-22.04 20 | steps: 21 | - uses: actions/checkout@v4 22 | with: 23 | submodules: true 24 | fetch-depth: 0 25 | 26 | - uses: pypa/cibuildwheel@v2.20 27 | env: 28 | PYODIDE_BUILD_EXPORTS: whole_archive 29 | with: 30 | package-dir: tests 31 | only: cp312-pyodide_wasm32 32 | -------------------------------------------------------------------------------- /.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 | - smart_holder 14 | - "v*" 15 | 16 | permissions: 17 | contents: read 18 | 19 | env: 20 | FORCE_COLOR: 3 21 | # For cmake: 22 | VERBOSE: 1 23 | 24 | jobs: 25 | pre-commit: 26 | name: Format 27 | runs-on: ubuntu-latest 28 | steps: 29 | - uses: actions/checkout@v4 30 | - uses: actions/setup-python@v5 31 | with: 32 | python-version: "3.x" 33 | - name: Add matchers 34 | run: echo "::add-matcher::$GITHUB_WORKSPACE/.github/matchers/pylint.json" 35 | - uses: pre-commit/action@v3.0.1 36 | with: 37 | # Slow hooks are marked with manual - slow is okay here, run them too 38 | extra_args: --hook-stage manual --all-files 39 | 40 | clang-tidy: 41 | # When making changes here, please also review the "Clang-Tidy" section 42 | # in .github/CONTRIBUTING.md and update as needed. 43 | name: Clang-Tidy 44 | runs-on: ubuntu-latest 45 | container: silkeh/clang:18-bookworm 46 | steps: 47 | - uses: actions/checkout@v4 48 | 49 | - name: Install requirements 50 | run: apt-get update && apt-get install -y git python3-dev python3-pytest 51 | 52 | - name: Configure 53 | run: > 54 | cmake -S . -B build 55 | -DCMAKE_CXX_CLANG_TIDY="$(which clang-tidy);--use-color;--warnings-as-errors=*" 56 | -DDOWNLOAD_EIGEN=ON 57 | -DDOWNLOAD_CATCH=ON 58 | -DCMAKE_CXX_STANDARD=17 59 | 60 | - name: Build 61 | run: cmake --build build -j 2 -- --keep-going 62 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | ## Community Guidelines 4 | 5 | This project follows 6 | [Google's Open Source Community Guidelines](https://opensource.google/conduct/). 7 | 8 | ## Contributor License Agreement 9 | 10 | Contributions to this project must be accompanied by a Contributor License 11 | Agreement (CLA). You (or your employer) retain the copyright to your 12 | contribution; this simply gives us permission to use and redistribute your 13 | contributions as part of the project. Head over to 14 | to see your current agreements on file or 15 | to sign a new one. 16 | 17 | You generally only need to submit a CLA once, so if you've already submitted one 18 | (even if it was for a different project), you probably don't need to do it 19 | again. 20 | 21 | ## Code Reviews 22 | 23 | All submissions, including submissions by project members, require review. We 24 | use GitHub pull requests for this purpose. Consult 25 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 26 | information on using pull requests. 27 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | prune tests 2 | prune ubench 3 | include CONTRIBUTING.md 4 | include README_smart_holder.rst 5 | recursive-include pybind11/include/pybind11 *.h 6 | recursive-include pybind11 *.py 7 | recursive-include pybind11 py.typed 8 | include pybind11/share/cmake/pybind11/*.cmake 9 | include LICENSE README.rst SECURITY.md pyproject.toml setup.py setup.cfg 10 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ============================================================================ 2 | pybind11clif — A fork of pybind11 to support the PyCLIF-pybind11 unification 3 | ============================================================================ 4 | 5 | WARNING — The PyCLIF-pybind11 unification was SUSPENDED. 6 | ======================================================== 7 | 8 | Possibly, **THIS FORK WILL NO LONGER BE UPDATED**. 9 | 10 | Please use the upstream project: https://github.com/pybind/pybind11 11 | 12 | For completeness, this repo was renamed twice: 13 | 14 | * Originally the name was google/pywrapcc (Feb 2023), 15 | 16 | * then google/pybind11k (Apr 2024), 17 | 18 | * then google/pybind11clif (Aug 2024). 19 | -------------------------------------------------------------------------------- /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/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 | 40 | .. toctree:: 41 | :caption: Extra Information 42 | :maxdepth: 1 43 | 44 | faq 45 | benchmark 46 | limitations 47 | reference 48 | cmake/index 49 | -------------------------------------------------------------------------------- /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/google/pybind11clif/4841661df5daf26ecdedaace54e64d0782e63f64/docs/pybind11-logo.png -------------------------------------------------------------------------------- /docs/pybind11_vs_boost_python1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/pybind11clif/4841661df5daf26ecdedaace54e64d0782e63f64/docs/pybind11_vs_boost_python1.png -------------------------------------------------------------------------------- /docs/pybind11_vs_boost_python2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/pybind11clif/4841661df5daf26ecdedaace54e64d0782e63f64/docs/pybind11_vs_boost_python2.png -------------------------------------------------------------------------------- /docs/requirements.in: -------------------------------------------------------------------------------- 1 | breathe 2 | furo 3 | sphinx 4 | sphinx-copybutton 5 | sphinxcontrib-moderncmakedomain 6 | sphinxcontrib-svg2pdfconverter 7 | -------------------------------------------------------------------------------- /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/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("__cpp_conduit__"); 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 | #define PYBIND11_HAS_CPP_CONDUIT 75 | 76 | PYBIND11_NAMESPACE_END(detail) 77 | PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) 78 | -------------------------------------------------------------------------------- /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/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 "internals.h" 9 | 10 | #include 11 | 12 | #ifdef PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT 13 | # include "struct_smart_holder.h" 14 | #endif 15 | 16 | PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) 17 | 18 | #ifdef PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT 19 | using pybindit::memory::smart_holder; 20 | #endif 21 | 22 | PYBIND11_NAMESPACE_BEGIN(detail) 23 | 24 | #ifdef PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT 25 | template 26 | using is_smart_holder = std::is_same; 27 | #else 28 | template 29 | struct is_smart_holder : std::false_type {}; 30 | #endif 31 | 32 | PYBIND11_NAMESPACE_END(detail) 33 | PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) 34 | -------------------------------------------------------------------------------- /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/native_enum.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 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 | 16 | PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) 17 | 18 | enum class native_enum_kind { Enum, IntEnum }; 19 | 20 | /// Conversions between Python's native (stdlib) enum types and C++ enums. 21 | template 22 | class native_enum : public detail::native_enum_data { 23 | public: 24 | using Underlying = typename std::underlying_type::type; 25 | 26 | explicit native_enum(const char *name, native_enum_kind kind) 27 | : detail::native_enum_data( 28 | name, std::type_index(typeid(Type)), kind == native_enum_kind::IntEnum) { 29 | if (detail::get_local_type_info(typeid(Type)) != nullptr 30 | || detail::get_global_type_info(typeid(Type)) != nullptr) { 31 | pybind11_fail( 32 | "pybind11::native_enum<...>(\"" + enum_name_encoded 33 | + "\") is already registered as a `pybind11::enum_` or `pybind11::class_`!"); 34 | } 35 | if (detail::global_internals_native_enum_type_map_contains(enum_type_index)) { 36 | pybind11_fail("pybind11::native_enum<...>(\"" + enum_name_encoded 37 | + "\") is already registered!"); 38 | } 39 | arm_correct_use_check(); 40 | } 41 | 42 | /// Export enumeration entries into the parent scope 43 | native_enum &export_values() { 44 | export_values_flag = true; 45 | return *this; 46 | } 47 | 48 | /// Add an enumeration entry 49 | native_enum &value(char const *name, Type value, const char *doc = nullptr) { 50 | disarm_correct_use_check(); 51 | members.append(make_tuple(name, static_cast(value))); 52 | if (doc) { 53 | docs.append(make_tuple(name, doc)); 54 | } 55 | arm_correct_use_check(); 56 | return *this; 57 | } 58 | 59 | native_enum(const native_enum &) = delete; 60 | native_enum &operator=(const native_enum &) = delete; 61 | }; 62 | 63 | PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) 64 | -------------------------------------------------------------------------------- /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/smart_holder.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021-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.h" 8 | 9 | // Legacy macros introduced with smart_holder_type_casters implementation in 2021. 10 | // Deprecated. 11 | #define PYBIND11_TYPE_CASTER_BASE_HOLDER(...) 12 | #define PYBIND11_SMART_HOLDER_TYPE_CASTERS(...) 13 | #define PYBIND11_SH_AVL(...) // "Smart_Holder if AVaiLable" 14 | #define PYBIND11_SH_DEF(...) // "Smart_Holder if DEFault" 15 | -------------------------------------------------------------------------------- /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/internals.h" 8 | 9 | #ifdef PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT 10 | 11 | # include "detail/common.h" 12 | # include "detail/using_smart_holder.h" 13 | # include "detail/value_and_holder.h" 14 | 15 | PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) 16 | 17 | PYBIND11_NAMESPACE_BEGIN(detail) 18 | // PYBIND11:REMINDER: Needs refactoring of existing pybind11 code. 19 | inline bool deregister_instance(instance *self, void *valptr, const type_info *tinfo); 20 | PYBIND11_NAMESPACE_END(detail) 21 | 22 | // The original core idea for this struct goes back to PyCLIF: 23 | // https://github.com/google/clif/blob/07f95d7e69dca2fcf7022978a55ef3acff506c19/clif/python/runtime.cc#L37 24 | // URL provided here mainly to give proper credit. 25 | struct trampoline_self_life_support { 26 | detail::value_and_holder v_h; 27 | 28 | trampoline_self_life_support() = default; 29 | 30 | void activate_life_support(const detail::value_and_holder &v_h_) { 31 | Py_INCREF((PyObject *) v_h_.inst); 32 | v_h = v_h_; 33 | } 34 | 35 | void deactivate_life_support() { 36 | Py_DECREF((PyObject *) v_h.inst); 37 | v_h = detail::value_and_holder(); 38 | } 39 | 40 | ~trampoline_self_life_support() { 41 | if (v_h.inst != nullptr && v_h.vh != nullptr) { 42 | void *value_void_ptr = v_h.value_ptr(); 43 | if (value_void_ptr != nullptr) { 44 | PyGILState_STATE threadstate = PyGILState_Ensure(); 45 | v_h.value_ptr() = nullptr; 46 | v_h.holder().release_disowned(); 47 | detail::deregister_instance(v_h.inst, value_void_ptr, v_h.type); 48 | Py_DECREF((PyObject *) v_h.inst); // Must be after deregister. 49 | PyGILState_Release(threadstate); 50 | } 51 | } 52 | } 53 | 54 | // For the next two, the default implementations generate undefined behavior (ASAN failures 55 | // manually verified). The reason is that v_h needs to be kept default-initialized. 56 | trampoline_self_life_support(const trampoline_self_life_support &) {} 57 | trampoline_self_life_support(trampoline_self_life_support &&) noexcept {} 58 | 59 | // These should never be needed (please provide test cases if you think they are). 60 | trampoline_self_life_support &operator=(const trampoline_self_life_support &) = delete; 61 | trampoline_self_life_support &operator=(trampoline_self_life_support &&) = delete; 62 | }; 63 | 64 | PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) 65 | 66 | #endif // PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT 67 | -------------------------------------------------------------------------------- /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 | || policy == return_value_policy::_clif_automatic) { 37 | return src; 38 | } 39 | if (policy == return_value_policy::reference 40 | || policy == return_value_policy::automatic_reference) { 41 | return handle(src).inc_ref(); 42 | } 43 | pybind11_fail("type_caster::cast(): unsupported return_value_policy: " 44 | + std::to_string(static_cast(policy))); 45 | } 46 | 47 | bool load(handle src, bool) { 48 | value = reinterpret_borrow(src); 49 | return true; 50 | } 51 | 52 | template 53 | using cast_op_type = PyObject *; 54 | 55 | explicit operator PyObject *() { return value.ptr(); } 56 | 57 | private: 58 | object value; 59 | }; 60 | 61 | PYBIND11_NAMESPACE_END(detail) 62 | PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) 63 | -------------------------------------------------------------------------------- /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/__main__.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=missing-function-docstring 2 | from __future__ import annotations 3 | 4 | import argparse 5 | import re 6 | import sys 7 | import sysconfig 8 | 9 | from ._version import __version__ 10 | from .commands import get_cmake_dir, get_include, get_pkgconfig_dir 11 | 12 | # This is the conditional used for os.path being posixpath 13 | if "posix" in sys.builtin_module_names: 14 | from shlex import quote 15 | elif "nt" in sys.builtin_module_names: 16 | # See https://github.com/mesonbuild/meson/blob/db22551ed9d2dd7889abea01cc1c7bba02bf1c75/mesonbuild/utils/universal.py#L1092-L1121 17 | # and the original documents: 18 | # https://docs.microsoft.com/en-us/cpp/c-language/parsing-c-command-line-arguments and 19 | # https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/ 20 | UNSAFE = re.compile("[ \t\n\r]") 21 | 22 | def quote(s: str) -> str: 23 | if s and not UNSAFE.search(s): 24 | return s 25 | 26 | # Paths cannot contain a '"' on Windows, so we don't need to worry 27 | # about nuanced counting here. 28 | return f'"{s}\\"' if s.endswith("\\") else f'"{s}"' 29 | else: 30 | 31 | def quote(s: str) -> str: 32 | return s 33 | 34 | 35 | def print_includes() -> None: 36 | dirs = [ 37 | sysconfig.get_path("include"), 38 | sysconfig.get_path("platinclude"), 39 | get_include(), 40 | ] 41 | 42 | # Make unique but preserve order 43 | unique_dirs = [] 44 | for d in dirs: 45 | if d and d not in unique_dirs: 46 | unique_dirs.append(d) 47 | 48 | print(" ".join(quote(f"-I{d}") for d in unique_dirs)) 49 | 50 | 51 | def main() -> None: 52 | parser = argparse.ArgumentParser() 53 | parser.add_argument( 54 | "--version", 55 | action="version", 56 | version=__version__, 57 | help="Print the version and exit.", 58 | ) 59 | parser.add_argument( 60 | "--includes", 61 | action="store_true", 62 | help="Include flags for both pybind11 and Python headers.", 63 | ) 64 | parser.add_argument( 65 | "--cmakedir", 66 | action="store_true", 67 | help="Print the CMake module directory, ideal for setting -Dpybind11_ROOT in CMake.", 68 | ) 69 | parser.add_argument( 70 | "--pkgconfigdir", 71 | action="store_true", 72 | help="Print the pkgconfig directory, ideal for setting $PKG_CONFIG_PATH.", 73 | ) 74 | args = parser.parse_args() 75 | if not sys.argv[1:]: 76 | parser.print_help() 77 | if args.includes: 78 | print_includes() 79 | if args.cmakedir: 80 | print(quote(get_cmake_dir())) 81 | if args.pkgconfigdir: 82 | print(quote(get_pkgconfig_dir())) 83 | 84 | 85 | if __name__ == "__main__": 86 | main() 87 | -------------------------------------------------------------------------------- /pybind11/_version.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | 4 | def _to_int(s: str) -> int | str: 5 | try: 6 | return int(s) 7 | except ValueError: 8 | return s 9 | 10 | 11 | __version__ = "3.0.0.dev1" 12 | version_info = tuple(_to_int(s) for s in __version__.split(".")) 13 | -------------------------------------------------------------------------------- /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/google/pybind11clif/4841661df5daf26ecdedaace54e64d0782e63f64/pybind11/py.typed -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools>=42", "cmake>=3.18", "ninja"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | 6 | [tool.check-manifest] 7 | ignore = [ 8 | "tests/**", 9 | "docs/**", 10 | "tools/**", 11 | "include/**", 12 | ".*", 13 | "pybind11/include/**", 14 | "pybind11/share/**", 15 | "CMakeLists.txt", 16 | "noxfile.py", 17 | ] 18 | 19 | 20 | [tool.mypy] 21 | files = ["pybind11"] 22 | python_version = "3.8" 23 | strict = true 24 | enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"] 25 | warn_unreachable = true 26 | 27 | [[tool.mypy.overrides]] 28 | module = ["ghapi.*"] 29 | ignore_missing_imports = true 30 | 31 | 32 | [tool.pylint] 33 | master.py-version = "3.8" 34 | reports.output-format = "colorized" 35 | messages_control.disable = [ 36 | "design", 37 | "fixme", 38 | "imports", 39 | "line-too-long", 40 | "imports", 41 | "invalid-name", 42 | "protected-access", 43 | "missing-module-docstring", 44 | "unused-argument", # covered by Ruff ARG 45 | ] 46 | 47 | [tool.ruff] 48 | target-version = "py38" 49 | src = ["src"] 50 | 51 | [tool.ruff.lint] 52 | extend-select = [ 53 | "B", # flake8-bugbear 54 | "I", # isort 55 | "N", # pep8-naming 56 | "ARG", # flake8-unused-arguments 57 | "C4", # flake8-comprehensions 58 | "EM", # flake8-errmsg 59 | "ICN", # flake8-import-conventions 60 | "PGH", # pygrep-hooks 61 | "PIE", # flake8-pie 62 | "PL", # pylint 63 | "PT", # flake8-pytest-style 64 | "RET", # flake8-return 65 | "RUF100", # Ruff-specific 66 | "SIM", # flake8-simplify 67 | "UP", # pyupgrade 68 | "YTT", # flake8-2020 69 | ] 70 | ignore = [ 71 | "PLR", # Design related pylint 72 | "E501", # Line too long (Black is enough) 73 | "PT011", # Too broad with raises in pytest 74 | "PT004", # Fixture that doesn't return needs underscore (no, it is fine) 75 | "SIM118", # iter(x) is not always the same as iter(x.keys()) 76 | ] 77 | unfixable = ["T20"] 78 | isort.known-first-party = ["env", "pybind11_cross_module_tests", "pybind11_tests"] 79 | isort.required-imports = ["from __future__ import annotations"] 80 | 81 | 82 | [tool.ruff.lint.per-file-ignores] 83 | "tests/**" = ["EM", "N", "E721"] 84 | "tests/test_call_policies.py" = ["PLC1901"] 85 | 86 | [tool.repo-review] 87 | ignore = ["PP"] 88 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | long_description = file: README.rst 3 | long_description_content_type = text/x-rst 4 | description = Seamless operability between C++11 and Python 5 | author = Wenzel Jakob 6 | author_email = wenzel.jakob@epfl.ch 7 | url = https://github.com/pybind/pybind11 8 | license = BSD 9 | 10 | classifiers = 11 | Development Status :: 5 - Production/Stable 12 | Intended Audience :: Developers 13 | Topic :: Software Development :: Libraries :: Python Modules 14 | Topic :: Utilities 15 | Programming Language :: C++ 16 | Programming Language :: Python :: 3 :: Only 17 | Programming Language :: Python :: 3.8 18 | Programming Language :: Python :: 3.9 19 | Programming Language :: Python :: 3.10 20 | Programming Language :: Python :: 3.11 21 | Programming Language :: Python :: 3.12 22 | Programming Language :: Python :: 3.13 23 | License :: OSI Approved :: BSD License 24 | Programming Language :: Python :: Implementation :: PyPy 25 | Programming Language :: Python :: Implementation :: CPython 26 | Programming Language :: C++ 27 | Topic :: Software Development :: Libraries :: Python Modules 28 | 29 | keywords = 30 | C++11 31 | Python bindings 32 | 33 | project_urls = 34 | Documentation = https://pybind11.readthedocs.io/ 35 | Bug Tracker = https://github.com/pybind/pybind11/issues 36 | Discussions = https://github.com/pybind/pybind11/discussions 37 | Changelog = https://pybind11.readthedocs.io/en/latest/changelog.html 38 | Chat = https://gitter.im/pybind/Lobby 39 | 40 | [options] 41 | python_requires = >=3.8 42 | zip_safe = False 43 | -------------------------------------------------------------------------------- /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/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 | PY_GIL_DISABLED = bool(sysconfig.get_config_var("Py_GIL_DISABLED")) 16 | 17 | 18 | def deprecated_call(): 19 | """ 20 | pytest.deprecated_call() seems broken in pytest<3.9.x; concretely, it 21 | doesn't work on CPython 3.8.0 with pytest==3.3.2 on Ubuntu 18.04 (#2922). 22 | 23 | This is a narrowed reimplementation of the following PR :( 24 | https://github.com/pytest-dev/pytest/pull/4104 25 | """ 26 | # TODO: Remove this when testing requires pytest>=3.9. 27 | pieces = pytest.__version__.split(".") 28 | pytest_major_minor = (int(pieces[0]), int(pieces[1])) 29 | if pytest_major_minor < (3, 9): 30 | return pytest.warns((DeprecationWarning, PendingDeprecationWarning)) 31 | return pytest.deprecated_call() 32 | -------------------------------------------------------------------------------- /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 | PYBIND11_MODULE(exo_planet_pybind11, m) { pybind11_tests::test_cpp_conduit::wrap_traveler(m); } 11 | -------------------------------------------------------------------------------- /tests/extra_python_package/pytest.ini: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/pybind11clif/4841661df5daf26ecdedaace54e64d0782e63f64/tests/extra_python_package/pytest.ini -------------------------------------------------------------------------------- /tests/extra_setuptools/pytest.ini: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/pybind11clif/4841661df5daf26ecdedaace54e64d0782e63f64/tests/extra_setuptools/pytest.ini -------------------------------------------------------------------------------- /tests/namespace_visibility.inl: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 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 8 | 9 | #ifdef __GNUG__ 10 | # define PYBIND11_NS_VIS_U /* unspecified */ 11 | # define PYBIND11_NS_VIS_H __attribute__((visibility("hidden"))) 12 | #else 13 | # define PYBIND11_NS_VIS_U 14 | # define PYBIND11_NS_VIS_H 15 | #endif 16 | 17 | #define PYBIND11_NS_VIS_FUNC \ 18 | inline std::ptrdiff_t func() { \ 19 | static std::ptrdiff_t value = 0; \ 20 | return reinterpret_cast(&value); \ 21 | } 22 | 23 | #define PYBIND11_NS_VIS_DEFS \ 24 | m.def("ns_vis_uuu_func", pybind11_ns_vis_uuu::func); \ 25 | m.def("ns_vis_uuh_func", pybind11_ns_vis_uuh::func); \ 26 | m.def("ns_vis_uhu_func", pybind11_ns_vis_uhu::func); \ 27 | m.def("ns_vis_uhh_func", pybind11_ns_vis_uhh::func); \ 28 | m.def("ns_vis_huu_func", pybind11_ns_vis_huu::func); \ 29 | m.def("ns_vis_huh_func", pybind11_ns_vis_huh::func); \ 30 | m.def("ns_vis_hhu_func", pybind11_ns_vis_hhu::func); \ 31 | m.def("ns_vis_hhh_func", pybind11_ns_vis_hhh::func); 32 | -------------------------------------------------------------------------------- /tests/namespace_visibility_1.cpp: -------------------------------------------------------------------------------- 1 | #include "pybind11/pybind11.h" 2 | #include "namespace_visibility.inl" 3 | 4 | // clang-format off 5 | namespace pybind11_ns_vis_uuu PYBIND11_NS_VIS_U { PYBIND11_NS_VIS_FUNC } 6 | namespace pybind11_ns_vis_uuh PYBIND11_NS_VIS_U { PYBIND11_NS_VIS_FUNC } 7 | namespace pybind11_ns_vis_uhu PYBIND11_NS_VIS_U { PYBIND11_NS_VIS_FUNC } 8 | namespace pybind11_ns_vis_uhh PYBIND11_NS_VIS_U { PYBIND11_NS_VIS_FUNC } 9 | namespace pybind11_ns_vis_huu PYBIND11_NS_VIS_H { PYBIND11_NS_VIS_FUNC } 10 | namespace pybind11_ns_vis_huh PYBIND11_NS_VIS_H { PYBIND11_NS_VIS_FUNC } 11 | namespace pybind11_ns_vis_hhu PYBIND11_NS_VIS_H { PYBIND11_NS_VIS_FUNC } 12 | namespace pybind11_ns_vis_hhh PYBIND11_NS_VIS_H { PYBIND11_NS_VIS_FUNC } 13 | // ^ ^ 14 | // bit used .............. here 15 | // clang-format on 16 | 17 | void namespace_visibility_1s(pybind11::module_ &m); 18 | 19 | PYBIND11_MODULE(namespace_visibility_1, m) { 20 | PYBIND11_NS_VIS_DEFS 21 | 22 | auto sm = m.def_submodule("submodule"); 23 | namespace_visibility_1s(sm); 24 | } 25 | -------------------------------------------------------------------------------- /tests/namespace_visibility_1s.cpp: -------------------------------------------------------------------------------- 1 | #include "pybind11/pybind11.h" 2 | #include "namespace_visibility.inl" 3 | 4 | // clang-format off 5 | namespace pybind11_ns_vis_uuu PYBIND11_NS_VIS_U { PYBIND11_NS_VIS_FUNC } 6 | namespace pybind11_ns_vis_uuh PYBIND11_NS_VIS_U { PYBIND11_NS_VIS_FUNC } 7 | namespace pybind11_ns_vis_uhu PYBIND11_NS_VIS_H { PYBIND11_NS_VIS_FUNC } 8 | namespace pybind11_ns_vis_uhh PYBIND11_NS_VIS_H { PYBIND11_NS_VIS_FUNC } 9 | namespace pybind11_ns_vis_huu PYBIND11_NS_VIS_U { PYBIND11_NS_VIS_FUNC } 10 | namespace pybind11_ns_vis_huh PYBIND11_NS_VIS_U { PYBIND11_NS_VIS_FUNC } 11 | namespace pybind11_ns_vis_hhu PYBIND11_NS_VIS_H { PYBIND11_NS_VIS_FUNC } 12 | namespace pybind11_ns_vis_hhh PYBIND11_NS_VIS_H { PYBIND11_NS_VIS_FUNC } 13 | // ^ ^ 14 | // bit used ............. here 15 | // clang-format on 16 | 17 | void namespace_visibility_1s(pybind11::module_ &m) { PYBIND11_NS_VIS_DEFS } 18 | -------------------------------------------------------------------------------- /tests/namespace_visibility_2.cpp: -------------------------------------------------------------------------------- 1 | #include "pybind11/pybind11.h" 2 | #include "namespace_visibility.inl" 3 | 4 | // clang-format off 5 | namespace pybind11_ns_vis_uuu PYBIND11_NS_VIS_U { PYBIND11_NS_VIS_FUNC } 6 | namespace pybind11_ns_vis_uuh PYBIND11_NS_VIS_H { PYBIND11_NS_VIS_FUNC } 7 | namespace pybind11_ns_vis_uhu PYBIND11_NS_VIS_U { PYBIND11_NS_VIS_FUNC } 8 | namespace pybind11_ns_vis_uhh PYBIND11_NS_VIS_H { PYBIND11_NS_VIS_FUNC } 9 | namespace pybind11_ns_vis_huu PYBIND11_NS_VIS_U { PYBIND11_NS_VIS_FUNC } 10 | namespace pybind11_ns_vis_huh PYBIND11_NS_VIS_H { PYBIND11_NS_VIS_FUNC } 11 | namespace pybind11_ns_vis_hhu PYBIND11_NS_VIS_U { PYBIND11_NS_VIS_FUNC } 12 | namespace pybind11_ns_vis_hhh PYBIND11_NS_VIS_H { PYBIND11_NS_VIS_FUNC } 13 | // ^ ^ 14 | // bit used ............ here 15 | // clang-format on 16 | 17 | PYBIND11_MODULE(namespace_visibility_2, m) { PYBIND11_NS_VIS_DEFS } 18 | -------------------------------------------------------------------------------- /tests/pure_cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(Catch 2.13.2) 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 | namespace pybindit { 10 | namespace memory { 11 | namespace smart_holder_poc { // Proof-of-Concept implementations. 12 | 13 | template 14 | T &as_lvalue_ref(const smart_holder &hld) { 15 | static const char *context = "as_lvalue_ref"; 16 | hld.ensure_is_populated(context); 17 | hld.ensure_has_pointee(context); 18 | return *hld.as_raw_ptr_unowned(); 19 | } 20 | 21 | template 22 | T &&as_rvalue_ref(const smart_holder &hld) { 23 | static const char *context = "as_rvalue_ref"; 24 | hld.ensure_is_populated(context); 25 | hld.ensure_has_pointee(context); 26 | return std::move(*hld.as_raw_ptr_unowned()); 27 | } 28 | 29 | template 30 | T *as_raw_ptr_release_ownership(smart_holder &hld, 31 | const char *context = "as_raw_ptr_release_ownership") { 32 | hld.ensure_can_release_ownership(context); 33 | T *raw_ptr = hld.as_raw_ptr_unowned(); 34 | hld.release_ownership(); 35 | return raw_ptr; 36 | } 37 | 38 | template > 39 | std::unique_ptr as_unique_ptr(smart_holder &hld) { 40 | static const char *context = "as_unique_ptr"; 41 | hld.ensure_compatible_rtti_uqp_del(context); 42 | hld.ensure_use_count_1(context); 43 | T *raw_ptr = hld.as_raw_ptr_unowned(); 44 | hld.release_ownership(); 45 | // KNOWN DEFECT (see PR #4850): Does not copy the deleter. 46 | return std::unique_ptr(raw_ptr); 47 | } 48 | 49 | } // namespace smart_holder_poc 50 | } // namespace memory 51 | } // namespace pybindit 52 | -------------------------------------------------------------------------------- /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", "numpy", "scipy"] 12 | 13 | [tool.scikit-build.cmake.define] 14 | PYBIND11_FINDPYTHON = true 15 | 16 | [tool.cibuildwheel] 17 | test-command = "pytest -o timeout=0 -p no:cacheprovider {project}/tests/test_*.py" 18 | -------------------------------------------------------------------------------- /tests/pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | minversion = 3.10 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 | default:The global interpreter lock:RuntimeWarning 24 | -------------------------------------------------------------------------------- /tests/requirements.txt: -------------------------------------------------------------------------------- 1 | --only-binary=:all: 2 | build~=1.0; python_version>="3.8" 3 | numpy~=1.23.0; python_version=="3.8" and platform_python_implementation=="PyPy" 4 | numpy~=1.25.0; python_version=="3.9" and platform_python_implementation=='PyPy' 5 | numpy~=1.21.5; platform_python_implementation!="PyPy" and python_version>="3.8" and python_version<"3.10" 6 | numpy~=1.22.2; platform_python_implementation!="PyPy" and python_version=="3.10" 7 | numpy~=1.26.0; platform_python_implementation!="PyPy" and python_version>="3.11" and python_version<"3.13" 8 | pytest~=7.0 9 | pytest-timeout 10 | scipy~=1.5.4; platform_python_implementation!="PyPy" and python_version<"3.10" 11 | scipy~=1.8.0; platform_python_implementation!="PyPy" and python_version=="3.10" and sys_platform!='win32' 12 | scipy~=1.11.1; platform_python_implementation!="PyPy" and python_version>="3.11" and python_version<"3.13" and sys_platform!='win32' 13 | -------------------------------------------------------------------------------- /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 | RegistryType &PyGILState_Check_Results() { 14 | static auto *singleton = new RegistryType(); 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 2 | 3 | #include "pybind11_tests.h" 4 | 5 | #include 6 | 7 | namespace pybind11_tests { 8 | namespace class_sh_disowning { 9 | 10 | template // Using int as a trick to easily generate a series of types. 11 | struct Atype { 12 | int val = 0; 13 | explicit Atype(int val_) : val{val_} {} 14 | int get() const { return val * 10 + SerNo; } 15 | }; 16 | 17 | int same_twice(std::unique_ptr> at1a, std::unique_ptr> at1b) { 18 | return at1a->get() * 100 + at1b->get() * 10; 19 | } 20 | 21 | int mixed(std::unique_ptr> at1, std::unique_ptr> at2) { 22 | return at1->get() * 200 + at2->get() * 20; 23 | } 24 | 25 | int overloaded(std::unique_ptr> at1, int i) { return at1->get() * 30 + i; } 26 | int overloaded(std::unique_ptr> at2, int i) { return at2->get() * 40 + i; } 27 | 28 | } // namespace class_sh_disowning 29 | } // namespace pybind11_tests 30 | 31 | PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_disowning::Atype<1>) 32 | PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_disowning::Atype<2>) 33 | 34 | TEST_SUBMODULE(class_sh_disowning, m) { 35 | m.attr("defined_PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT") = 36 | #ifndef PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT 37 | false; 38 | #else 39 | true; 40 | 41 | using namespace pybind11_tests::class_sh_disowning; 42 | 43 | py::classh>(m, "Atype1").def(py::init()).def("get", &Atype<1>::get); 44 | py::classh>(m, "Atype2").def(py::init()).def("get", &Atype<2>::get); 45 | 46 | m.def("same_twice", same_twice); 47 | 48 | m.def("mixed", mixed); 49 | 50 | m.def("overloaded", (int (*)(std::unique_ptr>, int)) & overloaded); 51 | m.def("overloaded", (int (*)(std::unique_ptr>, int)) & overloaded); 52 | #endif // PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT 53 | } 54 | -------------------------------------------------------------------------------- /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 | if not m.defined_PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT: 8 | pytest.skip("smart_holder not available.", allow_module_level=True) 9 | 10 | 11 | def test_atyp_factories(): 12 | assert m.atyp_valu().get_mtxt() == "Valu" 13 | assert m.atyp_rref().get_mtxt() == "Rref" 14 | # sert m.atyp_cref().get_mtxt() == "Cref" 15 | # sert m.atyp_mref().get_mtxt() == "Mref" 16 | # sert m.atyp_cptr().get_mtxt() == "Cptr" 17 | assert m.atyp_mptr().get_mtxt() == "Mptr" 18 | assert m.atyp_shmp().get_mtxt() == "Shmp" 19 | # sert m.atyp_shcp().get_mtxt() == "Shcp" 20 | assert m.atyp_uqmp().get_mtxt() == "Uqmp" 21 | # sert m.atyp_uqcp().get_mtxt() == "Uqcp" 22 | assert m.atyp_udmp().get_mtxt() == "Udmp" 23 | # sert m.atyp_udcp().get_mtxt() == "Udcp" 24 | 25 | 26 | @pytest.mark.parametrize( 27 | ("init_args", "expected"), 28 | [ 29 | ((3,), 300), 30 | ((5, 7), 570), 31 | ((9, 11, 13), 1023), 32 | ], 33 | ) 34 | def test_with_alias_success(init_args, expected): 35 | assert m.with_alias(*init_args).val == expected 36 | 37 | 38 | @pytest.mark.parametrize( 39 | ("num_init_args", "smart_ptr"), 40 | [ 41 | (4, "std::unique_ptr"), 42 | (5, "std::shared_ptr"), 43 | ], 44 | ) 45 | def test_with_alias_invalid(num_init_args, smart_ptr): 46 | class PyDrvdWithAlias(m.with_alias): 47 | pass 48 | 49 | with pytest.raises(TypeError) as excinfo: 50 | PyDrvdWithAlias(*((0,) * num_init_args)) 51 | assert ( 52 | str(excinfo.value) 53 | == "pybind11::init(): construction failed: returned " 54 | + smart_ptr 55 | + " pointee is not an alias instance" 56 | ) 57 | -------------------------------------------------------------------------------- /tests/test_class_sh_inheritance.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pytest 4 | 5 | from pybind11_tests import class_sh_inheritance as m 6 | 7 | if not m.defined_PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT: 8 | pytest.skip("smart_holder not available.", allow_module_level=True) 9 | 10 | 11 | def test_rtrn_mptr_drvd_pass_cptr_base(): 12 | d = m.rtrn_mptr_drvd() 13 | i = m.pass_cptr_base(d) # load_impl Case 2a 14 | assert i == 2 * 100 + 11 15 | 16 | 17 | def test_rtrn_shmp_drvd_pass_shcp_base(): 18 | d = m.rtrn_shmp_drvd() 19 | i = m.pass_shcp_base(d) # load_impl Case 2a 20 | assert i == 2 * 100 + 21 21 | 22 | 23 | def test_rtrn_mptr_drvd_up_cast_pass_cptr_drvd(): 24 | b = m.rtrn_mptr_drvd_up_cast() 25 | # the base return is down-cast immediately. 26 | assert b.__class__.__name__ == "drvd" 27 | i = m.pass_cptr_drvd(b) 28 | assert i == 2 * 100 + 12 29 | 30 | 31 | def test_rtrn_shmp_drvd_up_cast_pass_shcp_drvd(): 32 | b = m.rtrn_shmp_drvd_up_cast() 33 | # the base return is down-cast immediately. 34 | assert b.__class__.__name__ == "drvd" 35 | i = m.pass_shcp_drvd(b) 36 | assert i == 2 * 100 + 22 37 | 38 | 39 | def test_rtrn_mptr_drvd2_pass_cptr_bases(): 40 | d = m.rtrn_mptr_drvd2() 41 | i1 = m.pass_cptr_base1(d) # load_impl Case 2c 42 | assert i1 == 3 * 110 + 4 * 120 + 21 43 | i2 = m.pass_cptr_base2(d) 44 | assert i2 == 3 * 110 + 4 * 120 + 22 45 | 46 | 47 | def test_rtrn_mptr_drvd2_up_casts_pass_cptr_drvd2(): 48 | b1 = m.rtrn_mptr_drvd2_up_cast1() 49 | assert b1.__class__.__name__ == "drvd2" 50 | i1 = m.pass_cptr_drvd2(b1) 51 | assert i1 == 3 * 110 + 4 * 120 + 23 52 | b2 = m.rtrn_mptr_drvd2_up_cast2() 53 | assert b2.__class__.__name__ == "drvd2" 54 | i2 = m.pass_cptr_drvd2(b2) 55 | assert i2 == 3 * 110 + 4 * 120 + 23 56 | 57 | 58 | def test_python_drvd2(): 59 | class Drvd2(m.base1, m.base2): 60 | def __init__(self): 61 | m.base1.__init__(self) 62 | m.base2.__init__(self) 63 | 64 | d = Drvd2() 65 | i1 = m.pass_cptr_base1(d) # load_impl Case 2b 66 | assert i1 == 110 + 21 67 | i2 = m.pass_cptr_base2(d) 68 | assert i2 == 120 + 22 69 | -------------------------------------------------------------------------------- /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 | if not m.defined_PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT: 8 | pytest.skip("smart_holder not available.", allow_module_level=True) 9 | 10 | 11 | def test_ptrdiff_drvd_base0(): 12 | ptrdiff = m.ptrdiff_drvd_base0() 13 | # A failure here does not (necessarily) mean that there is a bug, but that 14 | # test_class_sh_mi_thunks is not exercising what it is supposed to. 15 | # If this ever fails on some platforms: use pytest.skip() 16 | # If this ever fails on all platforms: don't know, seems extremely unlikely. 17 | assert ptrdiff != 0 18 | 19 | 20 | @pytest.mark.parametrize( 21 | "vec_size_fn", 22 | [ 23 | m.vec_size_base0_raw_ptr, 24 | m.vec_size_base0_shared_ptr, 25 | ], 26 | ) 27 | @pytest.mark.parametrize( 28 | "get_fn", 29 | [ 30 | m.get_drvd_as_base0_raw_ptr, 31 | m.get_drvd_as_base0_shared_ptr, 32 | m.get_drvd_as_base0_unique_ptr, 33 | ], 34 | ) 35 | def test_get_vec_size_raw_shared(get_fn, vec_size_fn): 36 | obj = get_fn() 37 | assert vec_size_fn(obj) == 5 38 | 39 | 40 | @pytest.mark.parametrize( 41 | "get_fn", [m.get_drvd_as_base0_raw_ptr, m.get_drvd_as_base0_unique_ptr] 42 | ) 43 | def test_get_vec_size_unique(get_fn): 44 | obj = get_fn() 45 | assert m.vec_size_base0_unique_ptr(obj) == 5 46 | with pytest.raises(ValueError, match="Python instance was disowned"): 47 | m.vec_size_base0_unique_ptr(obj) 48 | 49 | 50 | def test_get_shared_vec_size_unique(): 51 | obj = m.get_drvd_as_base0_shared_ptr() 52 | with pytest.raises(ValueError) as exc_info: 53 | m.vec_size_base0_unique_ptr(obj) 54 | assert ( 55 | str(exc_info.value) == "Cannot disown external shared_ptr (load_as_unique_ptr)." 56 | ) 57 | -------------------------------------------------------------------------------- /tests/test_class_sh_property_non_owning.cpp: -------------------------------------------------------------------------------- 1 | #include "pybind11/smart_holder.h" 2 | #include "pybind11_tests.h" 3 | 4 | #include 5 | #include 6 | 7 | namespace test_class_sh_property_non_owning { 8 | 9 | struct CoreField { 10 | explicit CoreField(int int_value = -99) : int_value{int_value} {} 11 | int int_value; 12 | }; 13 | 14 | struct DataField { 15 | DataField(int i_value, int i_shared, int i_unique) 16 | : core_fld_value{i_value}, core_fld_shared_ptr{new CoreField{i_shared}}, 17 | core_fld_raw_ptr{core_fld_shared_ptr.get()}, 18 | core_fld_unique_ptr{new CoreField{i_unique}} {} 19 | CoreField core_fld_value; 20 | std::shared_ptr core_fld_shared_ptr; 21 | CoreField *core_fld_raw_ptr; 22 | std::unique_ptr core_fld_unique_ptr; 23 | }; 24 | 25 | struct DataFieldsHolder { 26 | private: 27 | std::vector vec; 28 | 29 | public: 30 | explicit DataFieldsHolder(std::size_t vec_size) { 31 | for (std::size_t i = 0; i < vec_size; i++) { 32 | int i11 = static_cast(i) * 11; 33 | vec.emplace_back(13 + i11, 14 + i11, 15 + i11); 34 | } 35 | } 36 | 37 | DataField *vec_at(std::size_t index) { 38 | if (index >= vec.size()) { 39 | return nullptr; 40 | } 41 | return &vec[index]; 42 | } 43 | }; 44 | 45 | } // namespace test_class_sh_property_non_owning 46 | 47 | using namespace test_class_sh_property_non_owning; 48 | 49 | PYBIND11_SMART_HOLDER_TYPE_CASTERS(CoreField) 50 | PYBIND11_SMART_HOLDER_TYPE_CASTERS(DataField) 51 | PYBIND11_SMART_HOLDER_TYPE_CASTERS(DataFieldsHolder) 52 | 53 | TEST_SUBMODULE(class_sh_property_non_owning, m) { 54 | m.attr("defined_PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT") = 55 | #ifndef PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT 56 | false; 57 | #else 58 | true; 59 | 60 | py::classh(m, "CoreField").def_readwrite("int_value", &CoreField::int_value); 61 | 62 | py::classh(m, "DataField") 63 | .def_readonly("core_fld_value_ro", &DataField::core_fld_value) 64 | .def_readwrite("core_fld_value_rw", &DataField::core_fld_value) 65 | .def_readonly("core_fld_shared_ptr_ro", &DataField::core_fld_shared_ptr) 66 | .def_readwrite("core_fld_shared_ptr_rw", &DataField::core_fld_shared_ptr) 67 | .def_readonly("core_fld_raw_ptr_ro", &DataField::core_fld_raw_ptr) 68 | .def_readwrite("core_fld_raw_ptr_rw", &DataField::core_fld_raw_ptr) 69 | .def_readwrite("core_fld_unique_ptr_rw", &DataField::core_fld_unique_ptr); 70 | 71 | py::classh(m, "DataFieldsHolder") 72 | .def(py::init()) 73 | .def("vec_at", &DataFieldsHolder::vec_at, py::return_value_policy::reference_internal); 74 | #endif // PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT 75 | } 76 | -------------------------------------------------------------------------------- /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 | if not m.defined_PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT: 8 | pytest.skip("smart_holder not available.", allow_module_level=True) 9 | 10 | 11 | @pytest.mark.parametrize("persistent_holder", [True, False]) 12 | @pytest.mark.parametrize( 13 | ("core_fld", "expected"), 14 | [ 15 | ("core_fld_value_ro", (13, 24)), 16 | ("core_fld_value_rw", (13, 24)), 17 | ("core_fld_shared_ptr_ro", (14, 25)), 18 | ("core_fld_shared_ptr_rw", (14, 25)), 19 | ("core_fld_raw_ptr_ro", (14, 25)), 20 | ("core_fld_raw_ptr_rw", (14, 25)), 21 | ("core_fld_unique_ptr_rw", (15, 26)), 22 | ], 23 | ) 24 | def test_core_fld_common(core_fld, expected, persistent_holder): 25 | if persistent_holder: 26 | h = m.DataFieldsHolder(2) 27 | for i, exp in enumerate(expected): 28 | c = getattr(h.vec_at(i), core_fld) 29 | assert c.int_value == exp 30 | else: 31 | for i, exp in enumerate(expected): 32 | c = getattr(m.DataFieldsHolder(2).vec_at(i), core_fld) 33 | assert c.int_value == exp 34 | -------------------------------------------------------------------------------- /tests/test_class_sh_property_stl.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "pybind11_tests.h" 5 | 6 | #include 7 | #include 8 | 9 | namespace test_class_sh_property_stl { 10 | 11 | struct Field { 12 | explicit Field(int wrapped_int) : wrapped_int{wrapped_int} {} 13 | int wrapped_int = 100; 14 | }; 15 | 16 | struct FieldHolder { 17 | explicit FieldHolder(const Field &fld) : fld{fld} {} 18 | Field fld = Field{200}; 19 | }; 20 | 21 | struct VectorFieldHolder { 22 | std::vector vec_fld_hld; 23 | VectorFieldHolder() { vec_fld_hld.emplace_back(Field{300}); } 24 | void reset_at(std::size_t index, int wrapped_int) { 25 | if (index < vec_fld_hld.size()) { 26 | vec_fld_hld[index].fld.wrapped_int = wrapped_int; 27 | } 28 | } 29 | }; 30 | 31 | } // namespace test_class_sh_property_stl 32 | 33 | using namespace test_class_sh_property_stl; 34 | 35 | PYBIND11_SMART_HOLDER_TYPE_CASTERS(Field) 36 | PYBIND11_SMART_HOLDER_TYPE_CASTERS(FieldHolder) 37 | PYBIND11_SMART_HOLDER_TYPE_CASTERS(VectorFieldHolder) 38 | 39 | TEST_SUBMODULE(class_sh_property_stl, m) { 40 | py::classh(m, "Field").def_readwrite("wrapped_int", &Field::wrapped_int); 41 | 42 | py::classh(m, "FieldHolder").def_readwrite("fld", &FieldHolder::fld); 43 | 44 | py::classh(m, "VectorFieldHolder") 45 | .def(py::init<>()) 46 | .def("reset_at", &VectorFieldHolder::reset_at) 47 | .def_readwrite("vec_fld_hld_ref", &VectorFieldHolder::vec_fld_hld) 48 | .def_readwrite("vec_fld_hld_cpy", 49 | &VectorFieldHolder::vec_fld_hld, 50 | py::return_value_policy::_clif_automatic); 51 | } 52 | -------------------------------------------------------------------------------- /tests/test_class_sh_property_stl.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from pybind11_tests import class_sh_property_stl as m 4 | 5 | 6 | def test_cpy_after_ref(): 7 | h = m.VectorFieldHolder() 8 | c1 = h.vec_fld_hld_cpy 9 | c2 = h.vec_fld_hld_cpy 10 | assert repr(c2) != repr(c1) 11 | r1 = h.vec_fld_hld_ref 12 | assert repr(r1) != repr(c2) 13 | assert repr(r1) != repr(c1) 14 | r2 = h.vec_fld_hld_ref 15 | assert repr(r2) == repr(r1) 16 | c3 = h.vec_fld_hld_cpy 17 | assert repr(c3) == repr(r1) # SURPRISE! 18 | 19 | 20 | def test_persistent_holder(): 21 | h = m.VectorFieldHolder() 22 | c0 = h.vec_fld_hld_cpy[0] # Must be first. See test_cpy_after_ref(). 23 | r0 = h.vec_fld_hld_ref[0] # Must be second. 24 | assert c0.fld.wrapped_int == 300 25 | assert r0.fld.wrapped_int == 300 26 | h.reset_at(0, 400) 27 | assert c0.fld.wrapped_int == 300 28 | assert r0.fld.wrapped_int == 400 29 | 30 | 31 | def test_temporary_holder_keep_alive(): 32 | r0 = m.VectorFieldHolder().vec_fld_hld_ref[0] 33 | assert r0.fld.wrapped_int == 300 34 | -------------------------------------------------------------------------------- /tests/test_class_sh_shared_ptr_copy_move.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pytest 4 | 5 | from pybind11_tests import class_sh_shared_ptr_copy_move as m 6 | 7 | if not m.defined_PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT: 8 | pytest.skip("smart_holder not available.", allow_module_level=True) 9 | 10 | 11 | def test_shptr_copy(): 12 | txt = m.test_ShPtr_copy()[0].get_history() 13 | assert txt == "FooShPtr_copy" 14 | 15 | 16 | def test_smhld_copy(): 17 | txt = m.test_SmHld_copy()[0].get_history() 18 | assert txt == "FooSmHld_copy" 19 | 20 | 21 | def test_shptr_move(): 22 | txt = m.test_ShPtr_move()[0].get_history() 23 | assert txt == "FooShPtr_move" 24 | 25 | 26 | def test_smhld_move(): 27 | txt = m.test_SmHld_move()[0].get_history() 28 | assert txt == "FooSmHld_move" 29 | 30 | 31 | def _check_property(foo_typ, prop_typ, policy): 32 | o = m.Outer() 33 | name = f"{foo_typ}_{prop_typ}_{policy}" 34 | history = f"Foo{foo_typ}_Outer" 35 | f = getattr(o, name) 36 | assert f.get_history() == history 37 | # and try again to check that o did not get changed 38 | f = getattr(o, name) 39 | assert f.get_history() == history 40 | 41 | 42 | def test_properties(): 43 | for prop_typ in ("readonly", "readwrite", "property_readonly"): 44 | for foo_typ in ("ShPtr", "SmHld"): 45 | for policy in ("default", "copy", "move"): 46 | _check_property(foo_typ, prop_typ, policy) 47 | -------------------------------------------------------------------------------- /tests/test_class_sh_trampoline_basic.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pytest 4 | 5 | from pybind11_tests import class_sh_trampoline_basic as m 6 | 7 | if not m.defined_PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT: 8 | pytest.skip("smart_holder not available.", allow_module_level=True) 9 | 10 | 11 | class PyDrvd0(m.Abase0): 12 | def __init__(self, val): 13 | super().__init__(val) 14 | 15 | def Add(self, other_val): 16 | return self.Get() * 100 + other_val 17 | 18 | 19 | class PyDrvd1(m.Abase1): 20 | def __init__(self, val): 21 | super().__init__(val) 22 | 23 | def Add(self, other_val): 24 | return self.Get() * 200 + other_val 25 | 26 | 27 | def test_drvd0_add(): 28 | drvd = PyDrvd0(74) 29 | assert drvd.Add(38) == (74 * 10 + 3) * 100 + 38 30 | 31 | 32 | def test_drvd0_add_in_cpp_raw_ptr(): 33 | drvd = PyDrvd0(52) 34 | assert m.AddInCppRawPtr(drvd, 27) == ((52 * 10 + 3) * 100 + 27) * 10 + 7 35 | 36 | 37 | def test_drvd0_add_in_cpp_shared_ptr(): 38 | while True: 39 | drvd = PyDrvd0(36) 40 | assert m.AddInCppSharedPtr(drvd, 56) == ((36 * 10 + 3) * 100 + 56) * 100 + 11 41 | return # Comment out for manual leak checking (use `top` command). 42 | 43 | 44 | def test_drvd0_add_in_cpp_unique_ptr(): 45 | while True: 46 | drvd = PyDrvd0(0) 47 | with pytest.raises(ValueError) as exc_info: 48 | m.AddInCppUniquePtr(drvd, 0) 49 | assert ( 50 | str(exc_info.value) 51 | == "Alias class (also known as trampoline) does not inherit from" 52 | " py::trampoline_self_life_support, therefore the ownership of this" 53 | " instance cannot safely be transferred to C++." 54 | ) 55 | return # Comment out for manual leak checking (use `top` command). 56 | 57 | 58 | def test_drvd1_add_in_cpp_unique_ptr(): 59 | while True: 60 | drvd = PyDrvd1(25) 61 | assert m.AddInCppUniquePtr(drvd, 83) == ((25 * 10 + 3) * 200 + 83) * 100 + 13 62 | return # Comment out for manual leak checking (use `top` command). 63 | -------------------------------------------------------------------------------- /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 | if not m.defined_PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT: 8 | pytest.skip("smart_holder not available.", allow_module_level=True) 9 | 10 | 11 | class PyBig5(m.Big5): 12 | pass 13 | 14 | 15 | def test_m_big5(): 16 | obj = m.Big5("Seed") 17 | assert obj.history == "Seed" 18 | o1, o2 = m.action(obj, 0) 19 | assert o1 is not obj 20 | assert o1.history == "Seed" 21 | with pytest.raises(ValueError) as excinfo: 22 | _ = obj.history 23 | assert "Python instance was disowned" in str(excinfo.value) 24 | assert o2 is None 25 | 26 | 27 | @pytest.mark.parametrize( 28 | ("action_id", "expected_history"), 29 | [ 30 | (0, "Seed_CpCtor"), 31 | (1, "Seed_MvCtor"), 32 | (2, "Seed_OpEqLv"), 33 | (3, "Seed_OpEqRv"), 34 | ], 35 | ) 36 | def test_py_big5(action_id, expected_history): 37 | obj = PyBig5("Seed") 38 | assert obj.history == "Seed" 39 | o1, o2 = m.action(obj, action_id) 40 | assert o1 is obj 41 | assert o2.history == expected_history 42 | -------------------------------------------------------------------------------- /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/smart_holder.h" 6 | #include "pybind11/trampoline_self_life_support.h" 7 | #include "pybind11_tests.h" 8 | 9 | #include 10 | 11 | namespace pybind11_tests { 12 | namespace class_sh_trampoline_unique_ptr { 13 | 14 | class Class { 15 | public: 16 | virtual ~Class() = default; 17 | 18 | void setVal(std::uint64_t val) { val_ = val; } 19 | std::uint64_t getVal() const { return val_; } 20 | 21 | virtual std::unique_ptr clone() const = 0; 22 | virtual int foo() const = 0; 23 | 24 | protected: 25 | Class() = default; 26 | 27 | // Some compilers complain about implicitly defined versions of some of the following: 28 | Class(const Class &) = default; 29 | 30 | private: 31 | std::uint64_t val_ = 0; 32 | }; 33 | 34 | } // namespace class_sh_trampoline_unique_ptr 35 | } // namespace pybind11_tests 36 | 37 | PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_trampoline_unique_ptr::Class) 38 | 39 | namespace pybind11_tests { 40 | namespace class_sh_trampoline_unique_ptr { 41 | 42 | #ifdef PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT 43 | class PyClass : public Class, public py::trampoline_self_life_support { 44 | public: 45 | std::unique_ptr clone() const override { 46 | PYBIND11_OVERRIDE_PURE(std::unique_ptr, Class, clone); 47 | } 48 | 49 | int foo() const override { PYBIND11_OVERRIDE_PURE(int, Class, foo); } 50 | }; 51 | #endif 52 | 53 | } // namespace class_sh_trampoline_unique_ptr 54 | } // namespace pybind11_tests 55 | 56 | TEST_SUBMODULE(class_sh_trampoline_unique_ptr, m) { 57 | m.attr("defined_PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT") = 58 | #ifndef PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT 59 | false; 60 | #else 61 | true; 62 | 63 | using namespace pybind11_tests::class_sh_trampoline_unique_ptr; 64 | 65 | py::classh(m, "Class") 66 | .def(py::init<>()) 67 | .def("set_val", &Class::setVal) 68 | .def("get_val", &Class::getVal) 69 | .def("clone", &Class::clone) 70 | .def("foo", &Class::foo); 71 | 72 | m.def("clone", [](const Class &obj) { return obj.clone(); }); 73 | m.def("clone_and_foo", [](const Class &obj) { return obj.clone()->foo(); }); 74 | #endif // PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT 75 | } 76 | -------------------------------------------------------------------------------- /tests/test_class_sh_trampoline_unique_ptr.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pytest 4 | 5 | import pybind11_tests.class_sh_trampoline_unique_ptr as m 6 | 7 | if not m.defined_PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT: 8 | pytest.skip("smart_holder not available.", allow_module_level=True) 9 | 10 | 11 | class MyClass(m.Class): 12 | def foo(self): 13 | return 10 + self.get_val() 14 | 15 | def clone(self): 16 | cloned = MyClass() 17 | cloned.set_val(self.get_val() + 3) 18 | return cloned 19 | 20 | 21 | def test_m_clone(): 22 | obj = MyClass() 23 | while True: 24 | obj.set_val(5) 25 | obj = m.clone(obj) 26 | assert obj.get_val() == 5 + 3 27 | assert obj.foo() == 10 + 5 + 3 28 | return # Comment out for manual leak checking (use `top` command). 29 | 30 | 31 | def test_m_clone_and_foo(): 32 | obj = MyClass() 33 | obj.set_val(7) 34 | while True: 35 | assert m.clone_and_foo(obj) == 10 + 7 + 3 36 | return # Comment out for manual leak checking (use `top` command). 37 | -------------------------------------------------------------------------------- /tests/test_class_sh_unique_ptr_custom_deleter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "pybind11_tests.h" 4 | 5 | #include 6 | 7 | namespace pybind11_tests { 8 | namespace class_sh_unique_ptr_custom_deleter { 9 | 10 | // Reduced from a PyCLIF use case in the wild by @wangxf123456. 11 | class Pet { 12 | public: 13 | using Ptr = std::unique_ptr>; 14 | 15 | std::string name; 16 | 17 | static Ptr New(const std::string &name) { 18 | return Ptr(new Pet(name), std::default_delete()); 19 | } 20 | 21 | private: 22 | explicit Pet(const std::string &name) : name(name) {} 23 | }; 24 | 25 | } // namespace class_sh_unique_ptr_custom_deleter 26 | } // namespace pybind11_tests 27 | 28 | PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_unique_ptr_custom_deleter::Pet) 29 | 30 | namespace pybind11_tests { 31 | namespace class_sh_unique_ptr_custom_deleter { 32 | 33 | TEST_SUBMODULE(class_sh_unique_ptr_custom_deleter, m) { 34 | m.attr("defined_PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT") = 35 | #ifndef PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT 36 | false; 37 | #else 38 | true; 39 | 40 | py::classh(m, "Pet").def_readwrite("name", &Pet::name); 41 | 42 | m.def("create", &Pet::New); 43 | #endif // PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT 44 | } 45 | 46 | } // namespace class_sh_unique_ptr_custom_deleter 47 | } // namespace pybind11_tests 48 | -------------------------------------------------------------------------------- /tests/test_class_sh_unique_ptr_custom_deleter.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pytest 4 | 5 | from pybind11_tests import class_sh_unique_ptr_custom_deleter as m 6 | 7 | if not m.defined_PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT: 8 | pytest.skip("smart_holder not available.", allow_module_level=True) 9 | 10 | 11 | def test_create(): 12 | pet = m.create("abc") 13 | assert pet.name == "abc" 14 | -------------------------------------------------------------------------------- /tests/test_class_sh_unique_ptr_member.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "pybind11_tests.h" 4 | 5 | #include 6 | 7 | namespace pybind11_tests { 8 | namespace class_sh_unique_ptr_member { 9 | 10 | class pointee { // NOT copyable. 11 | public: 12 | pointee() = default; 13 | 14 | int get_int() const { return 213; } 15 | 16 | pointee(const pointee &) = delete; 17 | pointee(pointee &&) = delete; 18 | pointee &operator=(const pointee &) = delete; 19 | pointee &operator=(pointee &&) = delete; 20 | }; 21 | 22 | inline std::unique_ptr make_unique_pointee() { 23 | return std::unique_ptr(new pointee); 24 | } 25 | 26 | class ptr_owner { 27 | public: 28 | explicit ptr_owner(std::unique_ptr ptr) : ptr_(std::move(ptr)) {} 29 | 30 | bool is_owner() const { return bool(ptr_); } 31 | 32 | std::unique_ptr give_up_ownership_via_unique_ptr() { return std::move(ptr_); } 33 | std::shared_ptr give_up_ownership_via_shared_ptr() { return std::move(ptr_); } 34 | 35 | private: 36 | std::unique_ptr ptr_; 37 | }; 38 | 39 | } // namespace class_sh_unique_ptr_member 40 | } // namespace pybind11_tests 41 | 42 | PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_unique_ptr_member::pointee) 43 | 44 | namespace pybind11_tests { 45 | namespace class_sh_unique_ptr_member { 46 | 47 | TEST_SUBMODULE(class_sh_unique_ptr_member, m) { 48 | m.attr("defined_PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT") = 49 | #ifndef PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT 50 | false; 51 | #else 52 | true; 53 | 54 | py::classh(m, "pointee").def(py::init<>()).def("get_int", &pointee::get_int); 55 | 56 | m.def("make_unique_pointee", make_unique_pointee); 57 | 58 | py::class_(m, "ptr_owner") 59 | .def(py::init>(), py::arg("ptr")) 60 | .def("is_owner", &ptr_owner::is_owner) 61 | .def("give_up_ownership_via_unique_ptr", &ptr_owner::give_up_ownership_via_unique_ptr) 62 | .def("give_up_ownership_via_shared_ptr", &ptr_owner::give_up_ownership_via_shared_ptr); 63 | #endif // PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT 64 | } 65 | 66 | } // namespace class_sh_unique_ptr_member 67 | } // namespace pybind11_tests 68 | -------------------------------------------------------------------------------- /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 | if not m.defined_PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT: 8 | pytest.skip("smart_holder not available.", allow_module_level=True) 9 | 10 | 11 | def test_make_unique_pointee(): 12 | obj = m.make_unique_pointee() 13 | assert obj.get_int() == 213 14 | 15 | 16 | @pytest.mark.parametrize( 17 | "give_up_ownership_via", 18 | ["give_up_ownership_via_unique_ptr", "give_up_ownership_via_shared_ptr"], 19 | ) 20 | def test_pointee_and_ptr_owner(give_up_ownership_via): 21 | obj = m.pointee() 22 | assert obj.get_int() == 213 23 | owner = m.ptr_owner(obj) 24 | with pytest.raises(ValueError, match="Python instance was disowned"): 25 | obj.get_int() 26 | assert owner.is_owner() 27 | reclaimed = getattr(owner, give_up_ownership_via)() 28 | assert not owner.is_owner() 29 | assert reclaimed.get_int() == 213 30 | -------------------------------------------------------------------------------- /tests/test_class_sh_virtual_py_cpp_mix.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "pybind11_tests.h" 4 | 5 | #include 6 | 7 | namespace pybind11_tests { 8 | namespace class_sh_virtual_py_cpp_mix { 9 | 10 | class Base { 11 | public: 12 | virtual ~Base() = default; 13 | virtual int get() const { return 101; } 14 | 15 | // Some compilers complain about implicitly defined versions of some of the following: 16 | Base() = default; 17 | Base(const Base &) = default; 18 | }; 19 | 20 | class CppDerivedPlain : public Base { 21 | public: 22 | int get() const override { return 202; } 23 | }; 24 | 25 | class CppDerived : public Base { 26 | public: 27 | int get() const override { return 212; } 28 | }; 29 | 30 | int get_from_cpp_plainc_ptr(const Base *b) { return b->get() + 4000; } 31 | 32 | int get_from_cpp_unique_ptr(std::unique_ptr b) { return b->get() + 5000; } 33 | 34 | #ifdef PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT 35 | 36 | struct BaseVirtualOverrider : Base, py::trampoline_self_life_support { 37 | using Base::Base; 38 | 39 | int get() const override { PYBIND11_OVERRIDE(int, Base, get); } 40 | }; 41 | 42 | struct CppDerivedVirtualOverrider : CppDerived, py::trampoline_self_life_support { 43 | using CppDerived::CppDerived; 44 | 45 | int get() const override { PYBIND11_OVERRIDE(int, CppDerived, get); } 46 | }; 47 | 48 | #endif 49 | 50 | } // namespace class_sh_virtual_py_cpp_mix 51 | } // namespace pybind11_tests 52 | 53 | using namespace pybind11_tests::class_sh_virtual_py_cpp_mix; 54 | 55 | PYBIND11_SMART_HOLDER_TYPE_CASTERS(Base) 56 | PYBIND11_SMART_HOLDER_TYPE_CASTERS(CppDerivedPlain) 57 | PYBIND11_SMART_HOLDER_TYPE_CASTERS(CppDerived) 58 | 59 | TEST_SUBMODULE(class_sh_virtual_py_cpp_mix, m) { 60 | m.attr("defined_PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT") = 61 | #ifndef PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT 62 | false; 63 | #else 64 | true; 65 | 66 | py::classh(m, "Base").def(py::init<>()).def("get", &Base::get); 67 | 68 | py::classh(m, "CppDerivedPlain").def(py::init<>()); 69 | 70 | py::classh(m, "CppDerived").def(py::init<>()); 71 | 72 | m.def("get_from_cpp_plainc_ptr", get_from_cpp_plainc_ptr, py::arg("b")); 73 | m.def("get_from_cpp_unique_ptr", get_from_cpp_unique_ptr, py::arg("b")); 74 | #endif // PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT 75 | } 76 | -------------------------------------------------------------------------------- /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 | if not m.defined_PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT: 8 | pytest.skip("smart_holder not available.", allow_module_level=True) 9 | 10 | 11 | class PyBase(m.Base): # Avoiding name PyDerived, for more systematic naming. 12 | def __init__(self): 13 | m.Base.__init__(self) 14 | 15 | def get(self): 16 | return 323 17 | 18 | 19 | class PyCppDerived(m.CppDerived): 20 | def __init__(self): 21 | m.CppDerived.__init__(self) 22 | 23 | def get(self): 24 | return 434 25 | 26 | 27 | @pytest.mark.parametrize( 28 | ("ctor", "expected"), 29 | [ 30 | (m.Base, 101), 31 | (PyBase, 323), 32 | (m.CppDerivedPlain, 202), 33 | (m.CppDerived, 212), 34 | (PyCppDerived, 434), 35 | ], 36 | ) 37 | def test_base_get(ctor, expected): 38 | obj = ctor() 39 | assert obj.get() == expected 40 | 41 | 42 | @pytest.mark.parametrize( 43 | ("ctor", "expected"), 44 | [ 45 | (m.Base, 4101), 46 | (PyBase, 4323), 47 | (m.CppDerivedPlain, 4202), 48 | (m.CppDerived, 4212), 49 | (PyCppDerived, 4434), 50 | ], 51 | ) 52 | def test_get_from_cpp_plainc_ptr(ctor, expected): 53 | obj = ctor() 54 | assert m.get_from_cpp_plainc_ptr(obj) == expected 55 | 56 | 57 | @pytest.mark.parametrize( 58 | ("ctor", "expected"), 59 | [ 60 | (m.Base, 5101), 61 | (PyBase, 5323), 62 | (m.CppDerivedPlain, 5202), 63 | (m.CppDerived, 5212), 64 | (PyCppDerived, 5434), 65 | ], 66 | ) 67 | def test_get_from_cpp_unique_ptr(ctor, expected): 68 | obj = ctor() 69 | assert m.get_from_cpp_unique_ptr(obj) == expected 70 | -------------------------------------------------------------------------------- /tests/test_cmake_build/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_custom_target(test_cmake_build) 2 | 3 | function(pybind11_add_build_test name) 4 | cmake_parse_arguments(ARG "INSTALL" "" "" ${ARGN}) 5 | 6 | set(build_options "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}") 7 | 8 | list(APPEND build_options "-DPYBIND11_FINDPYTHON=${PYBIND11_FINDPYTHON}") 9 | if(PYBIND11_FINDPYTHON) 10 | if(DEFINED Python_ROOT_DIR) 11 | list(APPEND build_options "-DPython_ROOT_DIR=${Python_ROOT_DIR}") 12 | endif() 13 | 14 | list(APPEND build_options "-DPython_EXECUTABLE=${Python_EXECUTABLE}") 15 | else() 16 | list(APPEND build_options "-DPYTHON_EXECUTABLE=${PYTHON_EXECUTABLE}") 17 | endif() 18 | 19 | if(DEFINED CMAKE_CXX_STANDARD) 20 | list(APPEND build_options "-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}") 21 | endif() 22 | 23 | if(NOT ARG_INSTALL) 24 | list(APPEND build_options "-Dpybind11_SOURCE_DIR=${pybind11_SOURCE_DIR}") 25 | else() 26 | list(APPEND build_options "-DCMAKE_PREFIX_PATH=${pybind11_BINARY_DIR}/mock_install") 27 | endif() 28 | 29 | add_custom_target( 30 | test_build_${name} 31 | ${CMAKE_CTEST_COMMAND} 32 | --build-and-test 33 | "${CMAKE_CURRENT_SOURCE_DIR}/${name}" 34 | "${CMAKE_CURRENT_BINARY_DIR}/${name}" 35 | --build-config 36 | Release 37 | --build-noclean 38 | --build-generator 39 | ${CMAKE_GENERATOR} 40 | $<$:--build-generator-platform> 41 | ${CMAKE_GENERATOR_PLATFORM} 42 | --build-makeprogram 43 | ${CMAKE_MAKE_PROGRAM} 44 | --build-target 45 | check_${name} 46 | --build-options 47 | ${build_options}) 48 | if(ARG_INSTALL) 49 | add_dependencies(test_build_${name} mock_install) 50 | endif() 51 | add_dependencies(test_cmake_build test_build_${name}) 52 | endfunction() 53 | 54 | possibly_uninitialized(PYTHON_MODULE_EXTENSION Python_INTERPRETER_ID) 55 | 56 | pybind11_add_build_test(subdirectory_function) 57 | pybind11_add_build_test(subdirectory_target) 58 | if("${PYTHON_MODULE_EXTENSION}" MATCHES "pypy" OR "${Python_INTERPRETER_ID}" STREQUAL "PyPy") 59 | message(STATUS "Skipping embed test on PyPy") 60 | else() 61 | pybind11_add_build_test(subdirectory_embed) 62 | endif() 63 | 64 | if(PYBIND11_INSTALL) 65 | add_custom_target( 66 | mock_install ${CMAKE_COMMAND} "-DCMAKE_INSTALL_PREFIX=${pybind11_BINARY_DIR}/mock_install" -P 67 | "${pybind11_BINARY_DIR}/cmake_install.cmake") 68 | 69 | pybind11_add_build_test(installed_function INSTALL) 70 | pybind11_add_build_test(installed_target INSTALL) 71 | if(NOT ("${PYTHON_MODULE_EXTENSION}" MATCHES "pypy" OR "${Python_INTERPRETER_ID}" STREQUAL "PyPy" 72 | )) 73 | pybind11_add_build_test(installed_embed INSTALL) 74 | endif() 75 | endif() 76 | 77 | add_dependencies(check test_cmake_build) 78 | 79 | add_subdirectory(subdirectory_target EXCLUDE_FROM_ALL) 80 | add_subdirectory(subdirectory_embed EXCLUDE_FROM_ALL) 81 | -------------------------------------------------------------------------------- /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...3.30) 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...3.30) 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...3.30) 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...3.30) 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...3.30) 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...3.30) 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 | } 19 | 20 | } // namespace test_cpp_conduit 21 | } // namespace pybind11_tests 22 | -------------------------------------------------------------------------------- /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 | } // namespace test_cpp_conduit 39 | } // namespace pybind11_tests 40 | -------------------------------------------------------------------------------- /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 | } // namespace test_cpp_conduit 22 | } // namespace pybind11_tests 23 | -------------------------------------------------------------------------------- /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 | auto &self = py::cast(py::handle(self_base)); 30 | Py_VISIT(self.value.ptr()); 31 | return 0; 32 | }; 33 | type->tp_clear = [](PyObject *self_base) { 34 | auto &self = py::cast(py::handle(self_base)); 35 | self.value = py::none(); 36 | return 0; 37 | }; 38 | })); 39 | cls.def(py::init<>()); 40 | cls.def_readwrite("value", &OwnsPythonObjects::value); 41 | } 42 | -------------------------------------------------------------------------------- /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") 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") 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_descr_src_loc.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pytest 4 | 5 | from pybind11_tests import descr_src_loc as m 6 | 7 | if m.block_descr_offset is None: 8 | block_parametrize = (("all_blocks", None),) 9 | else: 10 | block_parametrize = ( 11 | ("block_descr", (("", 1), ("Abc", 2), ("D", 3), ("Ef", 4))), 12 | ( 13 | "block_const_name", 14 | ( 15 | ("G", 1), 16 | ("Hi", 2), 17 | ("0", 0), 18 | ("1", 0), 19 | ("23", 0), 20 | ("%", 6), 21 | ("J", 7), 22 | ("M", 8), 23 | ), 24 | ), 25 | ( 26 | "block_underscore", 27 | ( 28 | ("G", 2), 29 | ("Hi", 3), 30 | ("0", 0), 31 | ("1", 0), 32 | ("23", 0), 33 | ("%", 7), 34 | ("J", 8), 35 | ("M", 9), 36 | ), 37 | ), 38 | ("block_plus", (("NO", 1), ("PQ", 4))), 39 | ("block_concat", (("R", 1), ("S, T", 2), ("U, V", 6))), 40 | ("block_type_descr", (("{W}", 1),)), 41 | ("block_int_to_str", (("", 0), ("4", 0), ("56", 0))), 42 | ) 43 | 44 | 45 | @pytest.mark.skipif(m.block_descr_offset is None, reason="Not enabled.") 46 | @pytest.mark.parametrize(("block_name", "expected_text_line"), block_parametrize) 47 | def test_block(block_name, expected_text_line): 48 | if ( 49 | block_name == "block_underscore" 50 | and not m.defined_PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY 51 | ): 52 | pytest.skip("!defined(PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY)") 53 | offset = getattr(m, f"{block_name}_offset") 54 | for ix, (expected_text, expected_line) in enumerate(expected_text_line): 55 | text, file, line = getattr(m, f"{block_name}_c{ix}") 56 | assert text == expected_text 57 | if expected_line: 58 | assert file is not None, expected_text_line 59 | assert file.endswith("test_descr_src_loc.cpp") 60 | assert line == offset + expected_line 61 | else: 62 | assert file is None 63 | assert line == 0 64 | -------------------------------------------------------------------------------- /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("test_function3(a: int, b: int) -> None") 23 | 24 | assert m.test_function4.__doc__.startswith("test_function4(a: int, b: int) -> None") 25 | assert m.test_function4.__doc__.endswith("A custom docstring\n") 26 | 27 | # options.disable_function_signatures() 28 | # options.disable_user_defined_docstrings() 29 | assert not m.test_function5.__doc__ 30 | 31 | # nested options.enable_user_defined_docstrings() 32 | assert m.test_function6.__doc__ == "A custom docstring" 33 | 34 | # RAII destructor 35 | assert m.test_function7.__doc__.startswith("test_function7(a: int, b: int) -> None") 36 | assert m.test_function7.__doc__.endswith("A custom docstring\n") 37 | 38 | # when all options are disabled, no docstring (instead of an empty one) should be generated 39 | assert m.test_function8.__doc__ is None 40 | 41 | # Suppression of user-defined docstrings for non-function objects 42 | assert not m.DocstringTestFoo.__doc__ 43 | assert not m.DocstringTestFoo.value_prop.__doc__ 44 | 45 | # Check existig behaviour of enum docstings 46 | assert ( 47 | m.DocstringTestEnum1.__doc__ 48 | == "Enum docstring\n\nMembers:\n\n Member1\n\n Member2" 49 | ) 50 | 51 | # options.enable_enum_members_docstring() 52 | assert ( 53 | m.DocstringTestEnum2.__doc__ 54 | == "Enum docstring\n\nMembers:\n\n Member1\n\n Member2" 55 | ) 56 | 57 | # options.disable_enum_members_docstring() 58 | assert m.DocstringTestEnum3.__doc__ == "Enum docstring" 59 | 60 | # options.disable_user_defined_docstrings() 61 | assert m.DocstringTestEnum4.__doc__ == "Members:\n\n Member1\n\n Member2" 62 | 63 | # options.disable_user_defined_docstrings() 64 | # options.disable_enum_members_docstring() 65 | # When all options are disabled, no docstring (instead of an empty one) should be generated 66 | assert m.DocstringTestEnum5.__doc__ is None 67 | -------------------------------------------------------------------------------- /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" OR "${Python_INTERPRETER_ID}" STREQUAL "PyPy") 4 | message(STATUS "Skipping embed test on PyPy") 5 | add_custom_target(cpptest) # Dummy target on PyPy. Embedding is not supported. 6 | set(_suppress_unused_variable_warning "${DOWNLOAD_CATCH}") 7 | return() 8 | endif() 9 | 10 | if(TARGET Python::Module AND NOT TARGET Python::Python) 11 | message(STATUS "Skipping embed test since no embed libs found") 12 | add_custom_target(cpptest) # Dummy target since embedding is not supported. 13 | set(_suppress_unused_variable_warning "${DOWNLOAD_CATCH}") 14 | return() 15 | endif() 16 | 17 | find_package(Catch 2.13.9) 18 | 19 | if(CATCH_FOUND) 20 | message(STATUS "Building interpreter tests using Catch v${CATCH_VERSION}") 21 | else() 22 | message(STATUS "Catch not detected. Interpreter tests will be skipped. Install Catch headers" 23 | " manually or use `cmake -DDOWNLOAD_CATCH=ON` to fetch them automatically.") 24 | return() 25 | endif() 26 | 27 | find_package(Threads REQUIRED) 28 | 29 | add_executable(test_embed catch.cpp test_interpreter.cpp) 30 | pybind11_enable_warnings(test_embed) 31 | 32 | target_link_libraries(test_embed PRIVATE pybind11::embed Catch2::Catch2 Threads::Threads) 33 | 34 | if(NOT CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR) 35 | file(COPY test_interpreter.py test_trampoline.py DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") 36 | endif() 37 | 38 | add_custom_target( 39 | cpptest 40 | COMMAND "$" 41 | DEPENDS test_embed 42 | WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") 43 | 44 | pybind11_add_module(external_module THIN_LTO external_module.cpp) 45 | set_target_properties(external_module PROPERTIES LIBRARY_OUTPUT_DIRECTORY 46 | "${CMAKE_CURRENT_BINARY_DIR}") 47 | foreach(config ${CMAKE_CONFIGURATION_TYPES}) 48 | string(TOUPPER ${config} config) 49 | set_target_properties(external_module PROPERTIES LIBRARY_OUTPUT_DIRECTORY_${config} 50 | "${CMAKE_CURRENT_BINARY_DIR}") 51 | endforeach() 52 | add_dependencies(cpptest external_module) 53 | 54 | add_dependencies(check cpptest) 55 | -------------------------------------------------------------------------------- /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, m, py::mod_gil_not_used()) { 10 | class A { 11 | public: 12 | explicit A(int value) : v{value} {}; 13 | int v; 14 | }; 15 | 16 | py::class_(m, "A").def(py::init()).def_readwrite("value", &A::v); 17 | 18 | m.def("internals_at", 19 | []() { return reinterpret_cast(&py::detail::get_internals()); }); 20 | } 21 | -------------------------------------------------------------------------------- /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", 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 | from pybind11_tests import ConstructorStats, UserType 6 | from pybind11_tests import opaque_types as m 7 | 8 | 9 | def test_string_list(): 10 | lst = m.StringList() 11 | lst.push_back("Element 1") 12 | lst.push_back("Element 2") 13 | assert m.print_opaque_list(lst) == "Opaque list: [Element 1, Element 2]" 14 | assert lst.back() == "Element 2" 15 | 16 | for i, k in enumerate(lst, start=1): 17 | assert k == f"Element {i}" 18 | lst.pop_back() 19 | assert m.print_opaque_list(lst) == "Opaque list: [Element 1]" 20 | 21 | cvp = m.ClassWithSTLVecProperty() 22 | assert m.print_opaque_list(cvp.stringList) == "Opaque list: []" 23 | 24 | cvp.stringList = lst 25 | cvp.stringList.push_back("Element 3") 26 | assert m.print_opaque_list(cvp.stringList) == "Opaque list: [Element 1, Element 3]" 27 | 28 | 29 | def test_pointers(msg): 30 | living_before = ConstructorStats.get(UserType).alive() 31 | assert m.get_void_ptr_value(m.return_void_ptr()) == 0x1234 32 | assert m.get_void_ptr_value(UserType()) # Should also work for other C++ types 33 | assert ConstructorStats.get(UserType).alive() == living_before 34 | 35 | with pytest.raises(TypeError) as excinfo: 36 | m.get_void_ptr_value([1, 2, 3]) # This should not work 37 | assert ( 38 | msg(excinfo.value) 39 | == """ 40 | get_void_ptr_value(): incompatible function arguments. The following argument types are supported: 41 | 1. (arg0: capsule) -> int 42 | 43 | Invoked with: [1, 2, 3] 44 | """ 45 | ) 46 | 47 | assert m.return_null_str() is None 48 | assert m.get_null_str_value(m.return_null_str()) is not None 49 | 50 | ptr = m.return_unique_ptr() 51 | assert "StringList" in repr(ptr) 52 | assert m.print_opaque_list(ptr) == "Opaque list: [some value]" 53 | 54 | 55 | def test_unions(): 56 | int_float_union = m.IntFloat() 57 | int_float_union.i = 42 58 | assert int_float_union.i == 42 59 | int_float_union.f = 3.0 60 | assert int_float_union.f == 3.0 61 | -------------------------------------------------------------------------------- /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 | template // Using int as a trick to easily generate a series of types. 9 | struct CppBase { 10 | explicit CppBase(int value) : base_value(value) {} 11 | int get_base_value() const { return base_value; } 12 | void reset_base_value(int new_value) { base_value = new_value; } 13 | 14 | private: 15 | int base_value; 16 | }; 17 | 18 | template 19 | struct CppDrvd : CppBase { 20 | explicit CppDrvd(int value) : CppBase(value), drvd_value(value * 3) {} 21 | int get_drvd_value() const { return drvd_value; } 22 | void reset_drvd_value(int new_value) { drvd_value = new_value; } 23 | 24 | int get_base_value_from_drvd() const { return CppBase::get_base_value(); } 25 | void reset_base_value_from_drvd(int new_value) { CppBase::reset_base_value(new_value); } 26 | 27 | private: 28 | int drvd_value; 29 | }; 30 | 31 | template 32 | void wrap_classes(py::module_ &m, const char *name_base, const char *name_drvd, Extra... extra) { 33 | py::class_>(m, name_base, std::forward(extra)...) 34 | .def(py::init()) 35 | .def("get_base_value", &CppBase::get_base_value) 36 | .def("reset_base_value", &CppBase::reset_base_value); 37 | 38 | py::class_, CppBase>(m, name_drvd, std::forward(extra)...) 39 | .def(py::init()) 40 | .def("get_drvd_value", &CppDrvd::get_drvd_value) 41 | .def("reset_drvd_value", &CppDrvd::reset_drvd_value) 42 | .def("get_base_value_from_drvd", &CppDrvd::get_base_value_from_drvd) 43 | .def("reset_base_value_from_drvd", &CppDrvd::reset_base_value_from_drvd); 44 | } 45 | 46 | } // namespace test_python_multiple_inheritance 47 | 48 | TEST_SUBMODULE(python_multiple_inheritance, m) { 49 | using namespace test_python_multiple_inheritance; 50 | wrap_classes<0>(m, "CppBase0", "CppDrvd0"); 51 | wrap_classes<1>(m, "CppBase1", "CppDrvd1", py::metaclass((PyObject *) &PyType_Type)); 52 | 53 | m.attr("if_defined_PYBIND11_INIT_SAFETY_CHECKS_VIA_DEFAULT_PYBIND11_METACLASS") = 54 | #if defined(PYBIND11_INIT_SAFETY_CHECKS_VIA_DEFAULT_PYBIND11_METACLASS) 55 | true; 56 | #else 57 | false; 58 | #endif 59 | } 60 | -------------------------------------------------------------------------------- /tests/test_return_value_policy_override.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import re 4 | 5 | import pytest 6 | 7 | from pybind11_tests import return_value_policy_override as m 8 | 9 | 10 | def test_return_value(): 11 | assert m.return_value_with_default_policy() == "move" 12 | assert m.return_value_with_policy_copy() == "move" 13 | assert m.return_value_with_policy_clif_automatic() == "_clif_automatic" 14 | 15 | 16 | def test_return_pointer(): 17 | assert m.return_pointer_with_default_policy() == "automatic" 18 | assert m.return_pointer_with_policy_move() == "move" 19 | assert m.return_pointer_with_policy_clif_automatic() == "_clif_automatic" 20 | 21 | 22 | def test_persistent_holder(): 23 | h = m.data_fields_holder(2) 24 | assert h.vec_at(0).value == 13 25 | assert h.vec_at(1).value == 24 26 | assert h.vec_at_const_ptr(0).value == 13 27 | assert h.vec_at_const_ptr(1).value == 24 28 | 29 | 30 | def test_temporary_holder(): 31 | data_field = m.data_fields_holder(2).vec_at(1) 32 | assert data_field.value == 24 33 | data_field = m.data_fields_holder(2).vec_at_const_ptr(1) 34 | assert data_field.value == 24 35 | 36 | 37 | @pytest.mark.parametrize( 38 | ("func", "expected"), 39 | [ 40 | (m.return_value, "value(_MvCtor)*_MvCtor"), 41 | (m.return_pointer, "pointer"), 42 | (m.return_const_pointer, "const_pointer_CpCtor"), 43 | (m.return_reference, "reference_MvCtor"), 44 | (m.return_const_reference, "const_reference_CpCtor"), 45 | (m.return_unique_pointer, "unique_pointer"), 46 | (m.return_shared_pointer, "shared_pointer"), 47 | (m.return_value_nocopy, "value_nocopy(_MvCtor)*_MvCtor"), 48 | (m.return_pointer_nocopy, "pointer_nocopy"), 49 | (m.return_reference_nocopy, "reference_nocopy_MvCtor"), 50 | (m.return_unique_pointer_nocopy, "unique_pointer_nocopy"), 51 | (m.return_shared_pointer_nocopy, "shared_pointer_nocopy"), 52 | (m.return_pointer_nomove, "pointer_nomove"), 53 | (m.return_const_pointer_nomove, "const_pointer_nomove_CpCtor"), 54 | (m.return_reference_nomove, "reference_nomove_CpCtor"), 55 | (m.return_const_reference_nomove, "const_reference_nomove_CpCtor"), 56 | (m.return_unique_pointer_nomove, "unique_pointer_nomove"), 57 | (m.return_shared_pointer_nomove, "shared_pointer_nomove"), 58 | (m.return_pointer_nocopy_nomove, "pointer_nocopy_nomove"), 59 | (m.return_unique_pointer_nocopy_nomove, "unique_pointer_nocopy_nomove"), 60 | (m.return_shared_pointer_nocopy_nomove, "shared_pointer_nocopy_nomove"), 61 | ], 62 | ) 63 | def test_clif_automatic_return_value_policy_override(func, expected): 64 | assert re.match(expected, func().mtxt) 65 | -------------------------------------------------------------------------------- /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 | } // namespace 32 | 33 | TEST_SUBMODULE(thread, m) { 34 | 35 | py::class_(m, "IntStruct").def(py::init([](const int i) { return IntStruct(i); })); 36 | 37 | // implicitly_convertible uses loader_life_support when an implicit 38 | // conversion is required in order to lifetime extend the reference. 39 | // 40 | // This test should be run with ASAN for better effectiveness. 41 | py::implicitly_convertible(); 42 | 43 | m.def("test", [](int expected, const IntStruct &in) { 44 | { 45 | py::gil_scoped_release release; 46 | std::this_thread::sleep_for(std::chrono::milliseconds(5)); 47 | } 48 | 49 | if (in.value != expected) { 50 | throw std::runtime_error("Value changed!!"); 51 | } 52 | }); 53 | 54 | m.def( 55 | "test_no_gil", 56 | [](int expected, const IntStruct &in) { 57 | std::this_thread::sleep_for(std::chrono::milliseconds(5)); 58 | if (in.value != expected) { 59 | throw std::runtime_error("Value changed!!"); 60 | } 61 | }, 62 | py::call_guard()); 63 | 64 | // NOTE: std::string_view also uses loader_life_support to ensure that 65 | // the string contents remain alive, but that's a C++ 17 feature. 66 | } 67 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /tests/test_type_caster_odr_guard_1.cpp: -------------------------------------------------------------------------------- 1 | #define PYBIND11_DETAIL_TYPE_CASTER_ODR_GUARD_THROW_DISABLED true 2 | #include "pybind11_tests.h" 3 | 4 | // For test of real-world issue. 5 | #include "pybind11/stl.h" 6 | 7 | #include 8 | 9 | namespace mrc_ns { // minimal real caster 10 | 11 | struct type_mrc { 12 | explicit type_mrc(int v = -9999) : value(v) {} 13 | int value; 14 | }; 15 | 16 | struct minimal_real_caster { 17 | static constexpr auto name = py::detail::const_name(); 18 | 19 | static py::handle 20 | cast(type_mrc const &src, py::return_value_policy /*policy*/, py::handle /*parent*/) { 21 | return py::int_(src.value + 1010).release(); // ODR violation. 22 | } 23 | 24 | // Maximizing simplicity. This will go terribly wrong for other arg types. 25 | template 26 | using cast_op_type = const type_mrc &; 27 | 28 | // NOLINTNEXTLINE(google-explicit-constructor) 29 | operator type_mrc const &() { 30 | static type_mrc obj; 31 | obj.value = 11; // ODR violation. 32 | return obj; 33 | } 34 | 35 | bool load(py::handle src, bool /*convert*/) { 36 | // Only accepts str, but the value is ignored. 37 | return py::isinstance(src); 38 | } 39 | }; 40 | 41 | // Intentionally not called from Python: this test is to exercise the ODR guard, 42 | // not stl.h or stl_bind.h. 43 | inline void pass_vector_type_mrc(const std::vector &) {} 44 | 45 | } // namespace mrc_ns 46 | 47 | namespace pybind11 { 48 | namespace detail { 49 | template <> 50 | struct type_caster : mrc_ns::minimal_real_caster {}; 51 | } // namespace detail 52 | } // namespace pybind11 53 | 54 | TEST_SUBMODULE(type_caster_odr_guard_1, m) { 55 | m.def("type_mrc_to_python", []() { return mrc_ns::type_mrc(101); }); 56 | m.def("type_mrc_from_python", [](const mrc_ns::type_mrc &obj) { return obj.value + 100; }); 57 | m.def("type_caster_odr_guard_registry_values", []() { 58 | #if defined(PYBIND11_ENABLE_TYPE_CASTER_ODR_GUARD) 59 | py::list values; 60 | for (const auto ®_iter : py::detail::type_caster_odr_guard_registry()) { 61 | values.append(py::str(reg_iter.second)); 62 | } 63 | return values; 64 | #else 65 | return py::none(); 66 | #endif 67 | }); 68 | m.def("type_caster_odr_violation_detected_count", []() { 69 | #if defined(PYBIND11_ENABLE_TYPE_CASTER_ODR_GUARD) 70 | return py::detail::type_caster_odr_violation_detected_counter(); 71 | #else 72 | return py::none(); 73 | #endif 74 | }); 75 | 76 | // See comment near the bottom of test_type_caster_odr_guard_2.cpp. 77 | m.def("pass_vector_type_mrc", mrc_ns::pass_vector_type_mrc); 78 | 79 | m.attr("if_defined__NO_INLINE__") = 80 | #if defined(__NO_INLINE__) 81 | true; 82 | #else 83 | false; 84 | #endif 85 | } 86 | -------------------------------------------------------------------------------- /tests/test_type_caster_odr_guard_1.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pytest 4 | 5 | import pybind11_tests 6 | import pybind11_tests.type_caster_odr_guard_1 as m 7 | 8 | 9 | def test_type_mrc_to_python(): 10 | val = m.type_mrc_to_python() 11 | if val == 101 + 2020: 12 | pytest.skip( 13 | "UNEXPECTED: test_type_caster_odr_guard_2.cpp prevailed (to_python)." 14 | ) 15 | else: 16 | assert val == 101 + 1010 17 | 18 | 19 | def test_type_mrc_from_python(): 20 | val = m.type_mrc_from_python("ignored") 21 | if val == 100 + 22: 22 | pytest.skip( 23 | "UNEXPECTED: test_type_caster_odr_guard_2.cpp prevailed (from_python)." 24 | ) 25 | else: 26 | assert val == 100 + 11 27 | 28 | 29 | def test_type_caster_odr_registry_values(): 30 | reg_values = m.type_caster_odr_guard_registry_values() 31 | if reg_values is None: 32 | pytest.skip("type_caster_odr_guard_registry_values() is None") 33 | else: 34 | assert "test_type_caster_odr_guard_" in "\n".join(reg_values) 35 | 36 | 37 | def _count_0_message(tail): 38 | return ( 39 | "type_caster_odr_violation_detected_count() == 0:" 40 | f" {pybind11_tests.compiler_info}, {pybind11_tests.cpp_std}, " + tail 41 | ) 42 | 43 | 44 | def test_type_caster_odr_violation_detected_counter(): 45 | num_violations = m.type_caster_odr_violation_detected_count() 46 | if num_violations is None: 47 | pytest.skip("type_caster_odr_violation_detected_count() is None") 48 | if num_violations == 0 and m.if_defined__NO_INLINE__: 49 | pytest.skip(_count_0_message("__NO_INLINE__")) 50 | assert num_violations == 1 51 | -------------------------------------------------------------------------------- /tests/test_type_caster_odr_guard_2.cpp: -------------------------------------------------------------------------------- 1 | #define PYBIND11_DETAIL_TYPE_CASTER_ODR_GUARD_THROW_DISABLED true 2 | #include "pybind11_tests.h" 3 | 4 | // For test of real-world issue. 5 | #include "pybind11/stl_bind.h" 6 | 7 | #include 8 | 9 | namespace mrc_ns { // minimal real caster 10 | 11 | struct type_mrc { 12 | explicit type_mrc(int v = -9999) : value(v) {} 13 | int value; 14 | }; 15 | 16 | struct minimal_real_caster { 17 | static constexpr auto name = py::detail::const_name(); 18 | 19 | static py::handle 20 | cast(type_mrc const &src, py::return_value_policy /*policy*/, py::handle /*parent*/) { 21 | return py::int_(src.value + 2020).release(); // ODR violation. 22 | } 23 | 24 | // Maximizing simplicity. This will go terribly wrong for other arg types. 25 | template 26 | using cast_op_type = const type_mrc &; 27 | 28 | // NOLINTNEXTLINE(google-explicit-constructor) 29 | operator type_mrc const &() { 30 | static type_mrc obj; 31 | obj.value = 22; // ODR violation. 32 | return obj; 33 | } 34 | 35 | bool load(py::handle src, bool /*convert*/) { 36 | // Only accepts str, but the value is ignored. 37 | return py::isinstance(src); 38 | } 39 | }; 40 | 41 | // Intentionally not called from Python: this test is to exercise the ODR guard, 42 | // not stl.h or stl_bind.h. 43 | inline void pass_vector_type_mrc(const std::vector &) {} 44 | 45 | } // namespace mrc_ns 46 | 47 | PYBIND11_MAKE_OPAQUE(std::vector) 48 | 49 | namespace pybind11 { 50 | namespace detail { 51 | template <> 52 | struct type_caster : mrc_ns::minimal_real_caster {}; 53 | } // namespace detail 54 | } // namespace pybind11 55 | 56 | TEST_SUBMODULE(type_caster_odr_guard_2, m) { 57 | m.def("type_mrc_to_python", []() { return mrc_ns::type_mrc(202); }); 58 | m.def("type_mrc_from_python", [](const mrc_ns::type_mrc &obj) { return obj.value + 200; }); 59 | 60 | // Uncomment and run test_type_caster_odr_guard_1.py to verify that the 61 | // test_type_caster_odr_violation_detected_counter subtest fails 62 | // (num_violations 2 instead of 1). 63 | // Unlike the "controlled ODR violation" for the minimal_real_caster, this ODR violation is 64 | // completely unsafe, therefore it cannot portably be exercised with predictable results. 65 | // m.def("pass_vector_type_mrc", mrc_ns::pass_vector_type_mrc); 66 | } 67 | -------------------------------------------------------------------------------- /tests/test_type_caster_odr_guard_2.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pytest 4 | 5 | import pybind11_tests.type_caster_odr_guard_2 as m 6 | 7 | 8 | def test_type_mrc_to_python(): 9 | val = m.type_mrc_to_python() 10 | if val == 202 + 2020: 11 | pytest.skip( 12 | "UNEXPECTED: test_type_caster_odr_guard_2.cpp prevailed (to_python)." 13 | ) 14 | else: 15 | assert val == 202 + 1010 16 | 17 | 18 | def test_type_mrc_from_python(): 19 | val = m.type_mrc_from_python("ignored") 20 | if val == 200 + 22: 21 | pytest.skip( 22 | "UNEXPECTED: test_type_caster_odr_guard_2.cpp prevailed (from_python)." 23 | ) 24 | else: 25 | assert val == 200 + 11 26 | -------------------------------------------------------------------------------- /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("PYBIND11_INTERNALS_VERSION") = PYBIND11_INTERNALS_VERSION; 14 | m.attr("defined_WIN32_or__WIN32") = 15 | #if defined(WIN32) || defined(_WIN32) 16 | true; 17 | #else 18 | false; 19 | #endif 20 | m.attr("defined___clang__") = 21 | #if defined(__clang__) 22 | true; 23 | #else 24 | false; 25 | #endif 26 | m.attr("defined__LIBCPP_VERSION") = 27 | #if defined(_LIBCPP_VERSION) 28 | true; 29 | #else 30 | false; 31 | #endif 32 | m.attr("defined___GLIBCXX__") = 33 | #if defined(__GLIBCXX__) 34 | true; 35 | #else 36 | false; 37 | #endif 38 | } 39 | -------------------------------------------------------------------------------- /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 = ( 9 | "(m.PYBIND11_INTERNALS_VERSION <= 4 and (m.defined___clang__ or not m.defined___GLIBCXX__))" 10 | " or " 11 | "(m.PYBIND11_INTERNALS_VERSION >= 5 and not m.defined_WIN32_or__WIN32" 12 | " and " 13 | "(m.defined___clang__ or m.defined__LIBCPP_VERSION))" 14 | ) 15 | XFAIL_REASON = "Known issues: https://github.com/pybind/pybind11/pull/4319" 16 | 17 | 18 | @pytest.mark.xfail(XFAIL_CONDITION, reason=XFAIL_REASON, strict=False) 19 | @pytest.mark.parametrize( 20 | "any_struct", [m.unnamed_namespace_a_any_struct, mb.unnamed_namespace_b_any_struct] 21 | ) 22 | def test_have_class_any_struct(any_struct): 23 | assert any_struct is not None 24 | 25 | 26 | def test_have_at_least_one_class_any_struct(): 27 | assert ( 28 | m.unnamed_namespace_a_any_struct is not None 29 | or mb.unnamed_namespace_b_any_struct is not None 30 | ) 31 | 32 | 33 | @pytest.mark.xfail(XFAIL_CONDITION, reason=XFAIL_REASON, strict=True) 34 | def test_have_both_class_any_struct(): 35 | assert m.unnamed_namespace_a_any_struct is not None 36 | assert mb.unnamed_namespace_b_any_struct is not None 37 | -------------------------------------------------------------------------------- /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_changelog.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from __future__ import annotations 3 | 4 | import re 5 | 6 | import ghapi.all 7 | from rich import print 8 | from rich.syntax import Syntax 9 | 10 | ENTRY = re.compile( 11 | r""" 12 | Suggested \s changelog \s entry: 13 | .* 14 | ```rst 15 | \s* 16 | (.*?) 17 | \s* 18 | ``` 19 | """, 20 | re.DOTALL | re.VERBOSE, 21 | ) 22 | 23 | print() 24 | 25 | 26 | api = ghapi.all.GhApi(owner="pybind", repo="pybind11") 27 | 28 | issues_pages = ghapi.page.paged( 29 | api.issues.list_for_repo, labels="needs changelog", state="closed" 30 | ) 31 | issues = (issue for page in issues_pages for issue in page) 32 | missing = [] 33 | cats_descr = { 34 | "feat": "New Features", 35 | "feat(types)": "", 36 | "feat(cmake)": "", 37 | "fix": "Bug fixes", 38 | "fix(types)": "", 39 | "fix(cmake)": "", 40 | "docs": "Documentation", 41 | "tests": "Tests", 42 | "ci": "CI", 43 | "chore": "Other", 44 | "unknown": "Uncategorised", 45 | } 46 | cats: dict[str, list[str]] = {c: [] for c in cats_descr} 47 | 48 | for issue in issues: 49 | changelog = ENTRY.findall(issue.body or "") 50 | if not changelog or not changelog[0]: 51 | missing.append(issue) 52 | else: 53 | (msg,) = changelog 54 | if msg.startswith("- "): 55 | msg = msg[2:] 56 | if not msg.startswith("* "): 57 | msg = "* " + msg 58 | if not msg.endswith("."): 59 | msg += "." 60 | 61 | msg += f"\n `#{issue.number} <{issue.html_url}>`_" 62 | for cat in cats: 63 | if issue.title.lower().startswith(f"{cat}:"): 64 | cats[cat].append(msg) 65 | break 66 | else: 67 | cats["unknown"].append(msg) 68 | 69 | for cat, msgs in cats.items(): 70 | if msgs: 71 | desc = cats_descr[cat] 72 | print(f"[bold]{desc}:" if desc else f".. {cat}") 73 | print() 74 | for msg in msgs: 75 | print(Syntax(msg, "rst", theme="ansi_light", word_wrap=True)) 76 | print() 77 | print() 78 | 79 | if missing: 80 | print() 81 | print("[blue]" + "-" * 30) 82 | print() 83 | 84 | for issue in missing: 85 | print(f"[red bold]Missing:[/red bold][red] {issue.title}") 86 | print(f"[red] {issue.html_url}\n") 87 | 88 | print("[bold]Template:\n") 89 | msg = "## Suggested changelog entry:\n\n```rst\n\n```" 90 | print(Syntax(msg, "md", theme="ansi_light")) 91 | 92 | print() 93 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /tools/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools>=42", "wheel"] 3 | build-backend = "setuptools.build_meta" 4 | -------------------------------------------------------------------------------- /tools/setup_global.py.in: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Setup script for pybind11-global (in the sdist or in tools/setup_global.py in the repository) 4 | # This package is targeted for easy use from CMake. 5 | 6 | import glob 7 | import os 8 | import re 9 | 10 | # Setuptools has to be before distutils 11 | from setuptools import setup 12 | 13 | from distutils.command.install_headers import install_headers 14 | 15 | class InstallHeadersNested(install_headers): 16 | def run(self): 17 | headers = self.distribution.headers or [] 18 | for header in headers: 19 | # Remove pybind11/include/ 20 | short_header = header.split("/", 2)[-1] 21 | 22 | dst = os.path.join(self.install_dir, os.path.dirname(short_header)) 23 | self.mkpath(dst) 24 | (out, _) = self.copy_file(header, dst) 25 | self.outfiles.append(out) 26 | 27 | 28 | main_headers = glob.glob("pybind11/include/pybind11/*.h") 29 | detail_headers = glob.glob("pybind11/include/pybind11/detail/*.h") 30 | eigen_headers = glob.glob("pybind11/include/pybind11/eigen/*.h") 31 | stl_headers = glob.glob("pybind11/include/pybind11/stl/*.h") 32 | cmake_files = glob.glob("pybind11/share/cmake/pybind11/*.cmake") 33 | pkgconfig_files = glob.glob("pybind11/share/pkgconfig/*.pc") 34 | headers = main_headers + detail_headers + stl_headers + eigen_headers 35 | 36 | cmdclass = {"install_headers": InstallHeadersNested} 37 | $extra_cmd 38 | 39 | # This will _not_ affect installing from wheels, 40 | # only building wheels or installing from SDist. 41 | # Primarily intended on Windows, where this is sometimes 42 | # customized (for example, conda-forge uses Library/) 43 | base = os.environ.get("PYBIND11_GLOBAL_PREFIX", "") 44 | 45 | # Must have a separator 46 | if base and not base.endswith("/"): 47 | base += "/" 48 | 49 | setup( 50 | name="pybind11_global", 51 | version="$version", 52 | packages=[], 53 | headers=headers, 54 | data_files=[ 55 | (base + "share/cmake/pybind11", cmake_files), 56 | (base + "share/pkgconfig", pkgconfig_files), 57 | (base + "include/pybind11", main_headers), 58 | (base + "include/pybind11/detail", detail_headers), 59 | (base + "include/pybind11/eigen", eigen_headers), 60 | (base + "include/pybind11/stl", stl_headers), 61 | ], 62 | cmdclass=cmdclass, 63 | ) 64 | -------------------------------------------------------------------------------- /tools/setup_main.py.in: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Setup script (in the sdist or in tools/setup_main.py in the repository) 4 | 5 | from setuptools import setup 6 | 7 | cmdclass = {} 8 | $extra_cmd 9 | 10 | setup( 11 | name="pybind11", 12 | version="$version", 13 | download_url='https://github.com/pybind/pybind11/tarball/v$version', 14 | packages=[ 15 | "pybind11", 16 | "pybind11.include.pybind11", 17 | "pybind11.include.pybind11.detail", 18 | "pybind11.include.pybind11.eigen", 19 | "pybind11.include.pybind11.stl", 20 | "pybind11.share.cmake.pybind11", 21 | "pybind11.share.pkgconfig", 22 | ], 23 | package_data={ 24 | "pybind11": ["py.typed"], 25 | "pybind11.include.pybind11": ["*.h"], 26 | "pybind11.include.pybind11.detail": ["*.h"], 27 | "pybind11.include.pybind11.eigen": ["*.h"], 28 | "pybind11.include.pybind11.stl": ["*.h"], 29 | "pybind11.share.cmake.pybind11": ["*.cmake"], 30 | "pybind11.share.pkgconfig": ["*.pc"], 31 | }, 32 | extras_require={ 33 | "global": ["pybind11_global==$version"] 34 | }, 35 | entry_points={ 36 | "console_scripts": [ 37 | "pybind11-config = pybind11.__main__:main", 38 | ], 39 | "pipx.run": [ 40 | "pybind11 = pybind11.__main__:main", 41 | ] 42 | }, 43 | cmdclass=cmdclass 44 | ) 45 | -------------------------------------------------------------------------------- /ubench/holder_comparison.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "number_bucket.h" 4 | 5 | #include 6 | #include 7 | 8 | namespace hc { // holder comparison 9 | 10 | using nb_up = pybind11_ubench::number_bucket<1>; 11 | using nb_sp = pybind11_ubench::number_bucket<2>; 12 | using nb_pu = pybind11_ubench::number_bucket<3>; 13 | using nb_sh = pybind11_ubench::number_bucket<4>; 14 | 15 | namespace py = pybind11; 16 | 17 | template 18 | void wrap_number_bucket(py::module m, const char *class_name) { 19 | py::class_(m, class_name) 20 | .def(py::init(), py::arg("data_size") = 0) 21 | .def("sum", &WrappedType::sum) 22 | .def("add", &WrappedType::add, py::arg("other")); 23 | } 24 | 25 | #ifdef PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT 26 | 27 | template 28 | class padded_unique_ptr { 29 | std::unique_ptr ptr; 30 | char padding[sizeof(py::smart_holder) - sizeof(std::unique_ptr)]; 31 | 32 | public: 33 | padded_unique_ptr(T *p) : ptr(p) {} 34 | T *get() { return ptr.get(); } 35 | }; 36 | 37 | static_assert(sizeof(padded_unique_ptr) == sizeof(py::smart_holder), 38 | "Unexpected sizeof mismatch."); 39 | 40 | #endif 41 | 42 | } // namespace hc 43 | 44 | #ifdef PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT 45 | PYBIND11_DECLARE_HOLDER_TYPE(T, hc::padded_unique_ptr); 46 | #endif 47 | 48 | PYBIND11_MODULE(pybind11_ubench_holder_comparison, m) { 49 | using namespace hc; 50 | wrap_number_bucket>(m, "number_bucket_up"); 51 | wrap_number_bucket>(m, "number_bucket_sp"); 52 | #ifdef PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT 53 | m.def("sizeof_smart_holder", []() { return sizeof(py::smart_holder); }); 54 | wrap_number_bucket>(m, "number_bucket_pu"); 55 | wrap_number_bucket(m, "number_bucket_sh"); 56 | #endif 57 | } 58 | -------------------------------------------------------------------------------- /ubench/holder_comparison_extract_sheet_data.py: -------------------------------------------------------------------------------- 1 | """Extract mean ratios from holder_comparison.py output.""" 2 | 3 | # ruff: noqa 4 | # This code has no unit tests. 5 | # ruff cleanup deferred until the next time this code is actually used. 6 | 7 | import sys 8 | from typing import List, Optional 9 | 10 | 11 | def run(args: List[str]) -> None: 12 | assert len(args) == 1, "log_holder_comparison.txt" 13 | 14 | log_lines = open(args[0]).read().splitlines() 15 | 16 | for ratx in ("_ratS ", "_ratA "): 17 | print(ratx) 18 | header = None 19 | header_row = None 20 | data_row = None 21 | data_row_buffer: List[List[str]] = [] 22 | 23 | def show() -> Optional[List[str]]: 24 | if header_row: 25 | if header is None: # type: ignore[unreachable] 26 | print(",".join(header_row)) 27 | else: 28 | assert header == header_row 29 | if data_row is not None: 30 | print(",".join(data_row)) # type: ignore[unreachable] 31 | data_row_buffer.append(data_row) 32 | return header_row 33 | 34 | for line in log_lines: 35 | if line.endswith(" data_size"): 36 | header = show() 37 | flds = line.split() 38 | assert len(flds) == 2 39 | header_row = ["data_size"] 40 | data_row = [flds[0]] 41 | elif line.endswith(" call_repetitions"): 42 | flds = line.split() 43 | assert len(flds) == 2 44 | assert header_row is not None 45 | assert data_row is not None 46 | header_row.append("calls") 47 | data_row.append(flds[0]) 48 | header_row.append("up") 49 | data_row.append("1.000") 50 | elif line[2:].startswith(ratx): 51 | flds = line.split() 52 | assert len(flds) == 4 53 | assert header_row is not None 54 | assert data_row is not None 55 | header_row.append(line[:2]) 56 | data_row.append(flds[2]) 57 | show() 58 | 59 | assert header_row is not None 60 | print("Scaled to last column:") 61 | print(",".join(header_row)) 62 | for data_row in data_row_buffer: 63 | data_row_rescaled = data_row[:2] 64 | unit = float(data_row[-1]) 65 | for fld in data_row[2:]: 66 | data_row_rescaled.append("%.3f" % (float(fld) / unit)) 67 | print(",".join(data_row_rescaled)) 68 | 69 | 70 | if __name__ == "__main__": 71 | run(args=sys.argv[1:]) 72 | -------------------------------------------------------------------------------- /ubench/number_bucket.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace pybind11_ubench { 9 | 10 | template 11 | struct number_bucket { 12 | std::vector data; 13 | 14 | explicit number_bucket(std::size_t data_size = 0) : data(data_size, 1.0) {} 15 | 16 | double sum() const { 17 | std::size_t n = 0; 18 | double s = 0; 19 | const double *a = &*data.begin(); 20 | const double *e = &*data.end(); 21 | while (a != e) { 22 | s += *a++; 23 | n++; 24 | } 25 | if (n != data.size()) { 26 | std::cerr << "Internal consistency failure (sum)." << std::endl; 27 | std::terminate(); 28 | } 29 | return s; 30 | } 31 | 32 | std::size_t add(const number_bucket &other) { 33 | if (other.data.size() != data.size()) { 34 | std::cerr << "Incompatible data sizes (add)." << std::endl; 35 | std::terminate(); 36 | } 37 | std::size_t n = 0; 38 | double *a = &*data.begin(); 39 | const double *e = &*data.end(); 40 | const double *b = &*other.data.begin(); 41 | while (a != e) { 42 | *a++ += *b++; 43 | n++; 44 | } 45 | return n; 46 | } 47 | 48 | private: 49 | number_bucket(const number_bucket &) = delete; 50 | number_bucket(number_bucket &&) = delete; 51 | number_bucket &operator=(const number_bucket &) = delete; 52 | number_bucket &operator=(number_bucket &&) = delete; 53 | }; 54 | 55 | } // namespace pybind11_ubench 56 | -------------------------------------------------------------------------------- /ubench/python/number_bucket.clif: -------------------------------------------------------------------------------- 1 | from "pybind11/ubench/number_bucket.h": 2 | namespace `pybind11_ubench`: 3 | class `number_bucket<0>` as number_bucket_pc: 4 | def __init__(self, data_size: int = default) 5 | def sum(self) -> float 6 | def add(self, other: number_bucket_pc) -> int 7 | --------------------------------------------------------------------------------