Compare commits

...

74 Commits

Author SHA1 Message Date
Mana
a07e39e0f3
Merge 5f3d8d806a into 1f8b4a7f1a 2024-09-20 23:44:48 -07:00
Hintay
1f8b4a7f1a
fix(cmake): NO_EXTRAS in pybind11_add_module function partially working (#5378) 2024-09-19 11:24:35 -04:00
dependabot[bot]
ad9fd39e14
chore(deps): bump pypa/cibuildwheel in the actions group (#5376)
Bumps the actions group with 1 update: [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel).


Updates `pypa/cibuildwheel` from 2.20 to 2.21
- [Release notes](https://github.com/pypa/cibuildwheel/releases)
- [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md)
- [Commits](https://github.com/pypa/cibuildwheel/compare/v2.20...v2.21)

---
updated-dependencies:
- dependency-name: pypa/cibuildwheel
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-17 10:19:17 -07:00
vfdev
1d9483ff73
Added exception translator specific mutex used with try_translate_exceptions (#5362)
* Added exception translator specific mutex used with try_translate_exceptions
Fixes #5346

* - Replaced with_internals_for_exception_translator by with_exception_translators
- Incremented PYBIND11_INTERNALS_VERSION
- Added a test

* style: pre-commit fixes

* Fixed formatting and added explicit to ctors

* Addressed PR review comments

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-09-17 09:47:20 -07:00
Bobby R. Bruce
a7910be630
Add warn disable for GGC 12 bound checking error (#5355)
Issue: #5224

The `internals.registered_types_py...` line in pybind11.h triggers a
false-positive bounds checking warning in GCC 12.

This is discussed in
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=115824.

The workaround implemented is taken from suggestions then refactored to
use the `PYBIND11_WARNING_PUSH` and `PYBIND11_WARNING_POP` MACROS.
2024-09-14 23:51:50 -07:00
Henry Schreiner
0cf3a0f7b5
ci: PyPI attestations (#5374) 2024-09-13 20:21:43 -04:00
Henry Schreiner
5b7c0b04b9
docs: update changelog for 2.13.6 (#5372)
* docs: update changelog for 2.13.6

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* docs: mention supported versions

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2024-09-13 16:59:50 -04:00
Ralf W. Grosse-Kunstleve
ef5a9560bb
Enable type-safe interoperability between different independent Python/C++ bindings systems. (#5296)
* `self.__cpp_transporter__()` proof of concept: Enable passing C++ pointers across extensions even if the `PYBIND11_INTERNALS_VERSION`s do not match.

* Include cleanup (mainly to resolve PyPy build failures).

* Fix clang-tidy errors.

* Resolve `error: extra

* factor out platform_abi_id.h from internals.h (no functional changes)

* factor out internals_version.h from internals.h (no functional changes)

* Update CMakeLists.txt, tests/extra_python_package/test_files.py

* Revert "factor out internals_version.h from internals.h (no functional changes)"

This reverts commit 3ccea8cd43.

* Remove internals_version.h from CMakeLists.txt, tests/extra_python_package/test_files.py

* `.__cpp_transporter__()` implementation: compare `pybind11_platform_abi_id`, `cpp_typeid_name`

* Add PremiumTraveler

* Rename test_cpp_transporter_traveler_type.h -> test_cpp_transporter_traveler_types.h

* Expand tests: `PremiumTraveler`, `get_points()`

* Shuffle order of tests (no real changes).

* Move `__cpp_transporter__` lambda to `py::cpp_transporter()` regular function.

* Use `type_caster_generic::load(self)` instead of `cast<T *>(self)`

* Pass `const std::type_info *` via `py::capsule` (instead of `cpp_typeid_name`).

* Make platform_abi_id.h completely stand-alone.

* rename exo_planet.cpp -> exo_planet_pybind11.cpp

* Add exo_planet_c_api.cpp (incomplete).

* Fix silly oversight (wrong filename in `#include`).

* Resolve clang-tidy errors:

```
/__w/pybind11/pybind11/tests/exo_planet_c_api.cpp:10:18: error: 'wrapGetLuggage' is a static definition in anonymous namespace; static is redundant here [readability-static-definition-in-anonymous-namespace,-warnings-as-errors]
   10 | static PyObject *wrapGetLuggage(PyObject *, PyObject *) { return PyUnicode_FromString("TODO"); }
      | ~~~~~~           ^
/__w/pybind11/pybind11/tests/exo_planet_c_api.cpp:14:20: error: 'ThisMethodDef' is a static definition in anonymous namespace; static is redundant here [readability-static-definition-in-anonymous-namespace,-warnings-as-errors]
   14 | static PyMethodDef ThisMethodDef[]
      | ~~~~~~             ^
/__w/pybind11/pybind11/tests/exo_planet_c_api.cpp:17:27: error: 'ThisModuleDef' is a static definition in anonymous namespace; static is redundant here [readability-static-definition-in-anonymous-namespace,-warnings-as-errors]
   17 | static struct PyModuleDef ThisModuleDef = {
      | ~~~~~~                    ^
```

* Implement exo_planet_c_api GetLuggage(), GetPoints()

* Move new code from test_cpp_transporter_traveler_bindings.h to pybind11/detail/type_caster_base.h, under the name `class_dunder_cpp_transporter()`

* Fix oversight.

* Unconditionally add `__cpp_transporter__` method to all `py::class_` objects, but do not include that magic method in docstring signatures.

* Back out pybind11/detail/platform_abi_id.h for now. Maximizing reusability can be handled separately, later.

* Small cleanup.

* Restore and add to `test_call_cpp_transporter_*()`

* Ensure https://github.com/pybind/pybind11/issues/3788 does not bite again.

* `class_dunder_cpp_transporter()`: replace `obj.cast<std::string>()` with `std::string(obj)`

* Add (simple) copyright notices in all newly added files.

* Globally replace cpp_transporter with cpp_conduit

* style: pre-commit fixes

* IWYU fixes

* Rename `class_dunder_cpp_conduit()` -> `cpp_conduit_method()`

* Change `pybind11_platform_abi_id`, `pointer_kind` argument types from `str` to `bytes`.

This avoids the unicode decode/encode roundtrips:

* More robust (no decode/encode errors).

* Minor runtime optimization.

* Systematically rename `cap_cpp_type_info` -> `cpp_type_info_capsule` (no functional changes).

* Systematically replace `cpp_type_info_capsule` `name`: `"const std::type_info *"` -> `typeid(std::type_info).name()` (this IS a functional change).

This provides an extra layer of protection against C++ ABI mismatches:

* The first and most important layer is that the `PYBIND11_PLATFORM_ABI_ID`s must match between extensions.

* The second layer is that the `typeid(std::type_info).name()`s must match between extensions.

* Fix sort order accident in tests/CMakeLists.txt

* Apply suggestions from code review

Co-authored-by: Aaron Gokaslan <aaronGokaslan@gmail.com>

* style: pre-commit fixes

* refactor: rename to _pybind_conduit_v1_

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* Add test_home_planet_wrap_very_lonely_traveler(), test_exo_planet_pybind11_wrap_very_lonely_traveler()

* Resolve clang-tidy errors:

```
/__w/pybind11/pybind11/tests/test_cpp_conduit_traveler_bindings.h:39:32: error: parameter 'm' is passed by value and only copied once; consider moving it to avoid unnecessary copies [performance-unnecessary-value-param,-warnings-as-errors]
   10 |     py::class_<LonelyTraveler>(m, "LonelyTraveler");
      |                                ^
      |                                std::move( )
/__w/pybind11/pybind11/tests/test_cpp_conduit_traveler_bindings.h:43:52: error: parameter 'm' is passed by value and only copied once; consider moving it to avoid unnecessary copies [performance-unnecessary-value-param,-warnings-as-errors]
   43 |     py::class_<VeryLonelyTraveler, LonelyTraveler>(m, "VeryLonelyTraveler");
      |                                                    ^
      |                                                    std::move( )
```

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Ralf W. Grosse-Kunstleve <rwgk@google.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>
Co-authored-by: Aaron Gokaslan <aaronGokaslan@gmail.com>
2024-09-13 00:18:29 -04:00
dependabot[bot]
5efc7439d4
chore(deps): bump the actions group with 2 updates (#5361)
Bumps the actions group with 2 updates: [deadsnakes/action](https://github.com/deadsnakes/action) and [actions/attest-build-provenance](https://github.com/actions/attest-build-provenance).


Updates `deadsnakes/action` from 3.1.0 to 3.2.0
- [Release notes](https://github.com/deadsnakes/action/releases)
- [Commits](https://github.com/deadsnakes/action/compare/v3.1.0...v3.2.0)

Updates `actions/attest-build-provenance` from 1.4.2 to 1.4.3
- [Release notes](https://github.com/actions/attest-build-provenance/releases)
- [Changelog](https://github.com/actions/attest-build-provenance/blob/main/RELEASE.md)
- [Commits](6149ea5740...1c608d11d6)

---
updated-dependencies:
- dependency-name: deadsnakes/action
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
- dependency-name: actions/attest-build-provenance
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-09 14:31:38 -04:00
pre-commit-ci[bot]
8a801bdc32
chore(deps): update pre-commit hooks (#5350)
* chore(deps): update pre-commit hooks

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.5.6 → v0.6.3](https://github.com/astral-sh/ruff-pre-commit/compare/v0.5.6...v0.6.3)
- [github.com/pre-commit/mirrors-mypy: v1.11.1 → v1.11.2](https://github.com/pre-commit/mirrors-mypy/compare/v1.11.1...v1.11.2)
- [github.com/sirosen/texthooks: 0.6.6 → 0.6.7](https://github.com/sirosen/texthooks/compare/0.6.6...0.6.7)
- [github.com/PyCQA/pylint: v3.2.6 → v3.2.7](https://github.com/PyCQA/pylint/compare/v3.2.6...v3.2.7)
- [github.com/python-jsonschema/check-jsonschema: 0.29.1 → 0.29.2](https://github.com/python-jsonschema/check-jsonschema/compare/0.29.1...0.29.2)

* style: pre-commit fixes

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-09-03 10:51:21 -04:00
Vasily Litvinov
aeda49ed0b
Properly translate C++ exception to Python exception when creating Python buffer from wrapped object (#5324)
* Add test for throwing def_buffer case

* Translate C++ -> Python exceptions in buffer creation

This required a little refactoring to extract exception translation to a separate method

* Fix code formatting

* Fix "unused parameter" warning

* Refactor per review

* style: pre-commit fixes

* Address review comments

* Address review comments

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-09-02 09:01:59 -07:00
Jan Iwaszkiewicz
66c3774a64
Warnings wrappers to use from C++ (#5291)
* Add warning wrappers that allow to call warnings from pybind level

* Add missing include for warnings.h

* Change messages on failed checks, extend testing

* clang-tidy fix

* Refactor tests for warnings

* Move handle before check

* Remove unnecessary parametrized
2024-08-28 20:55:50 -07:00
Ralf W. Grosse-Kunstleve
65f4266cef
Add while True & top method to FAQ. (#5340) 2024-08-27 21:04:48 -07:00
ObeliskGate
3fb16ad175
fix: using __cpp_nontype_template_args instead of __cpp_nontype_template_parameter_class (#5330)
* fix: use `__cpp_nontype_template_args` instead of gnu extensions

* fix: add feature test value

* fix: change `PYBIND11_TYPING_H_HAS_STRING_LITERAL` skip reason
2024-08-25 23:36:03 -04:00
dependabot[bot]
e8f595bb85
chore(deps): bump actions/attest-build-provenance in the actions group (#5335)
Bumps the actions group with 1 update: [actions/attest-build-provenance](https://github.com/actions/attest-build-provenance).


Updates `actions/attest-build-provenance` from 1.4.1 to 1.4.2
- [Release notes](https://github.com/actions/attest-build-provenance/releases)
- [Changelog](https://github.com/actions/attest-build-provenance/blob/main/RELEASE.md)
- [Commits](310b0a4a3b...6149ea5740)

---
updated-dependencies:
- dependency-name: actions/attest-build-provenance
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-25 23:35:03 -04:00
Henry Schreiner
c2291e597f
docs: prepare for 2.13.5 (#5327)
* docs: prepare for 2.13.5

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* Update docs/changelog.rst

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2024-08-22 14:12:17 -04:00
DWesl
efa2b20d69
docs: clarify requirements for including pybind11 (#5326)
* DOC: Clarify requirements for including pybind11

Inherited from requirements for including Python.h

Closes #4999

* style: pre-commit fixes

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-08-22 00:29:21 -04:00
Henry Schreiner
9966ad409d
fix: allow -Wpedantic in C++20 mode (#5322)
* fix: allow -Wpedantic again

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* tests: ignore pedantic warning for PYBIND11_DECLARE_HOLDER_TYPE

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* tests: try just turning off pedantic for one file

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* tests: only run pedantic in C++20 mode

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* Update tests/local_bindings.h

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2024-08-22 00:27:50 -04:00
ObeliskGate
2baf9d6833
fix: <ranges> support for py::tuple and py::list (#5314)
* feat: add `<ranges>` support for `py::tuple` and `py::list`

* fix: format the code

* fix: disable `ranges` in clang < 16

* refactor: move `<ranges>` test macro to `test_pytypes.h`

* refactor: seperate `ranges` test into 3 funcs

* style: compress the if statement

* style: pre-commit fixes

* style: better formatting

---------

Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-08-21 15:33:06 -04:00
Henry Schreiner
7d85baa6a1
fix: never use .. in a header include (#5321)
* fix: never use `..` in a header include

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* fix: one more parent include

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2024-08-21 13:16:49 -04:00
Ralf W. Grosse-Kunstleve
a1d00916b2
Backport of https://github.com/google/pywrapcc/pull/30034 (#5305) 2024-08-14 22:30:29 -07:00
Henry Schreiner
bd5951b691
docs: prepare for 2.13.4 (#5312)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2024-08-14 23:57:37 -04:00
Henry Schreiner
28dbce4157
feat: require CMake 3.15+ (#5304)
* feat: require CMake 3.15+

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* Apply suggestions from code review

* Update CMakeLists.txt

* fix: adapt for CMake 3.30+ (using 3.18+)

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2024-08-14 18:21:04 -04:00
Markus Bauer
d893f9723a
fix: escape paths with spaces in pybind11-config (#4874)
* fix: Escape paths with spaces in include list from --includes

* fix: --includes should not use shlex on Windows platforms

* Apply suggestions from code review

* fix: use custom impl

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* Support trailing backslashes

Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Markus Bauer <markus.bauer@cispa.saarland>
Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>
2024-08-14 17:25:37 -04:00
Henry Schreiner
fc97cc41d5
Revert "fix: quote paths from pybind11-config (#5302)" (#5309)
This reverts commit 8d9f4d50cc.
2024-08-14 12:36:45 -04:00
pwdcd
0116906189
chore: remove repetitive words (#5308)
Signed-off-by: pwdcd <pwdcd@icloud.com>
2024-08-13 22:04:10 -07:00
Ralf W. Grosse-Kunstleve
0d44d720cb
Make stl.h list|set|map_caster more user friendly. (#4686)
* Add `test_pass_std_vector_int()`, `test_pass_std_set_int()` in test_stl

* Change `list_caster` to also accept generator objects (`PyGen_Check(src.ptr()`).

Note for completeness: This is a more conservative change than https://github.com/google/pywrapcc/pull/30042

* Drop in (currently unpublished) PyCLIF code, use in `list_caster`, adjust tests.

* Use `PyObjectTypeIsConvertibleToStdSet()` in `set_caster`, adjust tests.

* Use `PyObjectTypeIsConvertibleToStdMap()` in `map_caster`, add tests.

* Simplify `list_caster` `load()` implementation, push str/bytes check into `PyObjectTypeIsConvertibleToStdVector()`.

* clang-tidy cleanup with a few extra `(... != 0)` to be more consistent.

* Also use `PyObjectTypeIsConvertibleToStdVector()` in `array_caster`.

* Update comment pointing to clif/python/runtime.cc (code is unchanged).

* Comprehensive test coverage, enhanced set_caster load implementation.

* Resolve clang-tidy eror.

* Add a long C++ comment explaining what led to the `PyObjectTypeIsConvertibleTo*()` implementations.

* Minor function name change in test.

* strcmp -> std::strcmp (thanks @Skylion007 for catching this)

* Add `PyCallable_Check(items)` in `PyObjectTypeIsConvertibleToStdMap()`

* Resolve clang-tidy error

* Use `PyMapping_Items()` instead of `src.attr("items")()`, to be internally consistent with `PyMapping_Check()`

* Update link to PyCLIF sources.

* Fix typo (thanks @wangxf123456 for catching this)

* Add `test_pass_std_vector_int()`, `test_pass_std_set_int()` in test_stl

* Change `list_caster` to also accept generator objects (`PyGen_Check(src.ptr()`).

Note for completeness: This is a more conservative change than https://github.com/google/pywrapcc/pull/30042

* Drop in (currently unpublished) PyCLIF code, use in `list_caster`, adjust tests.

* Use `PyObjectTypeIsConvertibleToStdSet()` in `set_caster`, adjust tests.

* Use `PyObjectTypeIsConvertibleToStdMap()` in `map_caster`, add tests.

* Simplify `list_caster` `load()` implementation, push str/bytes check into `PyObjectTypeIsConvertibleToStdVector()`.

* clang-tidy cleanup with a few extra `(... != 0)` to be more consistent.

* Also use `PyObjectTypeIsConvertibleToStdVector()` in `array_caster`.

* Update comment pointing to clif/python/runtime.cc (code is unchanged).

* Comprehensive test coverage, enhanced set_caster load implementation.

* Resolve clang-tidy eror.

* Add a long C++ comment explaining what led to the `PyObjectTypeIsConvertibleTo*()` implementations.

* Minor function name change in test.

* strcmp -> std::strcmp (thanks @Skylion007 for catching this)

* Add `PyCallable_Check(items)` in `PyObjectTypeIsConvertibleToStdMap()`

* Resolve clang-tidy error

* Use `PyMapping_Items()` instead of `src.attr("items")()`, to be internally consistent with `PyMapping_Check()`

* Update link to PyCLIF sources.

* Fix typo (thanks @wangxf123456 for catching this)

* Fix typo discovered by new version of codespell.
2024-08-13 11:42:51 -07:00
Henry Schreiner
4a06eca591 docs: prepare for 2.13.3
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2024-08-13 13:25:38 -04:00
Henry Schreiner
8d9f4d50cc
fix: quote paths from pybind11-config (#5302)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2024-08-13 13:02:15 -04:00
Henry Schreiner
1fe92c7b35
fix: emscripten cmake issue (#5301)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2024-08-13 12:32:32 -04:00
Henry Schreiner
40f2c7863b
docs: prepare for 2.13.2 (#5299)
* docs: prepare for 2.13.2

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* Update changelog.rst

* Update changelog.rst

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2024-08-13 09:04:03 -04:00
dependabot[bot]
8d90b83b19
chore(deps): bump actions/attest-build-provenance in the actions group (#5297)
Bumps the actions group with 1 update: [actions/attest-build-provenance](https://github.com/actions/attest-build-provenance).


Updates `actions/attest-build-provenance` from 1.4.0 to 1.4.1
- [Release notes](https://github.com/actions/attest-build-provenance/releases)
- [Changelog](https://github.com/actions/attest-build-provenance/blob/main/RELEASE.md)
- [Commits](210c191353...310b0a4a3b)

---
updated-dependencies:
- dependency-name: actions/attest-build-provenance
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-12 19:49:56 -04:00
Henry Schreiner
fc59f4e6e5
fix(cmake): add required emscripten flags (#5298)
* fix(cmake): add required emscripten flags

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* Update emscripten.yaml

* fix(cmake): add required emscripten flags to headers target

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* fix(cmake): incorrect detection of Emscripten

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* fix(cmake): allow pybind11::headers to be modified

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* fix(cmake): hide a warning when building the tests standalone

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* fix(cmake): use explicit variable for is config

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* fix(cmake): go back to ALIAS target

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* chore: reduce overall diff

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* chore: reduce overall diff

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* chore: shorten code a bit

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2024-08-12 16:51:48 -04:00
Xiaofei Wang
898794488a
Add type_caster_std_function_specializations feature. (#4597)
* Allow specializations based on callback function return values.

* clang-tidy auto fix

* Add a test case for function specialization.

* Add test for callback function that raises Python exception.

* Fix test failures.

* style: pre-commit fixes

* Add `#define PYBIND11_HAS_TYPE_CASTER_STD_FUNCTION_SPECIALIZATIONS`

---------

Co-authored-by: Ralf W. Grosse-Kunstleve <rwgk@google.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-08-09 13:28:12 -07:00
pre-commit-ci[bot]
20551ab3d8
chore(deps): update pre-commit hooks (#5288)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.5.0 → v0.5.6](https://github.com/astral-sh/ruff-pre-commit/compare/v0.5.0...v0.5.6)
- [github.com/pre-commit/mirrors-mypy: v1.10.1 → v1.11.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.10.1...v1.11.1)
- [github.com/PyCQA/pylint: v3.2.4 → v3.2.6](https://github.com/PyCQA/pylint/compare/v3.2.4...v3.2.6)
- [github.com/python-jsonschema/check-jsonschema: 0.28.6 → 0.29.1](https://github.com/python-jsonschema/check-jsonschema/compare/0.28.6...0.29.1)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-08-05 20:19:10 -07:00
dependabot[bot]
845105383f
chore(deps): bump the actions group with 2 updates (#5287)
Bumps the actions group with 2 updates: [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) and [actions/attest-build-provenance](https://github.com/actions/attest-build-provenance).


Updates `pypa/cibuildwheel` from 2.19 to 2.20
- [Release notes](https://github.com/pypa/cibuildwheel/releases)
- [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md)
- [Commits](https://github.com/pypa/cibuildwheel/compare/v2.19...v2.20)

Updates `actions/attest-build-provenance` from 1.3.3 to 1.4.0
- [Release notes](https://github.com/actions/attest-build-provenance/releases)
- [Changelog](https://github.com/actions/attest-build-provenance/blob/main/RELEASE.md)
- [Commits](5e9cb68e95...210c191353)

---
updated-dependencies:
- dependency-name: pypa/cibuildwheel
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
- dependency-name: actions/attest-build-provenance
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-05 10:07:55 -04:00
fred-sch
916778df48
fix: typo in documentation (#5284) 2024-08-02 12:17:15 -07:00
Ralf W. Grosse-Kunstleve
723307283e
feat: remove Python 3.7 support (#5191)
* First pass updating misc files, informed by https://github.com/pybind/pybind11/pull/5177/commits

* Remove jobs using silkeh/clang and gcc docker containers that come with Python 3.7

* Add silkeh/clang:17-bookworm

* Add job using GCC 7

* Revert "Add job using GCC 7"

This reverts commit 518515a761.

* Try running in ubuntu-18.04 container under ubuntu-latest (to get GCC 7)

* Fix `-` vs `:` mixup.

* This reverts commit b1c4304475.

Revert "Try running in ubuntu:18.04 container under ubuntu-latest (to get GCC 7)"

This reverts commit b203a294bb.

* `git grep 0x03080000` cleanup.

* `git grep -I -E '3\.7'` cleanup.

Removes two changes made under pybind/pybind11#3702

* Revert "`git grep -I -E '3\.7'` cleanup."

This reverts commit bb5b9d187b.

* Remove comments that are evidently incorrect:

```
...
-- The CXX compiler identification is Clang 15.0.7
...
- Found Python: /usr/bin/python3.9 (found suitable version "3.9.2", minimum required is "3.7") found components: Interpreter Development.Module Development.Embed
...
/__w/pybind11/pybind11/include/pybind11/gil.h:150:13: error: 'auto key' can be declared as 'auto *key' [readability-qualified-auto,-warnings-as-errors]
            auto key = internals.tstate;
            ^~~~~
            auto *
/__w/pybind11/pybind11/include/pybind11/gil.h:174:13: error: 'auto key' can be declared as 'auto *key' [readability-qualified-auto,-warnings-as-errors]
            auto key = detail::get_internals().tstate;
            ^~~~~
            auto *
```

* .github/workflows/configure.yml: Change from Python 3.7 to 3.8

* Misc cleanup pass

* Miscellaneous changes based on manual review of the `git grep` matches below:

```
git_grep_37_38.sh |& sort | uniq -c
```

With git_grep_37_38.sh:

```
set -x
git grep 0x0307
git grep 0x0308
git grep PY_MINOR_VERSION
git grep PYPY_VERSION
git grep -I -E '3\.7'
git grep -I -E '3\.8'
git grep -I -E '\(3, 7'
git grep -I -E '\(3, 8'
git grep -I -E '3[^A-Za-z0-9.]+7'
git grep -I -E '3[^A-Za-z0-9.]+8'
```

Output:

```
      1 .appveyor.yml:    $env:CMAKE_INCLUDE_PATH = "eigen-3.3.7;$env:CMAKE_INCLUDE_PATH"
      1 .appveyor.yml:    7z x eigen-3.3.7.zip -y > $null
      1 .appveyor.yml:    Start-FileDownload 'https://gitlab.com/libeigen/eigen/-/archive/3.3.7/eigen-3.3.7.zip'
      1 CMakeLists.txt:    # Bug in macOS CMake < 3.7 is unable to download catch
      1 CMakeLists.txt:  elseif(WINDOWS AND CMAKE_VERSION VERSION_LESS 3.8)
      1 CMakeLists.txt:  if(OSX AND CMAKE_VERSION VERSION_LESS 3.7)
      1 CMakeLists.txt:    message(WARNING "CMAKE 3.7+ needed on macOS to download catch, and newer HIGHLY recommended")
      1 CMakeLists.txt:    message(WARNING "CMAKE 3.8+ tested on Windows, previous versions untested")
      1 CMakeLists.txt:    # Only tested with 3.8+ in CI.
      1 docs/advanced/functions.rst:Python 3.8 introduced a new positional-only argument syntax, using ``/`` in the
      1 docs/changelog.rst:* Adapt pybind11 to a C API convention change in Python 3.8. `#1950
      1 docs/changelog.rst:* Allow thread termination to be avoided during shutdown for CPython 3.7+ via
      1 docs/changelog.rst:  considered as conversion, consistent with Python 3.8+.
      1 docs/changelog.rst:  CPython 3.8 and 3.9 debug builds.
      1 docs/changelog.rst:* Enum now has an ``__index__`` method on Python <3.8 too.
      1 docs/changelog.rst:  on Python 3.8. `#1780 <https://github.com/pybind/pybind11/pull/1780>`_.
      1 docs/changelog.rst:* PyPy 3.10 support was added, PyPy 3.7 support was dropped.
      2 docs/changelog.rst:* Support PyPy 7.3.7 and the PyPy3.8 beta. Test python-3.11 on PRs with the
      1 docs/changelog.rst:* Use ``macos-13`` (Intel) for CI jobs for now (will drop Python 3.7 soon).
      1 docs/changelog.rst:* Use new Python 3.7 Thread Specific Storage (TSS) implementation if available.
      1 docs/compiling.rst:    cmake -DPYBIND11_PYTHON_VERSION=3.8 ..
      1 docs/compiling.rst:    find_package(Python 3.8 COMPONENTS Interpreter Development REQUIRED)
      1 docs/limitations.rst:- PyPy3 7.3.1 and 7.3.2 have issues with several tests on 32-bit Windows.
      1 docs/requirements.txt:idna==3.7 \
      1 + git grep 0x0307
      1 + git grep 0x0308
      1 + git grep -I -E '\(3, 7'
      1 + git grep -I -E '3\.7'
      1 + git grep -I -E '\(3, 8'
      1 + git grep -I -E '3\.8'
      1 + git grep -I -E '3[^A-Za-z0-9.]+7'
      1 + git grep -I -E '3[^A-Za-z0-9.]+8'
      1 + git grep PY_MINOR_VERSION
      1 + git grep PYPY_VERSION
      2 .github/workflows/ci.yml:        - '3.8'
      1 .github/workflows/ci.yml:        - 3.8
      1 .github/workflows/ci.yml:    - name: Add Python 3.8
      1 .github/workflows/ci.yml:        - 'pypy-3.8'
      2 .github/workflows/ci.yml:            python: '3.8'
      1 .github/workflows/ci.yml:          - python: '3.8'
      1 .github/workflows/ci.yml:          - python: 3.8
      1 .github/workflows/ci.yml:            python: 'pypy-3.8'
      1 .github/workflows/configure.yml:          cmake: "3.8"
      1 .github/workflows/configure.yml:    name: 🐍 3.8 • CMake ${{ matrix.cmake }} • ${{ matrix.runs-on }}
      1 .github/workflows/configure.yml:    - name: Setup Python 3.8
      1 .github/workflows/configure.yml:        python-version: 3.8
      1 .github/workflows/pip.yml:    name: 🐍 3.8 • 📦 & 📦 tests • ubuntu-latest
      1 .github/workflows/pip.yml:    name: 🐍 3.8 • 📦 tests • windows-latest
      2 .github/workflows/pip.yml:    - name: Setup 🐍 3.8
      2 .github/workflows/pip.yml:        python-version: 3.8
      2 include/pybind11/cast.h:#if !defined(PYPY_VERSION)
      2 include/pybind11/cast.h:#if defined(PYPY_VERSION)
      2 include/pybind11/cast.h:            // PyPy: 7.3.7's 3.8 does not implement PyLong_*'s __index__ calls.
      5 include/pybind11/detail/class.h:#if !defined(PYPY_VERSION)
      1 include/pybind11/detail/class.h:#if defined(PYPY_VERSION)
      1 include/pybind11/detail/class.h:    // This was not needed before Python 3.8 (Python issue 35810)
      1 include/pybind11/detail/common.h:    && !defined(PYPY_VERSION) && !defined(PYBIND11_ASSERT_GIL_HELD_INCREF_DECREF)
      2 include/pybind11/detail/common.h:#    error "PYTHON < 3.8 IS UNSUPPORTED. pybind11 v2.13 was the last to support Python 3.7."
      1 include/pybind11/detail/common.h:#if defined(PYPY_VERSION) && !defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
      1 include/pybind11/detail/common.h:#if PY_VERSION_HEX < 0x03080000
      1 include/pybind11/detail/common.h:            = PYBIND11_TOSTRING(PY_MAJOR_VERSION) "." PYBIND11_TOSTRING(PY_MINOR_VERSION);        \
      1 include/pybind11/detail/internals.h:        // called. PYBIND11_TLS_FREE is PyThread_tss_free on python 3.7+. On older python, it does
      1 include/pybind11/detail/internals.h:#if PYBIND11_INTERNALS_VERSION <= 4 || defined(PYPY_VERSION)
      1 include/pybind11/detail/internals.h:// The old Python Thread Local Storage (TLS) API is deprecated in Python 3.7 in favor of the new
      1 include/pybind11/detail/type_caster_base.h:#if defined(PYPY_VERSION)
      1 include/pybind11/embed.h:#    define PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX (0x03080000)
      1 include/pybind11/embed.h:#if defined(PYPY_VERSION)
      1 include/pybind11/eval.h:    // globals if not yet present.  Python 3.8 made PyRun_String behave
      2 include/pybind11/eval.h:#if defined(PYPY_VERSION)
      2 include/pybind11/eval.h:    // was missing from PyPy3.8 7.3.7.
      2 include/pybind11/gil.h:    /// allowed during shutdown. Check _Py_IsFinalizing() on Python 3.7+, and
      1 include/pybind11/pybind11.h:#if !defined(PYPY_VERSION)
      4 include/pybind11/pybind11.h:#if !defined(PYPY_VERSION) && PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION == 9
      1 include/pybind11/pytypes.h:#endif //! defined(PYPY_VERSION)
      2 include/pybind11/pytypes.h:#if !defined(PYPY_VERSION)
      1 include/pybind11/pytypes.h:#    if defined(PYPY_VERSION_NUM) && PYPY_VERSION_NUM < 0x07030a00
      1 include/pybind11/pytypes.h:#ifdef PYPY_VERSION
      1 include/pybind11/stl/filesystem.h:#    if !defined(PYPY_VERSION)
      2 pybind11/__init__.py:if sys.version_info < (3, 8):
      2 pybind11/__init__.py:    msg = "pybind11 does not support Python < 3.8. v2.13 was the last release supporting Python 3.7."
      1 pyproject.toml:master.py-version = "3.8"
      1 pyproject.toml:python_version = "3.8"
      1 README.rst:lines of code and depend on Python (3.8+, or PyPy) and the C++
      2 README.rst:- Python 3.8+, and PyPy3 7.3 are supported with an implementation-agnostic
      1 setup.cfg:    Programming Language :: Python :: 3.8
      1 setup.cfg:python_requires = >=3.8
      1 setup.py:# TODO: use literals & overload (typing extensions or Python 3.8)
      1 tests/CMakeLists.txt:if(NOT CMAKE_VERSION VERSION_LESS 3.8)
      2 tests/constructor_stats.h:#if defined(PYPY_VERSION)
      1 tests/env.py:    doesn't work on CPython 3.8.0 with pytest==3.3.2 on Ubuntu 18.04 (#2922).
      1 tests/requirements.txt:build~=1.0; python_version>="3.8"
      1 tests/requirements.txt:numpy~=1.21.5; platform_python_implementation!="PyPy" and python_version>="3.8" and python_version<"3.10"
      1 tests/requirements.txt:numpy~=1.23.0; python_version=="3.8" and platform_python_implementation=="PyPy"
      1 tests/test_buffers.py:    env.PYPY, reason="PyPy 7.3.7 doesn't clear this anymore", strict=False
      1 tests/test_builtin_casters.py:    # Before Python 3.8, `PyLong_AsLong` does not pick up on `obj.__index__`,
      2 tests/test_builtin_casters.py:    if (3, 8) <= sys.version_info < (3, 10) and env.CPYTHON:
      4 tests/test_builtin_casters.py:    # TODO: PyPy 3.8 does not behave like CPython 3.8 here yet (7.3.7)
      1 tests/test_callbacks.py:    assert m.test_callback3(z.double) == "func(43) = 86"
      2 tests/test_call_policies.cpp:#if !defined(PYPY_VERSION)
      1 tests/test_chrono.py:    diff = m.test_chrono_float_diff(43.789012, 1.123456)
      1 tests/test_constants_and_functions.py:    assert m.f3(86) == 89
      1 tests/test_eigen_matrix.py:    a_copy3[8, 1] = 11
      1 tests/test_eigen_matrix.py:    assert np.all(cornersc == np.array([[1.0, 3], [7, 9]]))
      1 tests/test_eigen_matrix.py:    assert np.all(cornersr == np.array([[1.0, 3], [7, 9]]))
      1 tests/test_eigen_matrix.py:        mymat = chol(np.array([[1.0, 2, 4], [2, 13, 23], [4, 23, 77]]))
      1 tests/test_exceptions.py:    if hasattr(pytest, unraisable):  # Python >= 3.8 and pytest >= 6
      2 tests/test_exceptions.py:@pytest.mark.xfail(env.PYPY, reason="Failure on PyPy 3.8 (7.3.7)", strict=False)
      1 tests/test_factory_constructors.py:    assert [i.alive() for i in cstats] == [13, 7]
      1 tests/test_kwargs_and_defaults.cpp:#ifdef PYPY_VERSION
      1 tests/test_local_bindings.py:    assert i1.get3() == 8
      1 tests/test_methods_and_attributes.cpp:#if !defined(PYPY_VERSION)
      1 tests/test_numpy_array.py:    a = np.arange(3 * 7 * 2) + 1
      1 tests/test_numpy_array.py:    assert str(excinfo.value) == "cannot reshape array of size 42 into shape (3,7,1)"
      2 tests/test_numpy_array.py:    assert x.shape == (3, 7, 2)
      2 tests/test_numpy_array.py:        m.reshape_tuple(a, (3, 7, 1))
      2 tests/test_numpy_array.py:    x = m.reshape_tuple(a, (3, 7, 2))
      1 tests/test_numpy_vectorize.py:    assert np.isclose(m.vectorized_func3(np.array(3 + 7j)), [6 + 14j])
      1 tests/test_pickling.cpp:#if !defined(PYPY_VERSION)
      1 tests/test_pytypes.cpp:#if (defined(__APPLE__) && defined(__clang__)) || defined(PYPY_VERSION)
      1 tests/test_smart_ptr.cpp:    m.def("make_myobject3_1", []() { return new MyObject3(8); });
      1 tests/test_smart_ptr.py:    assert cstats.values() == ["MyObject3[9]", "MyObject3[8]", "MyObject3[9]"]
      1 tests/test_stl_binders.py:    assert v_int2 == m.VectorInt([0, 99, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 88])
      1 tests/test_stl_binders.py:    assert v_int2 == m.VectorInt([0, 99, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 88, 4])
      1 tests/test_type_caster_pyobject_ptr.cpp:#if !defined(PYPY_VERSION) // It is not worth the trouble doing something special for PyPy.
      1 tools/FindPythonLibsNew.cmake:  set(PythonLibsNew_FIND_VERSION "3.8")
      1 tools/JoinPaths.cmake:# https://docs.python.org/3.7/library/os.path.html#os.path.join
      1 tools/pybind11NewTools.cmake:    Python 3.8 REQUIRED COMPONENTS ${_pybind11_interp_component} ${_pybind11_dev_component}
      1 tools/pybind11NewTools.cmake:# Python debug libraries expose slightly different objects before 3.8
      1 tools/pybind11Tools.cmake:    "3.12;3.11;3.10;3.9;3.8"
      1 tools/pybind11Tools.cmake:    if(NOT DEFINED PYPY_VERSION)
      1 tools/pybind11Tools.cmake:    message(STATUS "PYPY ${PYPY_VERSION} (Py ${PYTHON_VERSION})")
      1 tools/pybind11Tools.cmake:# Python debug libraries expose slightly different objects before 3.8
      1 tools/pybind11Tools.cmake:      set(PYPY_VERSION
```

* Change `[tool.ruff]` `target-version` to `"py38"`, as suggested by @Skylion007
2024-07-30 09:18:35 -07:00
Ralf W. Grosse-Kunstleve
2e260b067f
clang-tidy upgrade (to version 18) (#5272)
* `container: silkeh/clang:18-bookworm` in .github/workflows/format.yml

* clang-tidy auto-fix (trivial, in test only)

* Disable `performance-enum-size` (noisy, low value)

* Temporarily turn off 3 diagnostics (to be tackled one-by-one).

* Add explicit `switch` `default` to resolve clang-tidy `bugprone-switch-missing-default-case`

Debian clang version 18.1.8 (++20240718080534+3b5b5c1ec4a3-1~exp1~20240718200641.143)
Target: x86_64-pc-linux-gnu

tests/test_numpy_dtypes.cpp:212:5: warning: switching on non-enum value without default case may not cover all cases [bugprone-switch-missing-default-case]

* Add clang-17 and clang-18 testing.

* Add `NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange)` in test_tagbased_polymorphic.cpp

Debian clang version 18.1.8 (++20240718080534+3b5b5c1ec4a3-1~exp1~20240718200641.143)
Target: x86_64-pc-linux-gnu

tests/test_tagbased_polymorphic.cpp:77:40: warning: The value '150' provided to the cast expression is not in the valid range of values for 'Kind' [clang-analyzer-optin.core.EnumCastOutOfRange]

* Fix inconsistent pybind11/eigen/tensor.h behavior:

This existing comment in pybind11/eigen/tensor.h

```
                // move, take_ownership don't make any sense for a ref/map:
```

is at odds with the `delete src;` three lines up.

In real-world client code `take_ownership` will not exist (unless the client code is untested and unused). I.e. the `delete` is essentially only useful to avoid leaks in the pybind11 unit tests.

While upgrading to clang-tidy 18, the warning below appeared. Apparently it is produced during LTO, and it appears difficult to suppress. Regardless, the best way to resolve this is to remove the `delete` and to simply make the test objects `static` in the unit test code.

________

Debian clang version 18.1.8 (++20240718080534+3b5b5c1ec4a3-1~exp1~20240718200641.143)
Target: x86_64-pc-linux-gnu
________

```
lto-wrapper: warning: using serial compilation of 3 LTRANS jobs
lto-wrapper: note: see the ‘-flto’ option documentation for more information
In function ‘cast_impl’,
    inlined from ‘cast’ at /mounted_pybind11/include/pybind11/eigen/tensor.h:414:25,
    inlined from ‘operator()’ at /mounted_pybind11/include/pybind11/eigen/../pybind11.h:296:40,
    inlined from ‘_FUN’ at /mounted_pybind11/include/pybind11/eigen/../pybind11.h:267:21:
/mounted_pybind11/include/pybind11/eigen/tensor.h:475:17: warning: ‘operator delete’ called on unallocated object ‘<anonymous>’ [-Wfree-nonheap-object]
  475 |                 delete src;
      |                 ^
/mounted_pybind11/include/pybind11/eigen/../pybind11.h: In function ‘_FUN’:
/mounted_pybind11/include/pybind11/eigen/../pybind11.h:297:75: note: declared here
  297 |                     std::move(args_converter).template call<Return, Guard>(cap->f),
      |                                                                           ^
```

* Disable `bugprone-chained-comparison`: this clang-tidy check is incompatible with the Catch2 `REQUIRE` macro (26 warnings like the one below).
________

Debian clang version 18.1.8 (++20240718080534+3b5b5c1ec4a3-1~exp1~20240718200641.143)
Target: x86_64-pc-linux-gnu
________

```
/mounted_pybind11/tests/test_embed/test_interpreter.cpp:127:9: warning: chained comparison 'v0 <= v1 == v2' may generate unintended results, use parentheses to specify order of evaluation or a logical operator to separate comparison expressions [bugprone-chained-comparison]
  127 |         REQUIRE(ret == 42);
      |         ^
/build/tests/catch/catch.hpp:17670:24: note: expanded from macro 'REQUIRE'
 17670 | #define REQUIRE( ... ) INTERNAL_CATCH_TEST( "REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__  )
       |                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/build/tests/catch/catch.hpp:2710:47: note: expanded from macro 'INTERNAL_CATCH_TEST'
 2710 |             catchAssertionHandler.handleExpr( Catch::Decomposer() <= __VA_ARGS__ ); \
      |                                               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/mounted_pybind11/tests/test_embed/test_interpreter.cpp:127:9: note: operand 'v0' is here
  127 |         REQUIRE(ret == 42);
      |         ^
/build/tests/catch/catch.hpp:17670:24: note: expanded from macro 'REQUIRE'
 17670 | #define REQUIRE( ... ) INTERNAL_CATCH_TEST( "REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__  )
       |                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/build/tests/catch/catch.hpp:2710:47: note: expanded from macro 'INTERNAL_CATCH_TEST'
 2710 |             catchAssertionHandler.handleExpr( Catch::Decomposer() <= __VA_ARGS__ ); \
      |                                               ^~~~~~~~~~~~~~~~~~~
/mounted_pybind11/tests/test_embed/test_interpreter.cpp:127:17: note: operand 'v1' is here
  127 |         REQUIRE(ret == 42);
      |                 ^
/build/tests/catch/catch.hpp:17670:90: note: expanded from macro 'REQUIRE'
 17670 | #define REQUIRE( ... ) INTERNAL_CATCH_TEST( "REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__  )
       |                                                                                          ^~~~~~~~~~~
/build/tests/catch/catch.hpp:2710:70: note: expanded from macro 'INTERNAL_CATCH_TEST'
 2710 |             catchAssertionHandler.handleExpr( Catch::Decomposer() <= __VA_ARGS__ ); \
      |                                                                      ^~~~~~~~~~~
/mounted_pybind11/tests/test_embed/test_interpreter.cpp:127:24: note: operand 'v2' is here
  127 |         REQUIRE(ret == 42);
      |                        ^
/build/tests/catch/catch.hpp:17670:90: note: expanded from macro 'REQUIRE'
 17670 | #define REQUIRE( ... ) INTERNAL_CATCH_TEST( "REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__  )
       |                                                                                          ^~~~~~~~~~~
/build/tests/catch/catch.hpp:2710:70: note: expanded from macro 'INTERNAL_CATCH_TEST'
 2710 |             catchAssertionHandler.handleExpr( Catch::Decomposer() <= __VA_ARGS__ ); \
      |                                                                      ^~~~~~~~~~~
```

* Add 8 `// NOLINT(bugprone-empty-catch)`

* Resolve clang-tidy `bugprone-multi-level-implicit-pointer-conversion` warnings.
________
Debian clang version 18.1.8 (++20240718080534+3b5b5c1ec4a3-1~exp1~20240718200641.143)
Target: x86_64-pc-linux-gnu
________
```
pybind11/detail/internals.h:556:53: warning: multilevel pointer conversion from 'internals **' to 'const void *', please use explicit cast [bugprone-multi-level-implicit-pointer-conversion]

pybind11/detail/type_caster_base.h:431:20: warning: multilevel pointer conversion from 'void **' to 'void *', please use explicit cast [bugprone-multi-level-implicit-pointer-conversion]

pybind11/numpy.h:904:81: warning: multilevel pointer conversion from '_object *const *' to 'const void *', please use explicit cast [bugprone-multi-level-implicit-pointer-conversion]
pybind11/numpy.h:1989:39: warning: multilevel pointer conversion from 'typename vectorize_arg<const double *>::type *' (aka 'const double **') to 'void *', please use explicit cast [bugprone-multi-level-implicit-pointer-conversion]
pybind11/numpy.h:1989:39: warning: multilevel pointer conversion from 'typename vectorize_arg<const VectorizeTestClass *>::type *' (aka 'const VectorizeTestClass **') to 'void *', please use explicit cast [bugprone-multi-level-implicit-pointer-conversion]

pybind11/stl/filesystem.h:75:44: warning: multilevel pointer conversion from 'PyObject **' (aka '_object **') to 'void *', please use explicit cast [bugprone-multi-level-implicit-pointer-conversion]
pybind11/stl/filesystem.h:83:42: warning: multilevel pointer conversion from 'PyObject **' (aka '_object **') to 'void *', please use explicit cast [bugprone-multi-level-implicit-pointer-conversion]
```

* Introduce `PYBIND11_REINTERPRET_CAST_VOID_PTR_IF_NOT_PYPY` to resolve PyPy build errors:

```
In file included from /Users/runner/work/pybind11/pybind11/tests/test_stl.cpp:18:
/Users/runner/work/pybind11/pybind11/include/pybind11/stl/filesystem.h:75:17: error: no matching function for call to 'PyPyUnicode_FSConverter'
            if (PyUnicode_FSConverter(buf, reinterpret_cast<void *>(&native)) != 0) {
                ^~~~~~~~~~~~~~~~~~~~~
/Users/runner/hostedtoolcache/PyPy/3.10.14/x64/include/pypy3.10/pypy_decl.h:969:31: note: expanded from macro 'PyUnicode_FSConverter'
                              ^~~~~~~~~~~~~~~~~~~~~~~
/Users/runner/hostedtoolcache/PyPy/3.10.14/x64/include/pypy3.10/pypy_decl.h:970:17: note: candidate function not viable: cannot convert argument of incomplete type 'void *' to 'struct _object **' for 2nd argument
PyAPI_FUNC(int) PyUnicode_FSConverter(struct _object *arg0, struct _object **arg1);
                ^
/Users/runner/hostedtoolcache/PyPy/3.10.14/x64/include/pypy3.10/pypy_decl.h:969:31: note: expanded from macro 'PyUnicode_FSConverter'
                              ^
In file included from /Users/runner/work/pybind11/pybind11/tests/test_stl.cpp:18:
/Users/runner/work/pybind11/pybind11/include/pybind11/stl/filesystem.h:83:17: error: no matching function for call to 'PyPyUnicode_FSDecoder'
            if (PyUnicode_FSDecoder(buf, reinterpret_cast<void *>(&native)) != 0) {
                ^~~~~~~~~~~~~~~~~~~
/Users/runner/hostedtoolcache/PyPy/3.10.14/x64/include/pypy3.10/pypy_decl.h:971:29: note: expanded from macro 'PyUnicode_FSDecoder'
                            ^~~~~~~~~~~~~~~~~~~~~
/Users/runner/hostedtoolcache/PyPy/3.10.14/x64/include/pypy3.10/pypy_decl.h:972:17: note: candidate function not viable: cannot convert argument of incomplete type 'void *' to 'struct _object **' for 2nd argument
PyAPI_FUNC(int) PyUnicode_FSDecoder(struct _object *arg0, struct _object **arg1);
                ^
/Users/runner/hostedtoolcache/PyPy/3.10.14/x64/include/pypy3.10/pypy_decl.h:971:29: note: expanded from macro 'PyUnicode_FSDecoder'
                            ^
```

* clang-tidy auto-fix

* Fix silly oversight.
2024-07-29 11:10:03 -07:00
Theodore Tsirpanis
8e7307f069
docs: remove outdated known limitation. (#5263)
* Fix typo.

* Remove outdated known limitation.

See #5179.
2024-07-23 22:32:39 -04:00
Ralf W. Grosse-Kunstleve
6d4805ced1
Small cleanup/refactoring in support of PR #5213 (#5251)
* Factor out detail/value_and_holder.h (from detail/type_caster_base.h)

This is in support of PR #5213:

* trampoline_self_life_support.h depends on value_and_holder.h

* type_caster_base.h depends on trampoline_self_life_support.h

* Fix a minor and inconsequential inconsistency in `copyable_holder_caster`: the correct `load_value()` return type is `void` (as defined in `type_caster_generic`)

For easy future reference, this is the long-standing inconsistency:

* dbf848aff7/include/pybind11/detail/type_caster_base.h (L634)

* dbf848aff7/include/pybind11/cast.h (L797)

Noticed in passing while working on PR #5213.

* Add `DANGER ZONE` comment in detail/init.h, similar to a comment added on the smart_holder branch (all the way back in 2021).
2024-07-18 17:34:06 -07:00
Henry Schreiner
a582ca8a8e
tests: run on pyodide (#4745)
* tests: run on pyodide

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* ci: use cibuildwheel for pyodide test

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* tests: revert changes to test_embed

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2024-07-18 14:50:38 -04:00
Ralf Gommers
dbf848aff7
docs: extend PYBIND11_MODULE documentation, mention mod_gil_not_used (#5250)
This follows up on PR 5148, which introduced support for free-threaded CPython.
2024-07-17 23:26:45 -07:00
Sam Gross
43de8014f9
fix: make gil_safe_call_once thread-safe in free-threaded CPython (#5246)
* fix: Make gil_safe_call_once thread-safe in free-threaded CPython

The "is_initialized_" flags is not protected by the GIL in free-threaded
Python, so it needs to be an atomic field.

Fixes #5245

* style: pre-commit fixes

* Apply changes from code review

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-07-16 11:06:54 -07:00
dependabot[bot]
ccefee4c31
chore(deps): bump actions/attest-build-provenance in the actions group (#5243)
Bumps the actions group with 1 update: [actions/attest-build-provenance](https://github.com/actions/attest-build-provenance).


Updates `actions/attest-build-provenance` from 1.3.2 to 1.3.3
- [Release notes](https://github.com/actions/attest-build-provenance/releases)
- [Changelog](https://github.com/actions/attest-build-provenance/blob/main/RELEASE.md)
- [Commits](bdd51370e0...5e9cb68e95)

---
updated-dependencies:
- dependency-name: actions/attest-build-provenance
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-15 16:51:27 -04:00
dependabot[bot]
50acb81b0a
chore(deps): bump certifi from 2024.2.2 to 2024.7.4 in /docs (#5226)
Bumps [certifi](https://github.com/certifi/python-certifi) from 2024.2.2 to 2024.7.4.
- [Commits](https://github.com/certifi/python-certifi/compare/2024.02.02...2024.07.04)

---
updated-dependencies:
- dependency-name: certifi
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-06 12:22:52 -07:00
Sam Gross
bb05e0810b
Use PyMutex instead of std::mutex in free-threaded build. (#5219)
* Use PyMutex instead of std::mutex in free-threaded build.

PyMutex is now part of the public C API as of 3.13.0b3 and generally has
slightly less overhead than std::mutex.

* style: pre-commit fixes

* Fix instance_map_shard padding

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-07-02 09:58:09 -07:00
pre-commit-ci[bot]
b21b049029
chore(deps): update pre-commit hooks (#5220)
updates:
- [github.com/pre-commit/mirrors-clang-format: v18.1.5 → v18.1.8](https://github.com/pre-commit/mirrors-clang-format/compare/v18.1.5...v18.1.8)
- [github.com/astral-sh/ruff-pre-commit: v0.4.7 → v0.5.0](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.7...v0.5.0)
- [github.com/pre-commit/mirrors-mypy: v1.10.0 → v1.10.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.10.0...v1.10.1)
- [github.com/adamchainz/blacken-docs: 1.16.0 → 1.18.0](https://github.com/adamchainz/blacken-docs/compare/1.16.0...1.18.0)
- [github.com/PyCQA/pylint: v3.2.2 → v3.2.4](https://github.com/PyCQA/pylint/compare/v3.2.2...v3.2.4)
- [github.com/python-jsonschema/check-jsonschema: 0.28.4 → 0.28.6](https://github.com/python-jsonschema/check-jsonschema/compare/0.28.4...0.28.6)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-07-01 23:03:52 -07:00
dependabot[bot]
d78446cc2b
chore(deps): bump actions/attest-build-provenance in the actions group (#5216)
Bumps the actions group with 1 update: [actions/attest-build-provenance](https://github.com/actions/attest-build-provenance).


Updates `actions/attest-build-provenance` from 1.1.2 to 1.3.2
- [Release notes](https://github.com/actions/attest-build-provenance/releases)
- [Changelog](https://github.com/actions/attest-build-provenance/blob/main/RELEASE.md)
- [Commits](173725a120...bdd51370e0)

---
updated-dependencies:
- dependency-name: actions/attest-build-provenance
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-30 23:32:03 -04:00
Michael Carlstrom
d805e9967f
feat(types) Adds special Case for empty C++ tuple type annotation (#5214)
* add special case and unit test

* add newline
2024-06-30 09:52:37 -07:00
wenqing
51c2aa16de
Fixed a compilation error with gcc 14 (#5208) 2024-06-28 07:12:32 -07:00
Michael Carlstrom
08f946a431
fix: add guard for GCC <10.3 on C++20 (#5205)
* Update CI

* update define guard

* style: pre-commit fixes

* updated define guard

* style: pre-commit fixes

* update guard

* testing new guards

* update guards

* surely this time

* style: pre-commit fixes

* Define PYBIND11_TYPING_H_HAS_STRING_LITERAL to avoid repeating a complex expression.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Ralf W. Grosse-Kunstleve <rwgk@google.com>
2024-06-27 22:20:28 -07:00
Varun Agrawal
e0f9e77466
fix(cmake): remove extra = in flto assignment (#5207) 2024-06-27 22:26:09 -04:00
Henry Schreiner
57287b578d
docs: prepare for 2.13.1 (#5203)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2024-06-26 18:14:17 -04:00
Michael Carlstrom
4bd538a40a
feat(types): add support for Typing.Callable Special Case (#5202)
* Add special case

* linty
2024-06-26 16:34:06 -04:00
Sam Gross
2e35470cff
fix: use manual padding of instance_map_shard (#5200)
* Use manual padding of instance_map_shard.

The alignas(64) specifier requires aligned allocation, which is not
available on macOS when targeting versions before 10.14.

* Add 'see #5200'

* Update include/pybind11/detail/internals.h

* Update include/pybind11/detail/internals.h

---------

Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>
2024-06-26 15:46:46 -04:00
Henry Schreiner
895e657220
chore: back to work
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2024-06-26 00:42:54 -04:00
Henry Schreiner
0c69e1eb21
chore: prepare for 2.13.0 (#5198)
* chore: prepare for 2.13.0

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* Update changelog.rst

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2024-06-25 23:51:27 -04:00
Michael Carlstrom
2be85c6041
feat(types): adds support for TypeGuard and TypeIs (#5194)
* Adds support for TypeGuard and TypeIs

* style: pre-commit fixes

---------

Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-06-25 22:53:15 -04:00
Henry Schreiner
b5ec7c7174
ci: release with trusted publisher and attestations (#5196)
* ci: release with trusted publisher and attestations

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* Update pip.yml

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2024-06-25 21:12:58 -04:00
Michael Carlstrom
26281c7986
feat(types): adds support for Never and NoReturn from python Typing (#5193)
* Adds support for Never and NoReturn

* lint
2024-06-25 21:10:25 -04:00
Michael Carlstrom
183059f9a4
feat(types): add support for typing.Literal type (#5192)
* typevar prototype

* style: pre-commit fixes

* change to NameT

* style: pre-commit fixes

* make string const

* add missing closing bracket

* style: pre-commit fixes

* clean up handle_type_name

* style: pre-commit fixes

* add back missing <

* style: pre-commit fixes

* add back NameT

* try fixed_string

* style: pre-commit fixes

* std::basic_fixed_string

* test c++20

* style: pre-commit fixes

* cleanup

* fix object to typevar conversion

* style: pre-commit fixes

* And CPP20 checks

* style: pre-commit fixes

* add missing cpp20++ check

* style: pre-commit fixes

* Add C++20 check to python

* Fix python if {

* style: pre-commit fixes

* update test name

* style: pre-commit fixes

* remove call on cpp_std

* make field const

* test nontype_template

* update feature check

* update name of guard

* fix try except in test

* fix pre commit

* remove extra semi colon

* except AttributeError

* fix try except in test

* remove const

* Clean up tests

* style: pre-commit fixes

* start string literal

* start int literal

* func declare

* commit clean

* use contextlib.suppres

* resolve stash

* more literal type

* fix annotation name

* stash

* request changes

* lint

* Add comments

* style: pre-commit fixes

* Add support for unions and optionals to be compatible with object

* lint

* remove comment

* Create Literal Type implementation

* clean up

* Update comment

* remove incorrect comment

* rerun CI

* rerun CI

* fix extra line

* lint

* move if defined block

* style: pre-commit fixes

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-06-25 17:57:34 -04:00
Michael Carlstrom
aebcd704d2
Add TypeVars / method generics typing (#5167)
* typevar prototype

* style: pre-commit fixes

* change to NameT

* style: pre-commit fixes

* make string const

* add missing closing bracket

* style: pre-commit fixes

* clean up handle_type_name

* style: pre-commit fixes

* add back missing <

* style: pre-commit fixes

* add back NameT

* try fixed_string

* style: pre-commit fixes

* std::basic_fixed_string

* test c++20

* style: pre-commit fixes

* cleanup

* fix object to typevar conversion

* style: pre-commit fixes

* And CPP20 checks

* style: pre-commit fixes

* add missing cpp20++ check

* style: pre-commit fixes

* Add C++20 check to python

* Fix python if {

* style: pre-commit fixes

* update test name

* style: pre-commit fixes

* remove call on cpp_std

* make field const

* test nontype_template

* update feature check

* update name of guard

* fix try except in test

* fix pre commit

* remove extra semi colon

* except AttributeError

* fix try except in test

* remove const

* Clean up tests

* style: pre-commit fixes

* use contextlib.suppres

* request changes

* lint

* Add comments

* style: pre-commit fixes

* Add support for unions and optionals to be compatible with object

* lint

* remove comment

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-06-25 16:25:40 -04:00
Henry Schreiner
9ec64e37c3
docs: prepare for 2.13.0 (#5187)
* docs: prepare for 2.13.0

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* docs: reword cross-compiling entry

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* Update changelog.rst

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2024-06-25 16:24:54 -04:00
Ralf W. Grosse-Kunstleve
a406a62e5b
fix: use std::addressof in type_caster_base.h (#5189)
* Add tests only. Fails to build with this error message:

```
g++ -o pybind11/tests/test_copy_move.os -c -std=c++17 -fPIC -fvisibility=hidden -O0 -g -Wall -Wextra -Wconversion -Wcast-qual -Wdeprecated -Wundef -Wnon-virtual-dtor -Wunused-result -Werror -isystem /usr/include/python3.11 -isystem /usr/include/eigen3 -DPYBIND11_STRICT_ASSERTS_CLASS_HOLDER_VS_TYPE_CASTER_MIX -DPYBIND11_ENABLE_TYPE_CASTER_ODR_GUARD_IF_AVAILABLE -DPYBIND11_TEST_BOOST -DPYBIND11_INTERNALS_VERSION=10000000 -Ipybind11/include -I/usr/local/google/home/rwgk/forked/pybind11/include -I/usr/local/google/home/rwgk/clone/pybind11/include /usr/local/google/home/rwgk/forked/pybind11/tests/test_copy_move.cpp
In file included from /usr/local/google/home/rwgk/forked/pybind11/include/pybind11/detail/../cast.h:15,
                 from /usr/local/google/home/rwgk/forked/pybind11/include/pybind11/detail/../attr.h:14,
                 from /usr/local/google/home/rwgk/forked/pybind11/include/pybind11/detail/class.h:12,
                 from /usr/local/google/home/rwgk/forked/pybind11/include/pybind11/pybind11.h:13,
                 from /usr/local/google/home/rwgk/forked/pybind11/include/pybind11/stl.h:12,
                 from /usr/local/google/home/rwgk/forked/pybind11/tests/test_copy_move.cpp:11:
/usr/local/google/home/rwgk/forked/pybind11/include/pybind11/detail/../detail/type_caster_base.h: In instantiation of ‘static pybind11::handle pybind11::detail::type_caster_base<type>::cast(const itype&, pybind11::return_value_policy, pybind11::handle) [with type = UnusualOpRef; itype = UnusualOpRef]’:
/usr/local/google/home/rwgk/forked/pybind11/include/pybind11/detail/../cast.h:1230:37:   required from ‘pybind11::object pybind11::cast(T&&, return_value_policy, handle) [with T = const UnusualOpRef&; typename std::enable_if<(! std::is_base_of<detail::pyobject_tag, typename std::remove_reference<_Tp>::type>::value), int>::type <anonymous> = 0]’
/usr/local/google/home/rwgk/forked/pybind11/tests/test_copy_move.cpp:162:80:   required from here
/usr/local/google/home/rwgk/forked/pybind11/include/pybind11/detail/../detail/type_caster_base.h:1110:20: error: no matching function for call to ‘pybind11::detail::type_caster_base<UnusualOpRef>::cast(const UnusualOpRef::NonTrivialType, pybind11::return_value_policy&, pybind11::handle&)’
 1110 |         return cast(&src, policy, parent);
      |                ~~~~^~~~~~~~~~~~~~~~~~~~~~
/usr/local/google/home/rwgk/forked/pybind11/include/pybind11/detail/../detail/type_caster_base.h:1105:19: note: candidate: ‘static pybind11::handle pybind11::detail::type_caster_base<type>::cast(const itype&, pybind11::return_value_policy, pybind11::handle) [with type = UnusualOpRef; itype = UnusualOpRef]’
 1105 |     static handle cast(const itype &src, return_value_policy policy, handle parent) {
      |                   ^~~~
/usr/local/google/home/rwgk/forked/pybind11/include/pybind11/detail/../detail/type_caster_base.h:1105:37: note:   no known conversion for argument 1 from ‘const UnusualOpRef::NonTrivialType’ {aka ‘const std::shared_ptr<int>’} to ‘const pybind11::detail::type_caster_base<UnusualOpRef>::itype&’ {aka ‘const UnusualOpRef&’}
 1105 |     static handle cast(const itype &src, return_value_policy policy, handle parent) {
      |                        ~~~~~~~~~~~~~^~~
/usr/local/google/home/rwgk/forked/pybind11/include/pybind11/detail/../detail/type_caster_base.h:1113:19: note: candidate: ‘static pybind11::handle pybind11::detail::type_caster_base<type>::cast(itype&&, pybind11::return_value_policy, pybind11::handle) [with type = UnusualOpRef; itype = UnusualOpRef]’
 1113 |     static handle cast(itype &&src, return_value_policy, handle parent) {
      |                   ^~~~
/usr/local/google/home/rwgk/forked/pybind11/include/pybind11/detail/../detail/type_caster_base.h:1113:32: note:   no known conversion for argument 1 from ‘const UnusualOpRef::NonTrivialType’ {aka ‘const std::shared_ptr<int>’} to ‘pybind11::detail::type_caster_base<UnusualOpRef>::itype&&’ {aka ‘UnusualOpRef&&’}
 1113 |     static handle cast(itype &&src, return_value_policy, handle parent) {
      |                        ~~~~~~~~^~~
/usr/local/google/home/rwgk/forked/pybind11/include/pybind11/detail/../detail/type_caster_base.h:1142:19: note: candidate: ‘static pybind11::handle pybind11::detail::type_caster_base<type>::cast(const itype*, pybind11::return_value_policy, pybind11::handle) [with type = UnusualOpRef; itype = UnusualOpRef]’
 1142 |     static handle cast(const itype *src, return_value_policy policy, handle parent) {
      |                   ^~~~
/usr/local/google/home/rwgk/forked/pybind11/include/pybind11/detail/../detail/type_caster_base.h:1142:37: note:   no known conversion for argument 1 from ‘const UnusualOpRef::NonTrivialType’ {aka ‘const std::shared_ptr<int>’} to ‘const pybind11::detail::type_caster_base<UnusualOpRef>::itype*’ {aka ‘const UnusualOpRef*’}
 1142 |     static handle cast(const itype *src, return_value_policy policy, handle parent) {
      |                        ~~~~~~~~~~~~~^~~
/usr/local/google/home/rwgk/forked/pybind11/include/pybind11/detail/../detail/type_caster_base.h: In instantiation of ‘static pybind11::handle pybind11::detail::type_caster_base<type>::cast(itype&&, pybind11::return_value_policy, pybind11::handle) [with type = UnusualOpRef; itype = UnusualOpRef]’:
/usr/local/google/home/rwgk/forked/pybind11/include/pybind11/detail/../cast.h:1230:37:   required from ‘pybind11::object pybind11::cast(T&&, return_value_policy, handle) [with T = UnusualOpRef; typename std::enable_if<(! std::is_base_of<detail::pyobject_tag, typename std::remove_reference<_Tp>::type>::value), int>::type <anonymous> = 0]’
/usr/local/google/home/rwgk/forked/pybind11/tests/test_copy_move.cpp:163:74:   required from here
/usr/local/google/home/rwgk/forked/pybind11/include/pybind11/detail/../detail/type_caster_base.h:1114:20: error: no matching function for call to ‘pybind11::detail::type_caster_base<UnusualOpRef>::cast(UnusualOpRef::NonTrivialType, pybind11::return_value_policy, pybind11::handle&)’
 1114 |         return cast(&src, return_value_policy::move, parent);
      |                ~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/local/google/home/rwgk/forked/pybind11/include/pybind11/detail/../detail/type_caster_base.h:1105:19: note: candidate: ‘static pybind11::handle pybind11::detail::type_caster_base<type>::cast(const itype&, pybind11::return_value_policy, pybind11::handle) [with type = UnusualOpRef; itype = UnusualOpRef]’
 1105 |     static handle cast(const itype &src, return_value_policy policy, handle parent) {
      |                   ^~~~
/usr/local/google/home/rwgk/forked/pybind11/include/pybind11/detail/../detail/type_caster_base.h:1105:37: note:   no known conversion for argument 1 from ‘UnusualOpRef::NonTrivialType’ {aka ‘std::shared_ptr<int>’} to ‘const pybind11::detail::type_caster_base<UnusualOpRef>::itype&’ {aka ‘const UnusualOpRef&’}
 1105 |     static handle cast(const itype &src, return_value_policy policy, handle parent) {
      |                        ~~~~~~~~~~~~~^~~
/usr/local/google/home/rwgk/forked/pybind11/include/pybind11/detail/../detail/type_caster_base.h:1113:19: note: candidate: ‘static pybind11::handle pybind11::detail::type_caster_base<type>::cast(itype&&, pybind11::return_value_policy, pybind11::handle) [with type = UnusualOpRef; itype = UnusualOpRef]’
 1113 |     static handle cast(itype &&src, return_value_policy, handle parent) {
      |                   ^~~~
/usr/local/google/home/rwgk/forked/pybind11/include/pybind11/detail/../detail/type_caster_base.h:1113:32: note:   no known conversion for argument 1 from ‘UnusualOpRef::NonTrivialType’ {aka ‘std::shared_ptr<int>’} to ‘pybind11::detail::type_caster_base<UnusualOpRef>::itype&&’ {aka ‘UnusualOpRef&&’}
 1113 |     static handle cast(itype &&src, return_value_policy, handle parent) {
      |                        ~~~~~~~~^~~
/usr/local/google/home/rwgk/forked/pybind11/include/pybind11/detail/../detail/type_caster_base.h:1142:19: note: candidate: ‘static pybind11::handle pybind11::detail::type_caster_base<type>::cast(const itype*, pybind11::return_value_policy, pybind11::handle) [with type = UnusualOpRef; itype = UnusualOpRef]’
 1142 |     static handle cast(const itype *src, return_value_policy policy, handle parent) {
      |                   ^~~~
/usr/local/google/home/rwgk/forked/pybind11/include/pybind11/detail/../detail/type_caster_base.h:1142:37: note:   no known conversion for argument 1 from ‘UnusualOpRef::NonTrivialType’ {aka ‘std::shared_ptr<int>’} to ‘const pybind11::detail::type_caster_base<UnusualOpRef>::itype*’ {aka ‘const UnusualOpRef*’}
 1142 |     static handle cast(const itype *src, return_value_policy policy, handle parent) {
      |                        ~~~~~~~~~~~~~^~~
```

* Replace `&src` with `std::addressof(src)` to fix the error building the added tests.

* Fix accident (not sure how the `const` slipped in here when transferring the diff from pybind11k).
2024-06-24 08:59:55 -07:00
Ralf W. Grosse-Kunstleve
f1a2e03d19
feat: remove Python 3.6 support (#5177)
* Change Python version guard: PYTHON < 3.7 IS UNSUPPORTED.

* Replace or remove Python 3.6 jobs.

* Move appveyor to Python 3.8

* Change `[tool.pylint]` `master.py-version` from `3.6` to `3.8`

* Change `[tool.pylint]` `master.py-version` to `3.7`

* Remove `centos:7` job; Change almalinux:8 job to use Python 3.8

* Try 🐍 3.8 • ubuntu-20.04 • x64 without `-DCMAKE_CXX_FLAGS="-D_=1"`

* Update setup.cfg as suggested by @henryiii

* Try running `cmake --build . --target cpptest` on all platforms (`standard` job).

* Disable deadsnakes jobs entirely.

* Apply PR #5179: Add Python 3.10, 3.11, 3.12 to win32 job matrix.

* Add back `-DCMAKE_CXX_FLAGS="-D_=1"` but do not install boost in that case.

* PY_VERSION_HEX < 3.7 cleanup pass: include/pybind11

* WITH_THREAD cleanup pass: include/pybind11

* Undo incorrect change.

* Revert "Disable deadsnakes jobs entirely."

This reverts commit bbcd0087b2.

* WITH_THREAD cleanup pass: tests/

* Change Python version guard in pybind11/__init__.py: pybind11 does not support Python < 3.7.

* Misc cleanup pass

* chore: use future imports

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* Update tests/test_numpy_array.py

* Update test_numpy_array.py

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Henry Schreiner <henryschreineriii@gmail.com>
2024-06-22 00:55:00 -04:00
Ralf W. Grosse-Kunstleve
5552cbf205
Add Python 3.10, 3.11, 3.12 to win32 job matrix. (#5179)
* Add Python 3.10, 3.11, 3.12 to win32 job matrix.

* Quotes around Python version numbers (win32 job).

* tests/requirements.txt: no scipy for Python >= 3.10 under win32

* Try running `cmake --build . --target cpptest` on all platforms (`standard` job).
2024-06-21 07:41:17 -07:00
Henry Schreiner
186df220fd
docs: building suggestions update (#5168)
* docs: building suggestions update

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* docs: address review comments

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2024-06-18 17:41:54 -04:00
Sam Gross
baa540ec34
fix: support free-threaded CPython with GIL disabled (#5148)
* Support free-threaded CPython (PEP 703)

Some additional locking is added in the free-threaded build when
`Py_GIL_DISABLED` is defined:

- Most accesses to internals are protected by a single mutex
- The registered_instances uses a striped lock to improve concurrency

Pybind11 modules can indicate support for running with the GIL disabled
by calling `set_gil_not_used()`.

* refactor: use PYBIND11_MODULE (#11)

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* Address code review

* Suppress MSVC warning

* Changes from review

* style: pre-commit fixes

* `py::mod_gil_not_used()` suggestion.

* Update include/pybind11/pybind11.h

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Ralf W. Grosse-Kunstleve <rwgk@google.com>
2024-06-18 13:54:38 -07:00
dependabot[bot]
1961b96a8c
chore(deps): bump urllib3 from 2.2.1 to 2.2.2 in /docs (#5170)
Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.2.1 to 2.2.2.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/2.2.1...2.2.2)

---
updated-dependencies:
- dependency-name: urllib3
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-18 00:03:42 -07:00
dependabot[bot]
fdd20d69c7
chore(deps): bump seanmiddleditch/gha-setup-ninja in the actions group (#5169)
Bumps the actions group with 1 update: [seanmiddleditch/gha-setup-ninja](https://github.com/seanmiddleditch/gha-setup-ninja).


Updates `seanmiddleditch/gha-setup-ninja` from 4 to 5
- [Release notes](https://github.com/seanmiddleditch/gha-setup-ninja/releases)
- [Commits](https://github.com/seanmiddleditch/gha-setup-ninja/compare/v4...v5)

---
updated-dependencies:
- dependency-name: seanmiddleditch/gha-setup-ninja
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-16 20:59:19 -07:00
Michael Carlstrom
7c4ac91d75
Add type[T] support to typing.h (#5166)
* add type[T]

* style: pre-commit fixes

* fix merge

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-06-15 06:25:43 -07:00
Michael Carlstrom
68405a1141
Add Union and Optional to typing.h (#5165)
* add type unions and optionals

* add type inheritance

* style: pre-commit fixes

* switch to inheriting from object

* style: pre-commit fixes

* fix text case

* style: pre-commit fixes

* fix bind call

* fix function name

* add std::move for older code

* remove std::move

* move away from object creation

* style: pre-commit fixes

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-06-14 21:16:26 -07:00
Mana
5f3d8d806a demo race when using call_guard<gil_scoped_release> and implicitly_convertible 2020-12-07 18:59:27 +08:00
169 changed files with 3563 additions and 1111 deletions

View File

@ -9,7 +9,7 @@ platform:
- x86 - x86
environment: environment:
matrix: matrix:
- PYTHON: 36 - PYTHON: 38
CONFIG: Debug CONFIG: Debug
install: install:
- ps: | - ps: |

View File

@ -57,10 +57,12 @@ Checks: |
readability-string-compare, readability-string-compare,
readability-suspicious-call-argument, readability-suspicious-call-argument,
readability-uniqueptr-delete-release, readability-uniqueptr-delete-release,
-bugprone-chained-comparison,
-bugprone-easily-swappable-parameters, -bugprone-easily-swappable-parameters,
-bugprone-exception-escape, -bugprone-exception-escape,
-bugprone-reserved-identifier, -bugprone-reserved-identifier,
-bugprone-unused-raii, -bugprone-unused-raii,
-performance-enum-size,
CheckOptions: CheckOptions:
- key: modernize-use-equals-default.IgnoreMacros - key: modernize-use-equals-default.IgnoreMacros

View File

@ -81,7 +81,7 @@ nox -s build
### Full setup ### Full setup
To setup an ideal development environment, run the following commands on a To setup an ideal development environment, run the following commands on a
system with CMake 3.14+: system with CMake 3.15+:
```bash ```bash
python3 -m venv venv python3 -m venv venv
@ -96,8 +96,8 @@ Tips:
* You can use `virtualenv` (faster, from PyPI) instead of `venv`. * You can use `virtualenv` (faster, from PyPI) instead of `venv`.
* You can select any name for your environment folder; if it contains "env" it * You can select any name for your environment folder; if it contains "env" it
will be ignored by git. will be ignored by git.
* If you don't have CMake 3.14+, just add "cmake" to the pip install command. * If you don't have CMake 3.15+, just add "cmake" to the pip install command.
* You can use `-DPYBIND11_FINDPYTHON=ON` to use FindPython on CMake 3.12+ * You can use `-DPYBIND11_FINDPYTHON=ON` to use FindPython.
* In classic mode, you may need to set `-DPYTHON_EXECUTABLE=/path/to/python`. * In classic mode, you may need to set `-DPYTHON_EXECUTABLE=/path/to/python`.
FindPython uses `-DPython_ROOT_DIR=/path/to` or FindPython uses `-DPython_ROOT_DIR=/path/to` or
`-DPython_EXECUTABLE=/path/to/python`. `-DPython_EXECUTABLE=/path/to/python`.
@ -149,8 +149,8 @@ To run the tests, you can "build" the check target:
cmake --build build --target check cmake --build build --target check
``` ```
`--target` can be spelled `-t` in CMake 3.15+. You can also run individual `--target` can be spelled `-t`. You can also run individual tests with these
tests with these targets: targets:
* `pytest`: Python tests only, using the * `pytest`: Python tests only, using the
[pytest](https://docs.pytest.org/en/stable/) framework [pytest](https://docs.pytest.org/en/stable/) framework

View File

@ -32,7 +32,7 @@ jobs:
matrix: matrix:
runs-on: [ubuntu-20.04, windows-2022, macos-13] runs-on: [ubuntu-20.04, windows-2022, macos-13]
python: python:
- '3.6' - '3.8'
- '3.9' - '3.9'
- '3.12' - '3.12'
- '3.13' - '3.13'
@ -48,16 +48,17 @@ jobs:
include: include:
# Just add a key # Just add a key
- runs-on: ubuntu-20.04 - runs-on: ubuntu-20.04
python: '3.6' python: '3.8'
args: > args: >
-DPYBIND11_FINDPYTHON=ON -DPYBIND11_FINDPYTHON=ON
-DCMAKE_CXX_FLAGS="-D_=1" -DCMAKE_CXX_FLAGS="-D_=1"
exercise_D_: 1
- runs-on: ubuntu-20.04 - runs-on: ubuntu-20.04
python: 'pypy-3.8' python: 'pypy-3.8'
args: > args: >
-DPYBIND11_FINDPYTHON=ON -DPYBIND11_FINDPYTHON=ON
- runs-on: windows-2019 - runs-on: windows-2019
python: '3.6' python: '3.8'
args: > args: >
-DPYBIND11_FINDPYTHON=ON -DPYBIND11_FINDPYTHON=ON
# Inject a couple Windows 2019 runs # Inject a couple Windows 2019 runs
@ -82,7 +83,7 @@ jobs:
- name: Setup Boost (Linux) - name: Setup Boost (Linux)
# Can't use boost + define _ # Can't use boost + define _
if: runner.os == 'Linux' && matrix.python != '3.6' if: runner.os == 'Linux' && matrix.exercise_D_ != 1
run: sudo apt-get install libboost-dev run: sudo apt-get install libboost-dev
- name: Setup Boost (macOS) - name: Setup Boost (macOS)
@ -133,9 +134,7 @@ jobs:
run: cmake --build . --target pytest -j 2 run: cmake --build . --target pytest -j 2
- name: C++11 tests - name: C++11 tests
# TODO: Figure out how to load the DLL on Python 3.8+ run: cmake --build . --target cpptest -j 2
if: "!(runner.os == 'Windows' && (matrix.python == 3.8 || matrix.python == 3.9 || matrix.python == '3.10' || matrix.python == '3.11' || matrix.python == 'pypy-3.8'))"
run: cmake --build . --target cpptest -j 2
- name: Interface test C++11 - name: Interface test C++11
run: cmake --build . --target test_cmake_build run: cmake --build . --target test_cmake_build
@ -164,8 +163,6 @@ jobs:
run: cmake --build build2 --target pytest run: cmake --build build2 --target pytest
- name: C++ tests - name: C++ tests
# TODO: Figure out how to load the DLL on Python 3.8+
if: "!(runner.os == 'Windows' && (matrix.python == 3.8 || matrix.python == 3.9 || matrix.python == '3.10' || matrix.python == '3.11' || matrix.python == 'pypy-3.8'))"
run: cmake --build build2 --target cpptest run: cmake --build build2 --target cpptest
# Third build - C++17 mode with unstable ABI # Third build - C++17 mode with unstable ABI
@ -246,7 +243,7 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Setup Python ${{ matrix.python-version }} (deadsnakes) - name: Setup Python ${{ matrix.python-version }} (deadsnakes)
uses: deadsnakes/action@v3.1.0 uses: deadsnakes/action@v3.2.0
with: with:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
debug: ${{ matrix.python-debug }} debug: ${{ matrix.python-debug }}
@ -314,11 +311,6 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
clang: clang:
- 3.6
- 3.7
- 3.9
- 7
- 9
- dev - dev
std: std:
- 11 - 11
@ -327,8 +319,6 @@ jobs:
include: include:
- clang: 5 - clang: 5
std: 14 std: 14
- clang: 10
std: 17
- clang: 11 - clang: 11
std: 20 std: 20
- clang: 12 - clang: 12
@ -343,6 +333,12 @@ jobs:
- clang: 16 - clang: 16
std: 20 std: 20
container_suffix: "-bullseye" container_suffix: "-bullseye"
- clang: 17
std: 20
container_suffix: "-bookworm"
- clang: 18
std: 20
container_suffix: "-bookworm"
name: "🐍 3 • Clang ${{ matrix.clang }} • C++${{ matrix.std }} • x64" name: "🐍 3 • Clang ${{ matrix.clang }} • C++${{ matrix.std }} • x64"
container: "silkeh/clang:${{ matrix.clang }}${{ matrix.container_suffix }}" container: "silkeh/clang:${{ matrix.clang }}${{ matrix.container_suffix }}"
@ -500,11 +496,9 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
- { gcc: 7, std: 11 } - { gcc: 9, std: 20 }
- { gcc: 7, std: 17 }
- { gcc: 8, std: 14 }
- { gcc: 8, std: 17 }
- { gcc: 10, std: 17 } - { gcc: 10, std: 17 }
- { gcc: 10, std: 20 }
- { gcc: 11, std: 20 } - { gcc: 11, std: 20 }
- { gcc: 12, std: 20 } - { gcc: 12, std: 20 }
- { gcc: 13, std: 20 } - { gcc: 13, std: 20 }
@ -659,15 +653,13 @@ jobs:
cmake --build build-17 --target test_cmake_build cmake --build build-17 --target test_cmake_build
# Testing on CentOS (manylinux uses a centos base, and this is an easy way # Testing on CentOS (manylinux uses a centos base).
# to get GCC 4.8, which is the manylinux1 compiler).
centos: centos:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
container: container:
- "centos:7" # GCC 4.8
- "almalinux:8" - "almalinux:8"
- "almalinux:9" - "almalinux:9"
@ -677,18 +669,13 @@ jobs:
steps: steps:
- name: Latest actions/checkout - name: Latest actions/checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
if: matrix.container != 'centos:7'
- name: Pin actions/checkout as required for centos:7 - name: Add Python 3.8
uses: actions/checkout@v3 if: matrix.container == 'almalinux:8'
if: matrix.container == 'centos:7' run: dnf update -y && dnf install -y python38-devel gcc-c++ make git
- name: Add Python 3 (RHEL 7) - name: Add Python 3 (default)
if: matrix.container == 'centos:7' if: matrix.container != 'almalinux:8'
run: yum update -y && yum install -y python3-devel gcc-c++ make git
- name: Add Python 3 (RHEL 8+)
if: matrix.container != 'centos:7'
run: dnf update -y && dnf install -y python3-devel gcc-c++ make git run: dnf update -y && dnf install -y python3-devel gcc-c++ make git
- name: Update pip - name: Update pip
@ -729,9 +716,9 @@ jobs:
# This tests an "install" with the CMake tools # This tests an "install" with the CMake tools
install-classic: install-classic:
name: "🐍 3.7 • Debian • x86 • Install" name: "🐍 3.9 • Debian • x86 • Install"
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: i386/debian:buster container: i386/debian:bullseye
steps: steps:
- uses: actions/checkout@v1 # v1 is required to run inside docker - uses: actions/checkout@v1 # v1 is required to run inside docker
@ -811,18 +798,23 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
python: python:
- 3.6 - '3.8'
- 3.7 - '3.9'
- 3.8 - '3.10'
- 3.9 - '3.11'
- '3.12'
include: include:
- python: 3.9 - python: '3.12'
args: -DCMAKE_CXX_STANDARD=20 args: -DCMAKE_CXX_STANDARD=20
- python: 3.8 - python: '3.11'
args: -DCMAKE_CXX_STANDARD=20
- python: '3.10'
args: -DCMAKE_CXX_STANDARD=20
- python: '3.9'
args: -DCMAKE_CXX_STANDARD=20
- python: '3.8'
args: -DCMAKE_CXX_STANDARD=17 args: -DCMAKE_CXX_STANDARD=17
- python: 3.7
args: -DCMAKE_CXX_STANDARD=14
name: "🐍 ${{ matrix.python }} • MSVC 2019 • x86 ${{ matrix.args }}" name: "🐍 ${{ matrix.python }} • MSVC 2019 • x86 ${{ matrix.args }}"
@ -1111,7 +1103,7 @@ jobs:
uses: jwlawson/actions-setup-cmake@v2.0 uses: jwlawson/actions-setup-cmake@v2.0
- name: Install ninja-build tool - name: Install ninja-build tool
uses: seanmiddleditch/gha-setup-ninja@v4 uses: seanmiddleditch/gha-setup-ninja@v5
- name: Run pip installs - name: Run pip installs
run: | run: |

View File

@ -31,7 +31,7 @@ jobs:
include: include:
- runs-on: ubuntu-20.04 - runs-on: ubuntu-20.04
arch: x64 arch: x64
cmake: "3.5" cmake: "3.15"
- runs-on: ubuntu-20.04 - runs-on: ubuntu-20.04
arch: x64 arch: x64
@ -39,22 +39,22 @@ jobs:
- runs-on: macos-13 - runs-on: macos-13
arch: x64 arch: x64
cmake: "3.7" cmake: "3.15"
- runs-on: windows-2019 - runs-on: windows-2019
arch: x64 # x86 compilers seem to be missing on 2019 image arch: x64 # x86 compilers seem to be missing on 2019 image
cmake: "3.18" cmake: "3.18"
name: 🐍 3.7 • CMake ${{ matrix.cmake }} • ${{ matrix.runs-on }} name: 🐍 3.8 • CMake ${{ matrix.cmake }} • ${{ matrix.runs-on }}
runs-on: ${{ matrix.runs-on }} runs-on: ${{ matrix.runs-on }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Setup Python 3.7 - name: Setup Python 3.8
uses: actions/setup-python@v5 uses: actions/setup-python@v5
with: with:
python-version: 3.7 python-version: 3.8
architecture: ${{ matrix.arch }} architecture: ${{ matrix.arch }}
- name: Prepare env - name: Prepare env

30
.github/workflows/emscripten.yaml vendored Normal file
View File

@ -0,0 +1,30 @@
name: WASM
on:
workflow_dispatch:
pull_request:
branches:
- master
- stable
- v*
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build-wasm-emscripten:
name: Pyodide wheel
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
with:
submodules: true
fetch-depth: 0
- uses: pypa/cibuildwheel@v2.21
env:
PYODIDE_BUILD_EXPORTS: whole_archive
with:
package-dir: tests
only: cp312-pyodide_wasm32

View File

@ -41,7 +41,7 @@ jobs:
# in .github/CONTRIBUTING.md and update as needed. # in .github/CONTRIBUTING.md and update as needed.
name: Clang-Tidy name: Clang-Tidy
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: silkeh/clang:15-bullseye container: silkeh/clang:18-bookworm
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4

View File

@ -21,19 +21,18 @@ env:
jobs: jobs:
# This builds the sdists and wheels and makes sure the files are exactly as # This builds the sdists and wheels and makes sure the files are exactly as
# expected. Using Windows and Python 3.6, since that is often the most # expected.
# challenging matrix element.
test-packaging: test-packaging:
name: 🐍 3.6 • 📦 tests • windows-latest name: 🐍 3.8 • 📦 tests • windows-latest
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Setup 🐍 3.6 - name: Setup 🐍 3.8
uses: actions/setup-python@v5 uses: actions/setup-python@v5
with: with:
python-version: 3.6 python-version: 3.8
- name: Prepare env - name: Prepare env
run: | run: |
@ -92,23 +91,30 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: github.event_name == 'release' && github.event.action == 'published' if: github.event_name == 'release' && github.event.action == 'published'
needs: [packaging] needs: [packaging]
environment:
name: pypi
url: https://pypi.org/p/pybind11
permissions:
id-token: write
attestations: write
steps: steps:
- uses: actions/setup-python@v5
with:
python-version: "3.x"
# Downloads all to directories matching the artifact names # Downloads all to directories matching the artifact names
- uses: actions/download-artifact@v4 - uses: actions/download-artifact@v4
- name: Generate artifact attestation for sdist and wheel
uses: actions/attest-build-provenance@1c608d11d69870c2092266b3f9a6f3abbf17002c # v1.4.3
with:
subject-path: "*/pybind11*"
- name: Publish standard package - name: Publish standard package
uses: pypa/gh-action-pypi-publish@release/v1 uses: pypa/gh-action-pypi-publish@release/v1
with: with:
password: ${{ secrets.pypi_password }}
packages-dir: standard/ packages-dir: standard/
attestations: true
- name: Publish global package - name: Publish global package
uses: pypa/gh-action-pypi-publish@release/v1 uses: pypa/gh-action-pypi-publish@release/v1
with: with:
password: ${{ secrets.pypi_password_global }}
packages-dir: global/ packages-dir: global/
attestations: true

View File

@ -25,14 +25,14 @@ repos:
# Clang format the codebase automatically # Clang format the codebase automatically
- repo: https://github.com/pre-commit/mirrors-clang-format - repo: https://github.com/pre-commit/mirrors-clang-format
rev: "v18.1.5" rev: "v18.1.8"
hooks: hooks:
- id: clang-format - id: clang-format
types_or: [c++, c, cuda] types_or: [c++, c, cuda]
# Ruff, the Python auto-correcting linter/formatter written in Rust # Ruff, the Python auto-correcting linter/formatter written in Rust
- repo: https://github.com/astral-sh/ruff-pre-commit - repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.4.7 rev: v0.6.3
hooks: hooks:
- id: ruff - id: ruff
args: ["--fix", "--show-fixes"] args: ["--fix", "--show-fixes"]
@ -40,7 +40,7 @@ repos:
# Check static types with mypy # Check static types with mypy
- repo: https://github.com/pre-commit/mirrors-mypy - repo: https://github.com/pre-commit/mirrors-mypy
rev: "v1.10.0" rev: "v1.11.2"
hooks: hooks:
- id: mypy - id: mypy
args: [] args: []
@ -79,7 +79,7 @@ repos:
# Also code format the docs # Also code format the docs
- repo: https://github.com/adamchainz/blacken-docs - repo: https://github.com/adamchainz/blacken-docs
rev: "1.16.0" rev: "1.18.0"
hooks: hooks:
- id: blacken-docs - id: blacken-docs
additional_dependencies: additional_dependencies:
@ -93,7 +93,7 @@ repos:
# Avoid directional quotes # Avoid directional quotes
- repo: https://github.com/sirosen/texthooks - repo: https://github.com/sirosen/texthooks
rev: "0.6.6" rev: "0.6.7"
hooks: hooks:
- id: fix-ligatures - id: fix-ligatures
- id: fix-smartquotes - id: fix-smartquotes
@ -142,14 +142,14 @@ repos:
# PyLint has native support - not always usable, but works for us # PyLint has native support - not always usable, but works for us
- repo: https://github.com/PyCQA/pylint - repo: https://github.com/PyCQA/pylint
rev: "v3.2.2" rev: "v3.2.7"
hooks: hooks:
- id: pylint - id: pylint
files: ^pybind11 files: ^pybind11
# Check schemas on some of our YAML files # Check schemas on some of our YAML files
- repo: https://github.com/python-jsonschema/check-jsonschema - repo: https://github.com/python-jsonschema/check-jsonschema
rev: 0.28.4 rev: 0.29.2
hooks: hooks:
- id: check-readthedocs - id: check-readthedocs
- id: check-github-workflows - id: check-github-workflows

View File

@ -10,16 +10,7 @@ if(NOT CMAKE_VERSION VERSION_LESS "3.27")
cmake_policy(GET CMP0148 _pybind11_cmp0148) cmake_policy(GET CMP0148 _pybind11_cmp0148)
endif() endif()
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.15...3.30)
# The `cmake_minimum_required(VERSION 3.5...3.29)` syntax does not work with
# some versions of VS that have a patched CMake 3.11. This forces us to emulate
# the behavior using the following workaround:
if(${CMAKE_VERSION} VERSION_LESS 3.29)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else()
cmake_policy(VERSION 3.29)
endif()
if(_pybind11_cmp0148) if(_pybind11_cmp0148)
cmake_policy(SET CMP0148 ${_pybind11_cmp0148}) cmake_policy(SET CMP0148 ${_pybind11_cmp0148})
@ -27,9 +18,7 @@ if(_pybind11_cmp0148)
endif() endif()
# Avoid infinite recursion if tests include this as a subdirectory # Avoid infinite recursion if tests include this as a subdirectory
if(DEFINED PYBIND11_MASTER_PROJECT) include_guard(GLOBAL)
return()
endif()
# Extract project version from source # Extract project version from source
file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/include/pybind11/detail/common.h" file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/include/pybind11/detail/common.h"
@ -74,14 +63,6 @@ if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
set(PYBIND11_MASTER_PROJECT ON) set(PYBIND11_MASTER_PROJECT ON)
if(OSX AND CMAKE_VERSION VERSION_LESS 3.7)
# Bug in macOS CMake < 3.7 is unable to download catch
message(WARNING "CMAKE 3.7+ needed on macOS to download catch, and newer HIGHLY recommended")
elseif(WINDOWS AND CMAKE_VERSION VERSION_LESS 3.8)
# Only tested with 3.8+ in CI.
message(WARNING "CMAKE 3.8+ tested on Windows, previous versions untested")
endif()
message(STATUS "CMake ${CMAKE_VERSION}") message(STATUS "CMake ${CMAKE_VERSION}")
if(CMAKE_CXX_STANDARD) if(CMAKE_CXX_STANDARD)
@ -133,8 +114,7 @@ cmake_dependent_option(
"Install pybind11 headers in Python include directory instead of default installation prefix" "Install pybind11 headers in Python include directory instead of default installation prefix"
OFF "PYBIND11_INSTALL" OFF) OFF "PYBIND11_INSTALL" OFF)
cmake_dependent_option(PYBIND11_FINDPYTHON "Force new FindPython" ${_pybind11_findpython_default} option(PYBIND11_FINDPYTHON "Force new FindPython" ${_pybind11_findpython_default})
"NOT CMAKE_VERSION VERSION_LESS 3.12" OFF)
# Allow PYTHON_EXECUTABLE if in FINDPYTHON mode and building pybind11's tests # Allow PYTHON_EXECUTABLE if in FINDPYTHON mode and building pybind11's tests
# (makes transition easier while we support both modes). # (makes transition easier while we support both modes).
@ -149,11 +129,14 @@ endif()
set(PYBIND11_HEADERS set(PYBIND11_HEADERS
include/pybind11/detail/class.h include/pybind11/detail/class.h
include/pybind11/detail/common.h include/pybind11/detail/common.h
include/pybind11/detail/cpp_conduit.h
include/pybind11/detail/descr.h include/pybind11/detail/descr.h
include/pybind11/detail/init.h include/pybind11/detail/init.h
include/pybind11/detail/internals.h include/pybind11/detail/internals.h
include/pybind11/detail/type_caster_base.h include/pybind11/detail/type_caster_base.h
include/pybind11/detail/typeid.h include/pybind11/detail/typeid.h
include/pybind11/detail/value_and_holder.h
include/pybind11/detail/exception_translation.h
include/pybind11/attr.h include/pybind11/attr.h
include/pybind11/buffer_info.h include/pybind11/buffer_info.h
include/pybind11/cast.h include/pybind11/cast.h
@ -179,10 +162,11 @@ set(PYBIND11_HEADERS
include/pybind11/stl_bind.h include/pybind11/stl_bind.h
include/pybind11/stl/filesystem.h include/pybind11/stl/filesystem.h
include/pybind11/type_caster_pyobject_ptr.h include/pybind11/type_caster_pyobject_ptr.h
include/pybind11/typing.h) include/pybind11/typing.h
include/pybind11/warnings.h)
# Compare with grep and warn if mismatched # Compare with grep and warn if mismatched
if(PYBIND11_MASTER_PROJECT AND NOT CMAKE_VERSION VERSION_LESS 3.12) if(PYBIND11_MASTER_PROJECT)
file( file(
GLOB_RECURSE _pybind11_header_check GLOB_RECURSE _pybind11_header_check
LIST_DIRECTORIES false LIST_DIRECTORIES false
@ -200,10 +184,7 @@ if(PYBIND11_MASTER_PROJECT AND NOT CMAKE_VERSION VERSION_LESS 3.12)
endif() endif()
endif() endif()
# CMake 3.12 added list(TRANSFORM <list> PREPEND list(TRANSFORM PYBIND11_HEADERS PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/")
# But we can't use it yet
string(REPLACE "include/" "${CMAKE_CURRENT_SOURCE_DIR}/include/" PYBIND11_HEADERS
"${PYBIND11_HEADERS}")
# Cache variable so this can be used in parent projects # Cache variable so this can be used in parent projects
set(pybind11_INCLUDE_DIR set(pybind11_INCLUDE_DIR
@ -273,25 +254,11 @@ if(PYBIND11_INSTALL)
tools/${PROJECT_NAME}Config.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" tools/${PROJECT_NAME}Config.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
INSTALL_DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR}) INSTALL_DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR})
if(CMAKE_VERSION VERSION_LESS 3.14) # CMake natively supports header-only libraries
# Remove CMAKE_SIZEOF_VOID_P from ConfigVersion.cmake since the library does write_basic_package_version_file(
# not depend on architecture specific settings or libraries. ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
set(_PYBIND11_CMAKE_SIZEOF_VOID_P ${CMAKE_SIZEOF_VOID_P}) VERSION ${PROJECT_VERSION}
unset(CMAKE_SIZEOF_VOID_P) COMPATIBILITY AnyNewerVersion ARCH_INDEPENDENT)
write_basic_package_version_file(
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
VERSION ${PROJECT_VERSION}
COMPATIBILITY AnyNewerVersion)
set(CMAKE_SIZEOF_VOID_P ${_PYBIND11_CMAKE_SIZEOF_VOID_P})
else()
# CMake 3.14+ natively supports header-only libraries
write_basic_package_version_file(
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
VERSION ${PROJECT_VERSION}
COMPATIBILITY AnyNewerVersion ARCH_INDEPENDENT)
endif()
install( install(
FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake

View File

@ -34,7 +34,7 @@ dependency.
Think of this library as a tiny self-contained version of Boost.Python Think of this library as a tiny self-contained version of Boost.Python
with everything stripped away that isn't relevant for binding with everything stripped away that isn't relevant for binding
generation. Without comments, the core header files only require ~4K generation. Without comments, the core header files only require ~4K
lines of code and depend on Python (3.6+, or PyPy) and the C++ lines of code and depend on Python (3.8+, or PyPy) and the C++
standard library. This compact implementation was possible thanks to standard library. This compact implementation was possible thanks to
some C++11 language features (specifically: tuples, lambda functions and some C++11 language features (specifically: tuples, lambda functions and
variadic templates). Since its creation, this library has grown beyond variadic templates). Since its creation, this library has grown beyond
@ -79,7 +79,7 @@ Goodies
In addition to the core functionality, pybind11 provides some extra In addition to the core functionality, pybind11 provides some extra
goodies: goodies:
- Python 3.6+, and PyPy3 7.3 are supported with an implementation-agnostic - Python 3.8+, and PyPy3 7.3 are supported with an implementation-agnostic
interface (pybind11 2.9 was the last version to support Python 2 and 3.5). interface (pybind11 2.9 was the last version to support Python 2 and 3.5).
- It is possible to bind C++11 lambda functions with captured - It is possible to bind C++11 lambda functions with captured

View File

@ -259,7 +259,7 @@ copying to take place:
"small"_a // <- This one can be copied if needed "small"_a // <- This one can be copied if needed
); );
With the above binding code, attempting to call the the ``some_method(m)`` With the above binding code, attempting to call the ``some_method(m)``
method on a ``MyClass`` object, or attempting to call ``some_function(m, m2)`` method on a ``MyClass`` object, or attempting to call ``some_function(m, m2)``
will raise a ``RuntimeError`` rather than making a temporary copy of the array. will raise a ``RuntimeError`` rather than making a temporary copy of the array.
It will, however, allow the ``m2`` argument to be copied into a temporary if It will, however, allow the ``m2`` argument to be copied into a temporary if

View File

@ -162,7 +162,7 @@ the declaration
.. code-block:: cpp .. code-block:: cpp
PYBIND11_MAKE_OPAQUE(std::vector<int>); PYBIND11_MAKE_OPAQUE(std::vector<int>)
before any binding code (e.g. invocations to ``class_::def()``, etc.). This before any binding code (e.g. invocations to ``class_::def()``, etc.). This
macro must be specified at the top level (and outside of any namespaces), since macro must be specified at the top level (and outside of any namespaces), since
@ -207,8 +207,8 @@ The following example showcases usage of :file:`pybind11/stl_bind.h`:
// Don't forget this // Don't forget this
#include <pybind11/stl_bind.h> #include <pybind11/stl_bind.h>
PYBIND11_MAKE_OPAQUE(std::vector<int>); PYBIND11_MAKE_OPAQUE(std::vector<int>)
PYBIND11_MAKE_OPAQUE(std::map<std::string, double>); PYBIND11_MAKE_OPAQUE(std::map<std::string, double>)
// ... // ...

View File

@ -826,8 +826,7 @@ An instance can now be pickled as follows:
always use the latest available version. Beware: failure to follow these always use the latest available version. Beware: failure to follow these
instructions will cause important pybind11 memory allocation routines to be instructions will cause important pybind11 memory allocation routines to be
skipped during unpickling, which will likely lead to memory corruption skipped during unpickling, which will likely lead to memory corruption
and/or segmentation faults. Python defaults to version 3 (Python 3-3.7) and and/or segmentation faults.
version 4 for Python 3.8+.
.. seealso:: .. seealso::

View File

@ -18,7 +18,7 @@ information, see :doc:`/compiling`.
.. code-block:: cmake .. code-block:: cmake
cmake_minimum_required(VERSION 3.5...3.29) cmake_minimum_required(VERSION 3.15...3.30)
project(example) project(example)
find_package(pybind11 REQUIRED) # or `add_subdirectory(pybind11)` find_package(pybind11 REQUIRED) # or `add_subdirectory(pybind11)`

View File

@ -368,8 +368,7 @@ Should they throw or fail to catch any exceptions in their call graph,
the C++ runtime calls ``std::terminate()`` to abort immediately. the C++ runtime calls ``std::terminate()`` to abort immediately.
Similarly, Python exceptions raised in a class's ``__del__`` method do not Similarly, Python exceptions raised in a class's ``__del__`` method do not
propagate, but are logged by Python as an unraisable error. In Python 3.8+, a propagate, but ``sys.unraisablehook()`` `is triggered
`system hook is triggered
<https://docs.python.org/3/library/sys.html#sys.unraisablehook>`_ <https://docs.python.org/3/library/sys.html#sys.unraisablehook>`_
and an auditing event is logged. and an auditing event is logged.

View File

@ -124,7 +124,7 @@ top namespace level before any binding code:
.. code-block:: cpp .. code-block:: cpp
PYBIND11_DECLARE_HOLDER_TYPE(T, SmartPtr<T>); PYBIND11_DECLARE_HOLDER_TYPE(T, SmartPtr<T>)
The first argument of :func:`PYBIND11_DECLARE_HOLDER_TYPE` should be a The first argument of :func:`PYBIND11_DECLARE_HOLDER_TYPE` should be a
placeholder name that is used as a template parameter of the second argument. placeholder name that is used as a template parameter of the second argument.
@ -136,7 +136,7 @@ by default. Specify
.. code-block:: cpp .. code-block:: cpp
PYBIND11_DECLARE_HOLDER_TYPE(T, SmartPtr<T>, true); PYBIND11_DECLARE_HOLDER_TYPE(T, SmartPtr<T>, true)
if ``SmartPtr<T>`` can always be initialized from a ``T*`` pointer without the if ``SmartPtr<T>`` can always be initialized from a ``T*`` pointer without the
risk of inconsistencies (such as multiple independent ``SmartPtr`` instances risk of inconsistencies (such as multiple independent ``SmartPtr`` instances
@ -154,7 +154,7 @@ specialized:
.. code-block:: cpp .. code-block:: cpp
// Always needed for custom holder types // Always needed for custom holder types
PYBIND11_DECLARE_HOLDER_TYPE(T, SmartPtr<T>); PYBIND11_DECLARE_HOLDER_TYPE(T, SmartPtr<T>)
// Only needed if the type's `.get()` goes by another name // Only needed if the type's `.get()` goes by another name
namespace PYBIND11_NAMESPACE { namespace detail { namespace PYBIND11_NAMESPACE { namespace detail {

View File

@ -78,6 +78,13 @@ For brevity, all code examples assume that the following two lines are present:
namespace py = pybind11; namespace py = pybind11;
.. note::
``pybind11/pybind11.h`` includes ``Python.h``, as such it must be the first file
included in any source file or header for `the same reasons as Python.h`_.
.. _`the same reasons as Python.h`: https://docs.python.org/3/extending/extending.html#a-simple-example
Some features may require additional headers, but those will be specified as needed. Some features may require additional headers, but those will be specified as needed.
.. _simple_example: .. _simple_example:

View File

@ -1,3 +1,5 @@
from __future__ import annotations
import datetime as dt import datetime as dt
import os import os
import random import random

View File

@ -15,6 +15,273 @@ IN DEVELOPMENT
Changes will be summarized here periodically. Changes will be summarized here periodically.
New Features:
* Support for Python 3.7 was removed. (Official end-of-life: 2023-06-27).
`#5191 <https://github.com/pybind/pybind11/pull/5191>`_
* stl.h ``list|set|map_caster`` were made more user friendly: it is no longer
necessary to explicitly convert Python iterables to ``tuple()``, ``set()``,
or ``map()`` in many common situations.
`#4686 <https://github.com/pybind/pybind11/pull/4686>`_
* Support for CMake older than 3.15 removed. CMake 3.15-3.30 supported.
`#5304 <https://github.com/pybind/pybind11/pull/5304>`_
* The ``array_caster`` in pybind11/stl.h was enhanced to support value types that are not default-constructible.
`#5305 <https://github.com/pybind/pybind11/pull/5305>`_
* Added ``py::warnings`` namespace with ``py::warnings::warn`` and ``py::warnings::new_warning_type`` that provides the interface for Python warnings.
`#5291 <https://github.com/pybind/pybind11/pull/5291>`_
Version 2.13.6 (September 13, 2024)
-----------------------------------
New Features:
* A new ``self._pybind11_conduit_v1_()`` method is automatically added to all
``py::class_``-wrapped types, to enable type-safe interoperability between
different independent Python/C++ bindings systems, including pybind11
versions with different ``PYBIND11_INTERNALS_VERSION``'s. Supported on
pybind11 2.11.2, 2.12.1, and 2.13.6+.
`#5296 <https://github.com/pybind/pybind11/pull/5296>`_
Bug fixes:
* Using ``__cpp_nontype_template_args`` instead of ``__cpp_nontype_template_parameter_class``.
`#5330 <https://github.com/pybind/pybind11/pull/5330>`_
* Properly translate C++ exception to Python exception when creating Python buffer from wrapped object.
`#5324 <https://github.com/pybind/pybind11/pull/5324>`_
Documentation:
* Adds an answer (FAQ) for "What is a highly conclusive and simple way to find memory leaks?".
`#5340 <https://github.com/pybind/pybind11/pull/5340>`_
Version 2.13.5 (August 22, 2024)
--------------------------------
Bug fixes:
* Fix includes when using Windows long paths (``\\?\`` prefix).
`#5321 <https://github.com/pybind/pybind11/pull/5321>`_
* Support ``-Wpedantic`` in C++20 mode.
`#5322 <https://github.com/pybind/pybind11/pull/5322>`_
* Fix and test ``<ranges>`` support for ``py::tuple`` and ``py::list``.
`#5314 <https://github.com/pybind/pybind11/pull/5314>`_
Version 2.13.4 (August 14, 2024)
--------------------------------
Bug fixes:
* Fix paths with spaces, including on Windows.
(Replaces regression from `#5302 <https://github.com/pybind/pybind11/pull/5302>`_)
`#4874 <https://github.com/pybind/pybind11/pull/4874>`_
Documentation:
* Remove repetitive words.
`#5308 <https://github.com/pybind/pybind11/pull/5308>`_
Version 2.13.3 (August 13, 2024)
--------------------------------
Bug fixes:
* Quote paths from pybind11-config
`#5302 <https://github.com/pybind/pybind11/pull/5302>`_
* Fix typo in Emscripten support when in config mode (CMake)
`#5301 <https://github.com/pybind/pybind11/pull/5301>`_
Version 2.13.2 (August 13, 2024)
--------------------------------
New Features:
* A ``pybind11::detail::type_caster_std_function_specializations`` feature was added, to support specializations for
``std::function``'s with return types that require custom to-Python conversion behavior (to primary use case is to catch and
convert exceptions).
`#4597 <https://github.com/pybind/pybind11/pull/4597>`_
Changes:
* Use ``PyMutex`` instead of ``std::mutex`` for internal locking in the free-threaded build.
`#5219 <https://github.com/pybind/pybind11/pull/5219>`_
* Add a special type annotation for C++ empty tuple.
`#5214 <https://github.com/pybind/pybind11/pull/5214>`_
* When compiling for WebAssembly, add the required exception flags (CMake 3.13+).
`#5298 <https://github.com/pybind/pybind11/pull/5298>`_
Bug fixes:
* Make ``gil_safe_call_once_and_store`` thread-safe in free-threaded CPython.
`#5246 <https://github.com/pybind/pybind11/pull/5246>`_
* A missing ``#include <algorithm>`` in pybind11/typing.h was added to fix build errors (in case user code does not already depend
on that include).
`#5208 <https://github.com/pybind/pybind11/pull/5208>`_
* Fix regression introduced in #5201 for GCC<10.3 in C++20 mode.
`#5205 <https://github.com/pybind/pybind11/pull/5205>`_
.. fix(cmake)
* Remove extra = when assigning flto value in the case for Clang in CMake.
`#5207 <https://github.com/pybind/pybind11/pull/5207>`_
Tests:
* Adding WASM testing to our CI (Pyodide / Emscripten via scikit-build-core).
`#4745 <https://github.com/pybind/pybind11/pull/4745>`_
* clang-tidy (in GitHub Actions) was updated from clang 15 to clang 18.
`#5272 <https://github.com/pybind/pybind11/pull/5272>`_
Version 2.13.1 (June 26, 2024)
------------------------------
New Features:
* Add support for ``Typing.Callable[..., T]``.
`#5202 <https://github.com/pybind/pybind11/pull/5202>`_
Bug fixes:
* Avoid aligned allocation in free-threaded build in order to support macOS
versions before 10.14.
`#5200 <https://github.com/pybind/pybind11/pull/5200>`_
Version 2.13.0 (June 25, 2024)
------------------------------
New Features:
* Support free-threaded CPython (3.13t). Add ``py::mod_gil_not_used()`` tag to
indicate if a module supports running with the GIL disabled.
`#5148 <https://github.com/pybind/pybind11/pull/5148>`_
* Support for Python 3.6 was removed. (Official end-of-life: 2021-12-23).
`#5177 <https://github.com/pybind/pybind11/pull/5177>`_
* ``py::list`` gained a ``.clear()`` method.
`#5153 <https://github.com/pybind/pybind11/pull/5153>`_
.. feat(types)
* Support for ``Union``, ``Optional``, ``type[T]``, ``typing.TypeGuard``,
``typing.TypeIs``, ``typing.Never``, ``typing.NoReturn`` and
``typing.Literal`` was added to ``pybind11/typing.h``.
`#5166 <https://github.com/pybind/pybind11/pull/5166>`_
`#5165 <https://github.com/pybind/pybind11/pull/5165>`_
`#5194 <https://github.com/pybind/pybind11/pull/5194>`_
`#5193 <https://github.com/pybind/pybind11/pull/5193>`_
`#5192 <https://github.com/pybind/pybind11/pull/5192>`_
.. feat(cmake)
* In CMake, if ``PYBIND11_USE_CROSSCOMPILING`` is enabled, then
``CMAKE_CROSSCOMPILING`` will be respected and will keep pybind11 from
accessing the interpreter during configuration. Several CMake variables will
be required in this case, but can be deduced from the environment variable
``SETUPTOOLS_EXT_SUFFIX``. The default (currently ``OFF``) may be changed in
the future.
`#5083 <https://github.com/pybind/pybind11/pull/5083>`_
Bug fixes:
* A refcount bug (leading to heap-use-after-free) involving trampoline
functions with ``PyObject *`` return type was fixed.
`#5156 <https://github.com/pybind/pybind11/pull/5156>`_
* Return ``py::ssize_t`` from ``.ref_count()`` instead of ``int``.
`#5139 <https://github.com/pybind/pybind11/pull/5139>`_
* A subtle bug involving C++ types with unusual ``operator&`` overrides
was fixed.
`#5189 <https://github.com/pybind/pybind11/pull/5189>`_
* Support Python 3.13 with minor fix, add to CI.
`#5127 <https://github.com/pybind/pybind11/pull/5127>`_
.. fix(cmake)
* Fix mistake affecting old cmake and old boost.
`#5149 <https://github.com/pybind/pybind11/pull/5149>`_
Documentation:
* Build docs updated to feature scikit-build-core and meson-python, and updated
setuptools instructions.
`#5168 <https://github.com/pybind/pybind11/pull/5168>`_
Tests:
* Avoid immortal objects in tests.
`#5150 <https://github.com/pybind/pybind11/pull/5150>`_
CI:
* Compile against Python 3.13t in CI.
* Use ``macos-13`` (Intel) for CI jobs for now (will drop Python 3.7 soon).
`#5109 <https://github.com/pybind/pybind11/pull/5109>`_
* Releases now have artifact attestations, visible at
https://github.com/pybind/pybind11/attestations.
`#5196 <https://github.com/pybind/pybind11/pull/5196>`_
Other:
* Some cleanup in preparation for 3.13 support.
`#5137 <https://github.com/pybind/pybind11/pull/5137>`_
* Avoid a warning by ensuring an iterator end check is included in release mode.
`#5129 <https://github.com/pybind/pybind11/pull/5129>`_
* Bump max cmake to 3.29.
`#5075 <https://github.com/pybind/pybind11/pull/5075>`_
* Update docs and noxfile.
`#5071 <https://github.com/pybind/pybind11/pull/5071>`_
Version 2.12.1 (September 13, 2024)
-----------------------------------
New Features:
* A new ``self._pybind11_conduit_v1_()`` method is automatically added to all
``py::class_``-wrapped types, to enable type-safe interoperability between
different independent Python/C++ bindings systems, including pybind11
versions with different ``PYBIND11_INTERNALS_VERSION``'s. Supported on
pybind11 2.11.2, 2.12.1, and 2.13.6+.
`#5296 <https://github.com/pybind/pybind11/pull/5296>`_
Version 2.12.0 (March 27, 2024) Version 2.12.0 (March 27, 2024)
------------------------------- -------------------------------
@ -189,6 +456,18 @@ Other:
* An ``assert()`` was added to help Coverty avoid generating a false positive. * An ``assert()`` was added to help Coverty avoid generating a false positive.
`#4817 <https://github.com/pybind/pybind11/pull/4817>`_ `#4817 <https://github.com/pybind/pybind11/pull/4817>`_
Version 2.11.2 (September 13, 2024)
-----------------------------------
New Features:
* A new ``self._pybind11_conduit_v1_()`` method is automatically added to all
``py::class_``-wrapped types, to enable type-safe interoperability between
different independent Python/C++ bindings systems, including pybind11
versions with different ``PYBIND11_INTERNALS_VERSION``'s. Supported on
pybind11 2.11.2, 2.12.1, and 2.13.6+.
`#5296 <https://github.com/pybind/pybind11/pull/5296>`_
Version 2.11.1 (July 17, 2023) Version 2.11.1 (July 17, 2023)
------------------------------ ------------------------------

View File

@ -3,15 +3,123 @@
Build systems Build systems
############# #############
For an overview of Python packaging including compiled packaging with a pybind11
example, along with a cookiecutter that includes several pybind11 options, see
the `Scientific Python Development Guide`_.
.. _Scientific Python Development Guide: https://learn.scientific-python.org/development/guides/packaging-compiled/
.. scikit-build-core:
Modules with CMake
==================
A Python extension module can be created with just a few lines of code:
.. code-block:: cmake
cmake_minimum_required(VERSION 3.15...3.30)
project(example LANGUAGES CXX)
set(PYBIND11_FINDPYTHON ON)
find_package(pybind11 CONFIG REQUIRED)
pybind11_add_module(example example.cpp)
install(TARGETS example DESTINATION .)
(You use the ``add_subdirectory`` instead, see the example in :ref:`cmake`.) In
this example, the code is located in a file named :file:`example.cpp`. Either
method will import the pybind11 project which provides the
``pybind11_add_module`` function. It will take care of all the details needed
to build a Python extension module on any platform.
To build with pip, build, cibuildwheel, uv, or other Python tools, you can
add a ``pyproject.toml`` file like this:
.. code-block:: toml
[build-system]
requires = ["scikit-build-core", "pybind11"]
build-backend = "scikit_build_core.build"
[project]
name = "example"
version = "0.1.0"
You don't need setuptools files like ``MANIFEST.in``, ``setup.py``, or
``setup.cfg``, as this is not setuptools. See `scikit-build-core`_ for details.
For projects you plan to upload to PyPI, be sure to fill out the ``[project]``
table with other important metadata as well (see `Writing pyproject.toml`_).
A working sample project can be found in the [scikit_build_example]_
repository. An older and harder-to-maintain method is in [cmake_example]_. More
details about our cmake support can be found below in :ref:`cmake`.
.. _scikit-build-core: https://scikit-build-core.readthedocs.io
.. [scikit_build_example] https://github.com/pybind/scikit_build_example
.. [cmake_example] https://github.com/pybind/cmake_example
.. _modules-meson-python:
Modules with meson-python
=========================
You can also build a package with `Meson`_ using `meson-python`_, if you prefer
that. Your ``meson.build`` file would look something like this:
.. _meson-example:
.. code-block:: meson
project(
'example',
'cpp',
version: '0.1.0',
default_options: [
'cpp_std=c++11',
],
)
py = import('python').find_installation(pure: false)
pybind11_dep = dependency('pybind11')
py.extension_module('example',
'example.cpp',
install: true,
dependencies : [pybind11_dep],
)
And you would need a ``pyproject.toml`` file like this:
.. code-block:: toml
[build-system]
requires = ["meson-python", "pybind11"]
build-backend = "mesonpy"
Meson-python *requires* your project to be in git (or mercurial) as it uses it
for the SDist creation. For projects you plan to upload to PyPI, be sure to fill out the
``[project]`` table as well (see `Writing pyproject.toml`_).
.. _Writing pyproject.toml: https://packaging.python.org/en/latest/guides/writing-pyproject-toml
.. _meson: https://mesonbuild.com
.. _meson-python: https://meson-python.readthedocs.io/en/latest
.. _build-setuptools: .. _build-setuptools:
Building with setuptools Modules with setuptools
======================== =======================
For projects on PyPI, building with setuptools is the way to go. Sylvain Corlay For projects on PyPI, a historically popular option is setuptools. Sylvain
has kindly provided an example project which shows how to set up everything, Corlay has kindly provided an example project which shows how to set up
including automatic generation of documentation using Sphinx. Please refer to everything, including automatic generation of documentation using Sphinx.
the [python_example]_ repository. Please refer to the [python_example]_ repository.
.. [python_example] https://github.com/pybind/python_example .. [python_example] https://github.com/pybind/python_example
@ -21,11 +129,11 @@ To use pybind11 inside your ``setup.py``, you have to have some system to
ensure that ``pybind11`` is installed when you build your package. There are ensure that ``pybind11`` is installed when you build your package. There are
four possible ways to do this, and pybind11 supports all four: You can ask all four possible ways to do this, and pybind11 supports all four: You can ask all
users to install pybind11 beforehand (bad), you can use users to install pybind11 beforehand (bad), you can use
:ref:`setup_helpers-pep518` (good, but very new and requires Pip 10), :ref:`setup_helpers-pep518` (good), ``setup_requires=`` (discouraged), or you
:ref:`setup_helpers-setup_requires` (discouraged by Python packagers now that can :ref:`setup_helpers-copy-manually` (works but you have to manually sync
PEP 518 is available, but it still works everywhere), or you can your copy to get updates). Third party packagers like conda-forge generally
:ref:`setup_helpers-copy-manually` (always works but you have to manually sync strongly prefer the ``pyproject.toml`` method, as it gives them control over
your copy to get updates). the ``pybind11`` version, and they may apply patches, etc.
An example of a ``setup.py`` using pybind11's helpers: An example of a ``setup.py`` using pybind11's helpers:
@ -122,70 +230,41 @@ version number that includes the number of commits since your last tag and a
hash for a dirty directory. Another way to force a rebuild is purge your cache hash for a dirty directory. Another way to force a rebuild is purge your cache
or use Pip's ``--no-cache-dir`` option. or use Pip's ``--no-cache-dir`` option.
You also need a ``MANIFEST.in`` file to include all relevant files so that you
can make an SDist. If you use `pypa-build`_, that will build an SDist then a
wheel from that SDist by default, so you can look inside those files (wheels
are just zip files with a ``.whl`` extension) to make sure you aren't missing
files. `check-manifest`_ (setuptools specific) or `check-sdist`_ (general) are
CLI tools that can compare the SDist contents with your source control.
.. [Ccache] https://ccache.dev .. [Ccache] https://ccache.dev
.. [setuptools_scm] https://github.com/pypa/setuptools_scm .. [setuptools_scm] https://github.com/pypa/setuptools_scm
.. _setup_helpers-pep518: .. _setup_helpers-pep518:
PEP 518 requirements (Pip 10+ required) Build requirements
--------------------------------------- ------------------
If you use `PEP 518's <https://www.python.org/dev/peps/pep-0518/>`_ With a ``pyproject.toml`` file, you can ensure that ``pybind11`` is available
``pyproject.toml`` file, you can ensure that ``pybind11`` is available during during the compilation of your project. When this file exists, Pip will make a
the compilation of your project. When this file exists, Pip will make a new new virtual environment, download just the packages listed here in
virtual environment, download just the packages listed here in ``requires=``, ``requires=``, and build a wheel (binary Python package). It will then throw
and build a wheel (binary Python package). It will then throw away the away the environment, and install your wheel.
environment, and install your wheel.
Your ``pyproject.toml`` file will likely look something like this: Your ``pyproject.toml`` file will likely look something like this:
.. code-block:: toml .. code-block:: toml
[build-system] [build-system]
requires = ["setuptools>=42", "pybind11>=2.6.1"] requires = ["setuptools", "pybind11"]
build-backend = "setuptools.build_meta" build-backend = "setuptools.build_meta"
.. note::
The main drawback to this method is that a `PEP 517`_ compliant build tool,
such as Pip 10+, is required for this approach to work; older versions of
Pip completely ignore this file. If you distribute binaries (called wheels
in Python) using something like `cibuildwheel`_, remember that ``setup.py``
and ``pyproject.toml`` are not even contained in the wheel, so this high
Pip requirement is only for source builds, and will not affect users of
your binary wheels. If you are building SDists and wheels, then
`pypa-build`_ is the recommended official tool.
.. _PEP 517: https://www.python.org/dev/peps/pep-0517/ .. _PEP 517: https://www.python.org/dev/peps/pep-0517/
.. _cibuildwheel: https://cibuildwheel.readthedocs.io .. _cibuildwheel: https://cibuildwheel.pypa.io
.. _pypa-build: https://pypa-build.readthedocs.io/en/latest/ .. _pypa-build: https://build.pypa.io/en/latest/
.. _check-manifest: https://pypi.io/project/check-manifest
.. _setup_helpers-setup_requires: .. _check-sdist: https://pypi.io/project/check-sdist
Classic ``setup_requires``
--------------------------
If you want to support old versions of Pip with the classic
``setup_requires=["pybind11"]`` keyword argument to setup, which triggers a
two-phase ``setup.py`` run, then you will need to use something like this to
ensure the first pass works (which has not yet installed the ``setup_requires``
packages, since it can't install something it does not know about):
.. code-block:: python
try:
from pybind11.setup_helpers import Pybind11Extension
except ImportError:
from setuptools import Extension as Pybind11Extension
It doesn't matter that the Extension class is not the enhanced subclass for the
first pass run; and the second pass will have the ``setup_requires``
requirements.
This is obviously more of a hack than the PEP 518 method, but it supports
ancient versions of Pip.
.. _setup_helpers-copy-manually: .. _setup_helpers-copy-manually:
@ -231,32 +310,22 @@ the C++ source file. Python is then able to find the module and load it.
.. [cppimport] https://github.com/tbenthompson/cppimport .. [cppimport] https://github.com/tbenthompson/cppimport
.. _cmake: .. _cmake:
Building with CMake Building with CMake
=================== ===================
For C++ codebases that have an existing CMake-based build system, a Python For C++ codebases that have an existing CMake-based build system, a Python
extension module can be created with just a few lines of code: extension module can be created with just a few lines of code, as seen above in
the module section. Pybind11 currently defaults to the old mechanism, though be
aware that CMake 3.27 removed the old mechanism, so pybind11 will automatically
switch if the old mechanism is not available. Please opt into the new mechanism
if at all possible. Our default may change in future versions. This is the
minimum required:
.. code-block:: cmake
cmake_minimum_required(VERSION 3.5...3.29)
project(example LANGUAGES CXX)
add_subdirectory(pybind11)
pybind11_add_module(example example.cpp)
This assumes that the pybind11 repository is located in a subdirectory named
:file:`pybind11` and that the code is located in a file named :file:`example.cpp`.
The CMake command ``add_subdirectory`` will import the pybind11 project which
provides the ``pybind11_add_module`` function. It will take care of all the
details needed to build a Python extension module on any platform.
A working sample project, including a way to invoke CMake from :file:`setup.py` for
PyPI integration, can be found in the [cmake_example]_ repository.
.. [cmake_example] https://github.com/pybind/cmake_example
.. versionchanged:: 2.6 .. versionchanged:: 2.6
CMake 3.4+ is required. CMake 3.4+ is required.
@ -264,6 +333,10 @@ PyPI integration, can be found in the [cmake_example]_ repository.
.. versionchanged:: 2.11 .. versionchanged:: 2.11
CMake 3.5+ is required. CMake 3.5+ is required.
.. versionchanged:: 2.14
CMake 3.15+ is required.
Further information can be found at :doc:`cmake/index`. Further information can be found at :doc:`cmake/index`.
pybind11_add_module pybind11_add_module
@ -318,7 +391,7 @@ that will be respected instead of the built-in flag search.
The ``OPT_SIZE`` flag enables size-based optimization equivalent to the The ``OPT_SIZE`` flag enables size-based optimization equivalent to the
standard ``/Os`` or ``-Os`` compiler flags and the ``MinSizeRel`` build type, standard ``/Os`` or ``-Os`` compiler flags and the ``MinSizeRel`` build type,
which avoid optimizations that that can substantially increase the size of the which avoid optimizations that can substantially increase the size of the
resulting binary. This flag is particularly useful in projects that are split resulting binary. This flag is particularly useful in projects that are split
into performance-critical parts and associated bindings. In this case, we can into performance-critical parts and associated bindings. In this case, we can
compile the project in release mode (and hence, optimize performance globally), compile the project in release mode (and hence, optimize performance globally),
@ -356,7 +429,7 @@ with ``PYTHON_EXECUTABLE``. For example:
.. code-block:: bash .. code-block:: bash
cmake -DPYBIND11_PYTHON_VERSION=3.6 .. cmake -DPYBIND11_PYTHON_VERSION=3.8 ..
# Another method: # Another method:
cmake -DPYTHON_EXECUTABLE=/path/to/python .. cmake -DPYTHON_EXECUTABLE=/path/to/python ..
@ -374,7 +447,7 @@ See the `Config file`_ docstring for details of relevant CMake variables.
.. code-block:: cmake .. code-block:: cmake
cmake_minimum_required(VERSION 3.4...3.18) cmake_minimum_required(VERSION 3.15...3.30)
project(example LANGUAGES CXX) project(example LANGUAGES CXX)
find_package(pybind11 REQUIRED) find_package(pybind11 REQUIRED)
@ -413,17 +486,16 @@ can refer to the same [cmake_example]_ repository for a full sample project
FindPython mode FindPython mode
--------------- ---------------
CMake 3.12+ (3.15+ recommended, 3.18.2+ ideal) added a new module called Modern CMake (3.18.2+ ideal) added a new module called FindPython that had a
FindPython that had a highly improved search algorithm and modern targets highly improved search algorithm and modern targets and tools. If you use
and tools. If you use FindPython, pybind11 will detect this and use the FindPython, pybind11 will detect this and use the existing targets instead:
existing targets instead:
.. code-block:: cmake .. code-block:: cmake
cmake_minimum_required(VERSION 3.15...3.22) cmake_minimum_required(VERSION 3.15...3.30)
project(example LANGUAGES CXX) project(example LANGUAGES CXX)
find_package(Python 3.6 COMPONENTS Interpreter Development REQUIRED) find_package(Python 3.8 COMPONENTS Interpreter Development REQUIRED)
find_package(pybind11 CONFIG REQUIRED) find_package(pybind11 CONFIG REQUIRED)
# or add_subdirectory(pybind11) # or add_subdirectory(pybind11)
@ -471,7 +543,7 @@ available in all modes. The targets provided are:
Just the "linking" part of pybind11:module Just the "linking" part of pybind11:module
``pybind11::module`` ``pybind11::module``
Everything for extension modules - ``pybind11::pybind11`` + ``Python::Module`` (FindPython CMake 3.15+) or ``pybind11::python_link_helper`` Everything for extension modules - ``pybind11::pybind11`` + ``Python::Module`` (FindPython) or ``pybind11::python_link_helper``
``pybind11::embed`` ``pybind11::embed``
Everything for embedding the Python interpreter - ``pybind11::pybind11`` + ``Python::Python`` (FindPython) or Python libs Everything for embedding the Python interpreter - ``pybind11::pybind11`` + ``Python::Python`` (FindPython) or Python libs
@ -498,7 +570,7 @@ You can use these targets to build complex applications. For example, the
.. code-block:: cmake .. code-block:: cmake
cmake_minimum_required(VERSION 3.5...3.29) cmake_minimum_required(VERSION 3.15...3.30)
project(example LANGUAGES CXX) project(example LANGUAGES CXX)
find_package(pybind11 REQUIRED) # or add_subdirectory(pybind11) find_package(pybind11 REQUIRED) # or add_subdirectory(pybind11)
@ -556,7 +628,7 @@ information about usage in C++, see :doc:`/advanced/embedding`.
.. code-block:: cmake .. code-block:: cmake
cmake_minimum_required(VERSION 3.5...3.29) cmake_minimum_required(VERSION 3.15...3.30)
project(example LANGUAGES CXX) project(example LANGUAGES CXX)
find_package(pybind11 REQUIRED) # or add_subdirectory(pybind11) find_package(pybind11 REQUIRED) # or add_subdirectory(pybind11)
@ -616,6 +688,13 @@ Building with Bazel
You can build with the Bazel build system using the `pybind11_bazel You can build with the Bazel build system using the `pybind11_bazel
<https://github.com/pybind/pybind11_bazel>`_ repository. <https://github.com/pybind/pybind11_bazel>`_ repository.
Building with Meson
===================
You can use Meson, which has support for ``pybind11`` as a dependency (internally
relying on our ``pkg-config`` support). See the :ref:`module example above <meson-example>`.
Generating binding code automatically Generating binding code automatically
===================================== =====================================
@ -642,7 +721,7 @@ customizable pybind11-based wrappers by parsing C++ header files.
[litgen]_ is an automatic python bindings generator with a focus on generating [litgen]_ is an automatic python bindings generator with a focus on generating
documented and discoverable bindings: bindings will nicely reproduce the documentation documented and discoverable bindings: bindings will nicely reproduce the documentation
found in headers. It is is based on srcML (srcml.org), a highly scalable, multi-language found in headers. It is based on srcML (srcml.org), a highly scalable, multi-language
parsing tool with a developer centric approach. The API that you want to expose to python parsing tool with a developer centric approach. The API that you want to expose to python
must be C++14 compatible (but your implementation can use more modern constructs). must be C++14 compatible (but your implementation can use more modern constructs).

View File

@ -11,6 +11,7 @@
# #
# All configuration values have a default; values that are commented out # All configuration values have a default; values that are commented out
# serve to show the default. # serve to show the default.
from __future__ import annotations
import os import os
import re import re

View File

@ -247,6 +247,50 @@ been received, you must either explicitly interrupt execution by throwing
}); });
} }
What is a highly conclusive and simple way to find memory leaks (e.g. in pybind11 bindings)?
============================================================================================
Use ``while True`` & ``top`` (Linux, macOS).
For example, locally change tests/test_type_caster_pyobject_ptr.py like this:
.. code-block:: diff
def test_return_list_pyobject_ptr_reference():
+ while True:
vec_obj = m.return_list_pyobject_ptr_reference(ValueHolder)
assert [e.value for e in vec_obj] == [93, 186]
# Commenting out the next `assert` will leak the Python references.
# An easy way to see evidence of the leaks:
# Insert `while True:` as the first line of this function and monitor the
# process RES (Resident Memory Size) with the Unix top command.
- assert m.dec_ref_each_pyobject_ptr(vec_obj) == 2
+ # assert m.dec_ref_each_pyobject_ptr(vec_obj) == 2
Then run the test as you would normally do, which will go into the infinite loop.
**In another shell, but on the same machine** run:
.. code-block:: bash
top
This will show:
.. code-block::
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1266095 rwgk 20 0 5207496 611372 45696 R 100.0 0.3 0:08.01 test_type_caste
Look for the number under ``RES`` there. You'll see it going up very quickly.
**Don't forget to Ctrl-C the test command** before your machine becomes
unresponsive due to swapping.
This method only takes a couple minutes of effort and is very conclusive.
What you want to see is that the ``RES`` number is stable after a couple
seconds.
CMake doesn't detect the right Python version CMake doesn't detect the right Python version
============================================= =============================================
@ -258,9 +302,9 @@ CMake configure line. (Replace ``$(which python)`` with a path to python if
your prefer.) your prefer.)
You can alternatively try ``-DPYBIND11_FINDPYTHON=ON``, which will activate the You can alternatively try ``-DPYBIND11_FINDPYTHON=ON``, which will activate the
new CMake FindPython support instead of pybind11's custom search. Requires new CMake FindPython support instead of pybind11's custom search. Newer CMake,
CMake 3.12+, and 3.15+ or 3.18.2+ are even better. You can set this in your like, 3.18.2+, is recommended. You can set this in your ``CMakeLists.txt``
``CMakeLists.txt`` before adding or finding pybind11, as well. before adding or finding pybind11, as well.
Inconsistent detection of Python version in CMake and pybind11 Inconsistent detection of Python version in CMake and pybind11
============================================================== ==============================================================
@ -281,11 +325,11 @@ There are three possible solutions:
from CMake and rely on pybind11 in detecting Python version. If this is not from CMake and rely on pybind11 in detecting Python version. If this is not
possible, the CMake machinery should be called *before* including pybind11. possible, the CMake machinery should be called *before* including pybind11.
2. Set ``PYBIND11_FINDPYTHON`` to ``True`` or use ``find_package(Python 2. Set ``PYBIND11_FINDPYTHON`` to ``True`` or use ``find_package(Python
COMPONENTS Interpreter Development)`` on modern CMake (3.12+, 3.15+ better, COMPONENTS Interpreter Development)`` on modern CMake ( 3.18.2+ best).
3.18.2+ best). Pybind11 in these cases uses the new CMake FindPython instead Pybind11 in these cases uses the new CMake FindPython instead of the old,
of the old, deprecated search tools, and these modules are much better at deprecated search tools, and these modules are much better at finding the
finding the correct Python. If FindPythonLibs/Interp are not available correct Python. If FindPythonLibs/Interp are not available (CMake 3.27+),
(CMake 3.27+), then this will be ignored and FindPython will be used. then this will be ignored and FindPython will be used.
3. Set ``PYBIND11_NOPYTHON`` to ``TRUE``. Pybind11 will not search for Python. 3. Set ``PYBIND11_NOPYTHON`` to ``TRUE``. Pybind11 will not search for Python.
However, you will have to use the target-based system, and do more setup However, you will have to use the target-based system, and do more setup
yourself, because it does not know about or include things that depend on yourself, because it does not know about or include things that depend on

View File

@ -50,10 +50,6 @@ clean, well written patch would likely be accepted to solve them.
One consequence is that containers of ``char *`` are currently not supported. One consequence is that containers of ``char *`` are currently not supported.
`#2245 <https://github.com/pybind/pybind11/issues/2245>`_ `#2245 <https://github.com/pybind/pybind11/issues/2245>`_
- The ``cpptest`` does not run on Windows with Python 3.8 or newer, due to DLL
loader changes. User code that is correctly installed should not be affected.
`#2560 <https://github.com/pybind/pybind11/issue/2560>`_
Python 3.9.0 warning Python 3.9.0 warning
^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^

View File

@ -16,9 +16,9 @@ breathe==4.35.0 \
--hash=sha256:5165541c3c67b6c7adde8b3ecfe895c6f7844783c4076b6d8d287e4f33d62386 \ --hash=sha256:5165541c3c67b6c7adde8b3ecfe895c6f7844783c4076b6d8d287e4f33d62386 \
--hash=sha256:52c581f42ca4310737f9e435e3851c3d1f15446205a85fbc272f1f97ed74f5be --hash=sha256:52c581f42ca4310737f9e435e3851c3d1f15446205a85fbc272f1f97ed74f5be
# via -r requirements.in # via -r requirements.in
certifi==2024.2.2 \ certifi==2024.7.4 \
--hash=sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f \ --hash=sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b \
--hash=sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1 --hash=sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90
# via requests # via requests
charset-normalizer==3.3.2 \ charset-normalizer==3.3.2 \
--hash=sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027 \ --hash=sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027 \
@ -269,7 +269,7 @@ sphinxcontrib-svg2pdfconverter==1.2.2 \
--hash=sha256:04ec767b55780a6b18d89cc1a8ada6d900c6efde9d1683abdb98a49b144465ca \ --hash=sha256:04ec767b55780a6b18d89cc1a8ada6d900c6efde9d1683abdb98a49b144465ca \
--hash=sha256:80a55ca61f70eae93efc65f3814f2f177c86ba55934a9f6c5022f1778b62146b --hash=sha256:80a55ca61f70eae93efc65f3814f2f177c86ba55934a9f6c5022f1778b62146b
# via -r requirements.in # via -r requirements.in
urllib3==2.2.1 \ urllib3==2.2.2 \
--hash=sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d \ --hash=sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472 \
--hash=sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19 --hash=sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168
# via requests # via requests

View File

@ -158,7 +158,7 @@ public:
} else { } else {
handle src_or_index = src; handle src_or_index = src;
// PyPy: 7.3.7's 3.8 does not implement PyLong_*'s __index__ calls. // PyPy: 7.3.7's 3.8 does not implement PyLong_*'s __index__ calls.
#if PY_VERSION_HEX < 0x03080000 || defined(PYPY_VERSION) #if defined(PYPY_VERSION)
object index; object index;
if (!PYBIND11_LONG_CHECK(src.ptr())) { // So: index_check(src.ptr()) if (!PYBIND11_LONG_CHECK(src.ptr())) { // So: index_check(src.ptr())
index = reinterpret_steal<object>(PyNumber_Index(src.ptr())); index = reinterpret_steal<object>(PyNumber_Index(src.ptr()));
@ -740,6 +740,13 @@ class type_caster<std::pair<T1, T2>> : public tuple_caster<std::pair, T1, T2> {}
template <typename... Ts> template <typename... Ts>
class type_caster<std::tuple<Ts...>> : public tuple_caster<std::tuple, Ts...> {}; class type_caster<std::tuple<Ts...>> : public tuple_caster<std::tuple, Ts...> {};
template <>
class type_caster<std::tuple<>> : public tuple_caster<std::tuple> {
public:
// PEP 484 specifies this syntax for an empty tuple
static constexpr auto name = const_name("tuple[()]");
};
/// Helper class which abstracts away certain actions. Users can provide specializations for /// Helper class which abstracts away certain actions. Users can provide specializations for
/// custom holders, but it's only necessary if the type has a non-standard interface. /// custom holders, but it's only necessary if the type has a non-standard interface.
template <typename T> template <typename T>
@ -787,11 +794,11 @@ protected:
} }
} }
bool load_value(value_and_holder &&v_h) { void load_value(value_and_holder &&v_h) {
if (v_h.holder_constructed()) { if (v_h.holder_constructed()) {
value = v_h.value_ptr(); value = v_h.value_ptr();
holder = v_h.template holder<holder_type>(); holder = v_h.template holder<holder_type>();
return true; return;
} }
throw cast_error("Unable to cast from non-held to held instance (T& to Holder<T>) " throw cast_error("Unable to cast from non-held to held instance (T& to Holder<T>) "
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES) #if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
@ -1496,7 +1503,7 @@ struct kw_only {};
/// \ingroup annotations /// \ingroup annotations
/// Annotation indicating that all previous arguments are positional-only; the is the equivalent of /// Annotation indicating that all previous arguments are positional-only; the is the equivalent of
/// an unnamed '/' argument (in Python 3.8) /// an unnamed '/' argument
struct pos_only {}; struct pos_only {};
template <typename T> template <typename T>

View File

@ -9,8 +9,10 @@
#pragma once #pragma once
#include "../attr.h" #include <pybind11/attr.h>
#include "../options.h" #include <pybind11/options.h>
#include "exception_translation.h"
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
@ -205,39 +207,40 @@ extern "C" inline PyObject *pybind11_meta_call(PyObject *type, PyObject *args, P
/// Cleanup the type-info for a pybind11-registered type. /// Cleanup the type-info for a pybind11-registered type.
extern "C" inline void pybind11_meta_dealloc(PyObject *obj) { extern "C" inline void pybind11_meta_dealloc(PyObject *obj) {
auto *type = (PyTypeObject *) obj; with_internals([obj](internals &internals) {
auto &internals = get_internals(); auto *type = (PyTypeObject *) obj;
// A pybind11-registered type will: // A pybind11-registered type will:
// 1) be found in internals.registered_types_py // 1) be found in internals.registered_types_py
// 2) have exactly one associated `detail::type_info` // 2) have exactly one associated `detail::type_info`
auto found_type = internals.registered_types_py.find(type); auto found_type = internals.registered_types_py.find(type);
if (found_type != internals.registered_types_py.end() && found_type->second.size() == 1 if (found_type != internals.registered_types_py.end() && found_type->second.size() == 1
&& found_type->second[0]->type == type) { && found_type->second[0]->type == type) {
auto *tinfo = found_type->second[0]; auto *tinfo = found_type->second[0];
auto tindex = std::type_index(*tinfo->cpptype); auto tindex = std::type_index(*tinfo->cpptype);
internals.direct_conversions.erase(tindex); internals.direct_conversions.erase(tindex);
if (tinfo->module_local) { if (tinfo->module_local) {
get_local_internals().registered_types_cpp.erase(tindex); get_local_internals().registered_types_cpp.erase(tindex);
} else {
internals.registered_types_cpp.erase(tindex);
}
internals.registered_types_py.erase(tinfo->type);
// Actually just `std::erase_if`, but that's only available in C++20
auto &cache = internals.inactive_override_cache;
for (auto it = cache.begin(), last = cache.end(); it != last;) {
if (it->first == (PyObject *) tinfo->type) {
it = cache.erase(it);
} else { } else {
++it; internals.registered_types_cpp.erase(tindex);
} }
} internals.registered_types_py.erase(tinfo->type);
delete tinfo; // Actually just `std::erase_if`, but that's only available in C++20
} auto &cache = internals.inactive_override_cache;
for (auto it = cache.begin(), last = cache.end(); it != last;) {
if (it->first == (PyObject *) tinfo->type) {
it = cache.erase(it);
} else {
++it;
}
}
delete tinfo;
}
});
PyType_Type.tp_dealloc(obj); PyType_Type.tp_dealloc(obj);
} }
@ -310,19 +313,20 @@ inline void traverse_offset_bases(void *valueptr,
} }
inline bool register_instance_impl(void *ptr, instance *self) { inline bool register_instance_impl(void *ptr, instance *self) {
get_internals().registered_instances.emplace(ptr, self); with_instance_map(ptr, [&](instance_map &instances) { instances.emplace(ptr, self); });
return true; // unused, but gives the same signature as the deregister func return true; // unused, but gives the same signature as the deregister func
} }
inline bool deregister_instance_impl(void *ptr, instance *self) { inline bool deregister_instance_impl(void *ptr, instance *self) {
auto &registered_instances = get_internals().registered_instances; return with_instance_map(ptr, [&](instance_map &instances) {
auto range = registered_instances.equal_range(ptr); auto range = instances.equal_range(ptr);
for (auto it = range.first; it != range.second; ++it) { for (auto it = range.first; it != range.second; ++it) {
if (self == it->second) { if (self == it->second) {
registered_instances.erase(it); instances.erase(it);
return true; return true;
}
} }
} return false;
return false; });
} }
inline void register_instance(instance *self, void *valptr, const type_info *tinfo) { inline void register_instance(instance *self, void *valptr, const type_info *tinfo) {
@ -377,27 +381,32 @@ extern "C" inline int pybind11_object_init(PyObject *self, PyObject *, PyObject
} }
inline void add_patient(PyObject *nurse, PyObject *patient) { inline void add_patient(PyObject *nurse, PyObject *patient) {
auto &internals = get_internals();
auto *instance = reinterpret_cast<detail::instance *>(nurse); auto *instance = reinterpret_cast<detail::instance *>(nurse);
instance->has_patients = true; instance->has_patients = true;
Py_INCREF(patient); Py_INCREF(patient);
internals.patients[nurse].push_back(patient);
with_internals([&](internals &internals) { internals.patients[nurse].push_back(patient); });
} }
inline void clear_patients(PyObject *self) { inline void clear_patients(PyObject *self) {
auto *instance = reinterpret_cast<detail::instance *>(self); auto *instance = reinterpret_cast<detail::instance *>(self);
auto &internals = get_internals(); std::vector<PyObject *> patients;
auto pos = internals.patients.find(self);
if (pos == internals.patients.end()) { with_internals([&](internals &internals) {
pybind11_fail("FATAL: Internal consistency check failed: Invalid clear_patients() call."); auto pos = internals.patients.find(self);
}
if (pos == internals.patients.end()) {
pybind11_fail(
"FATAL: Internal consistency check failed: Invalid clear_patients() call.");
}
// Clearing the patients can cause more Python code to run, which
// can invalidate the iterator. Extract the vector of patients
// from the unordered_map first.
patients = std::move(pos->second);
internals.patients.erase(pos);
});
// Clearing the patients can cause more Python code to run, which
// can invalidate the iterator. Extract the vector of patients
// from the unordered_map first.
auto patients = std::move(pos->second);
internals.patients.erase(pos);
instance->has_patients = false; instance->has_patients = false;
for (PyObject *&patient : patients) { for (PyObject *&patient : patients) {
Py_CLEAR(patient); Py_CLEAR(patient);
@ -459,19 +468,9 @@ extern "C" inline void pybind11_object_dealloc(PyObject *self) {
type->tp_free(self); type->tp_free(self);
#if PY_VERSION_HEX < 0x03080000
// `type->tp_dealloc != pybind11_object_dealloc` means that we're being called
// as part of a derived type's dealloc, in which case we're not allowed to decref
// the type here. For cross-module compatibility, we shouldn't compare directly
// with `pybind11_object_dealloc`, but with the common one stashed in internals.
auto pybind11_object_type = (PyTypeObject *) get_internals().instance_base;
if (type->tp_dealloc == pybind11_object_type->tp_dealloc)
Py_DECREF(type);
#else
// This was not needed before Python 3.8 (Python issue 35810) // This was not needed before Python 3.8 (Python issue 35810)
// https://github.com/pybind/pybind11/issues/1946 // https://github.com/pybind/pybind11/issues/1946
Py_DECREF(type); Py_DECREF(type);
#endif
} }
std::string error_string(); std::string error_string();
@ -560,17 +559,9 @@ inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type) {
type->tp_traverse = pybind11_traverse; type->tp_traverse = pybind11_traverse;
type->tp_clear = pybind11_clear; type->tp_clear = pybind11_clear;
static PyGetSetDef getset[] = {{ static PyGetSetDef getset[]
#if PY_VERSION_HEX < 0x03070000 = {{"__dict__", PyObject_GenericGetDict, PyObject_GenericSetDict, nullptr, nullptr},
const_cast<char *>("__dict__"), {nullptr, nullptr, nullptr, nullptr, nullptr}};
#else
"__dict__",
#endif
PyObject_GenericGetDict,
PyObject_GenericSetDict,
nullptr,
nullptr},
{nullptr, nullptr, nullptr, nullptr, nullptr}};
type->tp_getset = getset; type->tp_getset = getset;
} }
@ -592,7 +583,18 @@ extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int fla
return -1; return -1;
} }
std::memset(view, 0, sizeof(Py_buffer)); std::memset(view, 0, sizeof(Py_buffer));
buffer_info *info = tinfo->get_buffer(obj, tinfo->get_buffer_data); buffer_info *info = nullptr;
try {
info = tinfo->get_buffer(obj, tinfo->get_buffer_data);
} catch (...) {
try_translate_exceptions();
raise_from(PyExc_BufferError, "Error getting buffer");
return -1;
}
if (info == nullptr) {
pybind11_fail("FATAL UNEXPECTED SITUATION: tinfo->get_buffer() returned nullptr.");
}
if ((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE && info->readonly) { if ((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE && info->readonly) {
delete info; delete info;
// view->obj = nullptr; // Was just memset to 0, so not necessary // view->obj = nullptr; // Was just memset to 0, so not necessary
@ -662,10 +664,13 @@ inline PyObject *make_new_python_type(const type_record &rec) {
char *tp_doc = nullptr; char *tp_doc = nullptr;
if (rec.doc && options::show_user_defined_docstrings()) { if (rec.doc && options::show_user_defined_docstrings()) {
/* Allocate memory for docstring (using PyObject_MALLOC, since /* Allocate memory for docstring (Python will free this later on) */
Python will free this later on) */
size_t size = std::strlen(rec.doc) + 1; size_t size = std::strlen(rec.doc) + 1;
#if PY_VERSION_HEX >= 0x030D0000
tp_doc = (char *) PyMem_MALLOC(size);
#else
tp_doc = (char *) PyObject_MALLOC(size); tp_doc = (char *) PyObject_MALLOC(size);
#endif
std::memcpy((void *) tp_doc, rec.doc, size); std::memcpy((void *) tp_doc, rec.doc, size);
} }

View File

@ -10,12 +10,12 @@
#pragma once #pragma once
#define PYBIND11_VERSION_MAJOR 2 #define PYBIND11_VERSION_MAJOR 2
#define PYBIND11_VERSION_MINOR 13 #define PYBIND11_VERSION_MINOR 14
#define PYBIND11_VERSION_PATCH 0.dev1 #define PYBIND11_VERSION_PATCH 0.dev1
// Similar to Python's convention: https://docs.python.org/3/c-api/apiabiversion.html // Similar to Python's convention: https://docs.python.org/3/c-api/apiabiversion.html
// Additional convention: 0xD = dev // Additional convention: 0xD = dev
#define PYBIND11_VERSION_HEX 0x020D00D1 #define PYBIND11_VERSION_HEX 0x020E00D1
// Define some generic pybind11 helper macros for warning management. // Define some generic pybind11 helper macros for warning management.
// //
@ -272,9 +272,8 @@ PYBIND11_WARNING_DISABLE_MSVC(4505)
#endif #endif
#include <Python.h> #include <Python.h>
// Reminder: WITH_THREAD is always defined if PY_VERSION_HEX >= 0x03070000 #if PY_VERSION_HEX < 0x03080000
#if PY_VERSION_HEX < 0x03060000 # error "PYTHON < 3.8 IS UNSUPPORTED. pybind11 v2.13 was the last to support Python 3.7."
# error "PYTHON < 3.6 IS UNSUPPORTED. pybind11 v2.9 was the last to support Python 2 and 3.5."
#endif #endif
#include <frameobject.h> #include <frameobject.h>
#include <pythread.h> #include <pythread.h>
@ -463,8 +462,26 @@ PYBIND11_WARNING_POP
return "Hello, World!"; return "Hello, World!";
}); });
} }
The third macro argument is optional (available since 2.13.0), and can be used to
mark the extension module as safe to run without the GIL under a free-threaded CPython
interpreter. Passing this argument has no effect on other interpreters.
.. code-block:: cpp
PYBIND11_MODULE(example, m, py::mod_gil_not_used()) {
m.doc() = "pybind11 example module safe to run without the GIL";
// Add bindings here
m.def("foo", []() {
return "Hello, Free-threaded World!";
});
}
\endrst */ \endrst */
#define PYBIND11_MODULE(name, variable) \ PYBIND11_WARNING_PUSH
PYBIND11_WARNING_DISABLE_CLANG("-Wgnu-zero-variadic-macro-arguments")
#define PYBIND11_MODULE(name, variable, ...) \
static ::pybind11::module_::module_def PYBIND11_CONCAT(pybind11_module_def_, name) \ static ::pybind11::module_::module_def PYBIND11_CONCAT(pybind11_module_def_, name) \
PYBIND11_MAYBE_UNUSED; \ PYBIND11_MAYBE_UNUSED; \
PYBIND11_MAYBE_UNUSED \ PYBIND11_MAYBE_UNUSED \
@ -473,7 +490,10 @@ PYBIND11_WARNING_POP
PYBIND11_CHECK_PYTHON_VERSION \ PYBIND11_CHECK_PYTHON_VERSION \
PYBIND11_ENSURE_INTERNALS_READY \ PYBIND11_ENSURE_INTERNALS_READY \
auto m = ::pybind11::module_::create_extension_module( \ auto m = ::pybind11::module_::create_extension_module( \
PYBIND11_TOSTRING(name), nullptr, &PYBIND11_CONCAT(pybind11_module_def_, name)); \ PYBIND11_TOSTRING(name), \
nullptr, \
&PYBIND11_CONCAT(pybind11_module_def_, name), \
##__VA_ARGS__); \
try { \ try { \
PYBIND11_CONCAT(pybind11_init_, name)(m); \ PYBIND11_CONCAT(pybind11_init_, name)(m); \
return m.ptr(); \ return m.ptr(); \
@ -481,6 +501,7 @@ PYBIND11_WARNING_POP
PYBIND11_CATCH_INIT_EXCEPTIONS \ PYBIND11_CATCH_INIT_EXCEPTIONS \
} \ } \
void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ & (variable)) void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ & (variable))
PYBIND11_WARNING_POP
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
@ -536,7 +557,7 @@ enum class return_value_policy : uint8_t {
object without taking ownership similar to the above object without taking ownership similar to the above
return_value_policy::reference policy. In contrast to that policy, the return_value_policy::reference policy. In contrast to that policy, the
function or property's implicit this argument (called the parent) is function or property's implicit this argument (called the parent) is
considered to be the the owner of the return value (the child). considered to be the owner of the return value (the child).
pybind11 then couples the lifetime of the parent to the child via a pybind11 then couples the lifetime of the parent to the child via a
reference relationship that ensures that the parent cannot be garbage reference relationship that ensures that the parent cannot be garbage
collected while Python is still using the child. More advanced collected while Python is still using the child. More advanced

View File

@ -0,0 +1,77 @@
// Copyright (c) 2024 The pybind Community.
#pragma once
#include <pybind11/pytypes.h>
#include "common.h"
#include "internals.h"
#include <typeinfo>
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail)
// Forward declaration needed here: Refactoring opportunity.
extern "C" inline PyObject *pybind11_object_new(PyTypeObject *type, PyObject *, PyObject *);
inline bool type_is_managed_by_our_internals(PyTypeObject *type_obj) {
#if defined(PYPY_VERSION)
auto &internals = get_internals();
return bool(internals.registered_types_py.find(type_obj)
!= internals.registered_types_py.end());
#else
return bool(type_obj->tp_new == pybind11_object_new);
#endif
}
inline bool is_instance_method_of_type(PyTypeObject *type_obj, PyObject *attr_name) {
PyObject *descr = _PyType_Lookup(type_obj, attr_name);
return bool((descr != nullptr) && PyInstanceMethod_Check(descr));
}
inline object try_get_cpp_conduit_method(PyObject *obj) {
if (PyType_Check(obj)) {
return object();
}
PyTypeObject *type_obj = Py_TYPE(obj);
str attr_name("_pybind11_conduit_v1_");
bool assumed_to_be_callable = false;
if (type_is_managed_by_our_internals(type_obj)) {
if (!is_instance_method_of_type(type_obj, attr_name.ptr())) {
return object();
}
assumed_to_be_callable = true;
}
PyObject *method = PyObject_GetAttr(obj, attr_name.ptr());
if (method == nullptr) {
PyErr_Clear();
return object();
}
if (!assumed_to_be_callable && PyCallable_Check(method) == 0) {
Py_DECREF(method);
return object();
}
return reinterpret_steal<object>(method);
}
inline void *try_raw_pointer_ephemeral_from_cpp_conduit(handle src,
const std::type_info *cpp_type_info) {
object method = try_get_cpp_conduit_method(src.ptr());
if (method) {
capsule cpp_type_info_capsule(const_cast<void *>(static_cast<const void *>(cpp_type_info)),
typeid(std::type_info).name());
object cpp_conduit = method(bytes(PYBIND11_PLATFORM_ABI_ID),
cpp_type_info_capsule,
bytes("raw_pointer_ephemeral"));
if (isinstance<capsule>(cpp_conduit)) {
return reinterpret_borrow<capsule>(cpp_conduit).get_pointer();
}
}
return nullptr;
}
#define PYBIND11_HAS_CPP_CONDUIT 1
PYBIND11_NAMESPACE_END(detail)
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)

View File

@ -0,0 +1,71 @@
/*
pybind11/detail/exception_translation.h: means to translate C++ exceptions to Python exceptions
Copyright (c) 2024 The Pybind Development Team.
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#pragma once
#include "common.h"
#include "internals.h"
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail)
// Apply all the extensions translators from a list
// Return true if one of the translators completed without raising an exception
// itself. Return of false indicates that if there are other translators
// available, they should be tried.
inline bool apply_exception_translators(std::forward_list<ExceptionTranslator> &translators) {
auto last_exception = std::current_exception();
for (auto &translator : translators) {
try {
translator(last_exception);
return true;
} catch (...) {
last_exception = std::current_exception();
}
}
return false;
}
inline void try_translate_exceptions() {
/* When an exception is caught, give each registered exception
translator a chance to translate it to a Python exception. First
all module-local translators will be tried in reverse order of
registration. If none of the module-locale translators handle
the exception (or there are no module-locale translators) then
the global translators will be tried, also in reverse order of
registration.
A translator may choose to do one of the following:
- catch the exception and call py::set_error()
to set a standard (or custom) Python exception, or
- do nothing and let the exception fall through to the next translator, or
- delegate translation to the next translator by throwing a new type of exception.
*/
bool handled = with_exception_translators(
[&](std::forward_list<ExceptionTranslator> &exception_translators,
std::forward_list<ExceptionTranslator> &local_exception_translators) {
if (detail::apply_exception_translators(local_exception_translators)) {
return true;
}
if (detail::apply_exception_translators(exception_translators)) {
return true;
}
return false;
});
if (!handled) {
set_error(PyExc_SystemError, "Exception escaped from default exception translator!");
}
}
PYBIND11_NAMESPACE_END(detail)
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)

View File

@ -128,11 +128,13 @@ void construct(value_and_holder &v_h, Cpp<Class> *ptr, bool need_alias) {
// the holder and destruction happens when we leave the C++ scope, and the holder // the holder and destruction happens when we leave the C++ scope, and the holder
// class gets to handle the destruction however it likes. // class gets to handle the destruction however it likes.
v_h.value_ptr() = ptr; v_h.value_ptr() = ptr;
v_h.set_instance_registered(true); // To prevent init_instance from registering it v_h.set_instance_registered(true); // Trick to prevent init_instance from registering it
v_h.type->init_instance(v_h.inst, nullptr); // Set up the holder // DANGER ZONE BEGIN: exceptions will leave v_h in an invalid state.
v_h.type->init_instance(v_h.inst, nullptr); // Set up the holder
Holder<Class> temp_holder(std::move(v_h.holder<Holder<Class>>())); // Steal the holder Holder<Class> temp_holder(std::move(v_h.holder<Holder<Class>>())); // Steal the holder
v_h.type->dealloc(v_h); // Destroys the moved-out holder remains, resets value ptr to null v_h.type->dealloc(v_h); // Destroys the moved-out holder remains, resets value ptr to null
v_h.set_instance_registered(false); v_h.set_instance_registered(false);
// DANGER ZONE END.
construct_alias_from_cpp<Class>(is_alias_constructible<Class>{}, v_h, std::move(*ptr)); construct_alias_from_cpp<Class>(is_alias_constructible<Class>{}, v_h, std::move(*ptr));
} else { } else {

View File

@ -11,13 +11,15 @@
#include "common.h" #include "common.h"
#if defined(WITH_THREAD) && defined(PYBIND11_SIMPLE_GIL_MANAGEMENT) #if defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
# include "../gil.h" # include <pybind11/gil.h>
#endif #endif
#include "../pytypes.h" #include <pybind11/pytypes.h>
#include <exception> #include <exception>
#include <mutex>
#include <thread>
/// Tracks the `internals` and `type_info` ABI version independent of the main library version. /// Tracks the `internals` and `type_info` ABI version independent of the main library version.
/// ///
@ -37,7 +39,11 @@
# if PY_VERSION_HEX >= 0x030C0000 || defined(_MSC_VER) # if PY_VERSION_HEX >= 0x030C0000 || defined(_MSC_VER)
// Version bump for Python 3.12+, before first 3.12 beta release. // Version bump for Python 3.12+, before first 3.12 beta release.
// Version bump for MSVC piggy-backed on PR #4779. See comments there. // Version bump for MSVC piggy-backed on PR #4779. See comments there.
# define PYBIND11_INTERNALS_VERSION 5 # ifdef Py_GIL_DISABLED
# define PYBIND11_INTERNALS_VERSION 6
# else
# define PYBIND11_INTERNALS_VERSION 5
# endif
# else # else
# define PYBIND11_INTERNALS_VERSION 4 # define PYBIND11_INTERNALS_VERSION 4
# endif # endif
@ -62,65 +68,41 @@ inline PyObject *make_object_base_type(PyTypeObject *metaclass);
// The old Python Thread Local Storage (TLS) API is deprecated in Python 3.7 in favor of the new // The old Python Thread Local Storage (TLS) API is deprecated in Python 3.7 in favor of the new
// Thread Specific Storage (TSS) API. // Thread Specific Storage (TSS) API.
#if PY_VERSION_HEX >= 0x03070000
// Avoid unnecessary allocation of `Py_tss_t`, since we cannot use // Avoid unnecessary allocation of `Py_tss_t`, since we cannot use
// `Py_LIMITED_API` anyway. // `Py_LIMITED_API` anyway.
# if PYBIND11_INTERNALS_VERSION > 4 #if PYBIND11_INTERNALS_VERSION > 4
# define PYBIND11_TLS_KEY_REF Py_tss_t & # define PYBIND11_TLS_KEY_REF Py_tss_t &
# if defined(__clang__) # if defined(__clang__)
# define PYBIND11_TLS_KEY_INIT(var) \ # define PYBIND11_TLS_KEY_INIT(var) \
_Pragma("clang diagnostic push") /**/ \ _Pragma("clang diagnostic push") /**/ \
_Pragma("clang diagnostic ignored \"-Wmissing-field-initializers\"") /**/ \ _Pragma("clang diagnostic ignored \"-Wmissing-field-initializers\"") /**/ \
Py_tss_t var \ Py_tss_t var \
= Py_tss_NEEDS_INIT; \ = Py_tss_NEEDS_INIT; \
_Pragma("clang diagnostic pop") _Pragma("clang diagnostic pop")
# elif defined(__GNUC__) && !defined(__INTEL_COMPILER) # elif defined(__GNUC__) && !defined(__INTEL_COMPILER)
# define PYBIND11_TLS_KEY_INIT(var) \ # define PYBIND11_TLS_KEY_INIT(var) \
_Pragma("GCC diagnostic push") /**/ \ _Pragma("GCC diagnostic push") /**/ \
_Pragma("GCC diagnostic ignored \"-Wmissing-field-initializers\"") /**/ \ _Pragma("GCC diagnostic ignored \"-Wmissing-field-initializers\"") /**/ \
Py_tss_t var \ Py_tss_t var \
= Py_tss_NEEDS_INIT; \ = Py_tss_NEEDS_INIT; \
_Pragma("GCC diagnostic pop") _Pragma("GCC diagnostic pop")
# else
# define PYBIND11_TLS_KEY_INIT(var) Py_tss_t var = Py_tss_NEEDS_INIT;
# endif
# define PYBIND11_TLS_KEY_CREATE(var) (PyThread_tss_create(&(var)) == 0)
# define PYBIND11_TLS_GET_VALUE(key) PyThread_tss_get(&(key))
# define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_tss_set(&(key), (value))
# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_tss_set(&(key), nullptr)
# define PYBIND11_TLS_FREE(key) PyThread_tss_delete(&(key))
# else # else
# define PYBIND11_TLS_KEY_REF Py_tss_t * # define PYBIND11_TLS_KEY_INIT(var) Py_tss_t var = Py_tss_NEEDS_INIT;
# define PYBIND11_TLS_KEY_INIT(var) Py_tss_t *var = nullptr;
# define PYBIND11_TLS_KEY_CREATE(var) \
(((var) = PyThread_tss_alloc()) != nullptr && (PyThread_tss_create((var)) == 0))
# define PYBIND11_TLS_GET_VALUE(key) PyThread_tss_get((key))
# define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_tss_set((key), (value))
# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_tss_set((key), nullptr)
# define PYBIND11_TLS_FREE(key) PyThread_tss_free(key)
# endif # endif
# define PYBIND11_TLS_KEY_CREATE(var) (PyThread_tss_create(&(var)) == 0)
# define PYBIND11_TLS_GET_VALUE(key) PyThread_tss_get(&(key))
# define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_tss_set(&(key), (value))
# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_tss_set(&(key), nullptr)
# define PYBIND11_TLS_FREE(key) PyThread_tss_delete(&(key))
#else #else
// Usually an int but a long on Cygwin64 with Python 3.x # define PYBIND11_TLS_KEY_REF Py_tss_t *
# define PYBIND11_TLS_KEY_REF decltype(PyThread_create_key()) # define PYBIND11_TLS_KEY_INIT(var) Py_tss_t *var = nullptr;
# define PYBIND11_TLS_KEY_INIT(var) PYBIND11_TLS_KEY_REF var = 0; # define PYBIND11_TLS_KEY_CREATE(var) \
# define PYBIND11_TLS_KEY_CREATE(var) (((var) = PyThread_create_key()) != -1) (((var) = PyThread_tss_alloc()) != nullptr && (PyThread_tss_create((var)) == 0))
# define PYBIND11_TLS_GET_VALUE(key) PyThread_get_key_value((key)) # define PYBIND11_TLS_GET_VALUE(key) PyThread_tss_get((key))
# if defined(PYPY_VERSION) # define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_tss_set((key), (value))
// On CPython < 3.4 and on PyPy, `PyThread_set_key_value` strangely does not set # define PYBIND11_TLS_DELETE_VALUE(key) PyThread_tss_set((key), nullptr)
// the value if it has already been set. Instead, it must first be deleted and # define PYBIND11_TLS_FREE(key) PyThread_tss_free(key)
// then set again.
inline void tls_replace_value(PYBIND11_TLS_KEY_REF key, void *value) {
PyThread_delete_key_value(key);
PyThread_set_key_value(key, value);
}
# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_delete_key_value(key)
# define PYBIND11_TLS_REPLACE_VALUE(key, value) \
::pybind11::detail::tls_replace_value((key), (value))
# else
# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_set_key_value((key), nullptr)
# define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_set_key_value((key), (value))
# endif
# define PYBIND11_TLS_FREE(key) (void) key
#endif #endif
// Python loads modules by default with dlopen with the RTLD_LOCAL flag; under libc++ and possibly // Python loads modules by default with dlopen with the RTLD_LOCAL flag; under libc++ and possibly
@ -168,15 +150,49 @@ struct override_hash {
} }
}; };
using instance_map = std::unordered_multimap<const void *, instance *>;
#ifdef Py_GIL_DISABLED
// Wrapper around PyMutex to provide BasicLockable semantics
class pymutex {
PyMutex mutex;
public:
pymutex() : mutex({}) {}
void lock() { PyMutex_Lock(&mutex); }
void unlock() { PyMutex_Unlock(&mutex); }
};
// Instance map shards are used to reduce mutex contention in free-threaded Python.
struct instance_map_shard {
instance_map registered_instances;
pymutex mutex;
// alignas(64) would be better, but causes compile errors in macOS before 10.14 (see #5200)
char padding[64 - (sizeof(instance_map) + sizeof(pymutex)) % 64];
};
static_assert(sizeof(instance_map_shard) % 64 == 0,
"instance_map_shard size is not a multiple of 64 bytes");
#endif
/// Internal data structure used to track registered instances and types. /// Internal data structure used to track registered instances and types.
/// Whenever binary incompatible changes are made to this structure, /// Whenever binary incompatible changes are made to this structure,
/// `PYBIND11_INTERNALS_VERSION` must be incremented. /// `PYBIND11_INTERNALS_VERSION` must be incremented.
struct internals { struct internals {
#ifdef Py_GIL_DISABLED
pymutex mutex;
pymutex exception_translator_mutex;
#endif
// std::type_index -> pybind11's type information // std::type_index -> pybind11's type information
type_map<type_info *> registered_types_cpp; type_map<type_info *> registered_types_cpp;
// PyTypeObject* -> base type_info(s) // PyTypeObject* -> base type_info(s)
std::unordered_map<PyTypeObject *, std::vector<type_info *>> registered_types_py; std::unordered_map<PyTypeObject *, std::vector<type_info *>> registered_types_py;
std::unordered_multimap<const void *, instance *> registered_instances; // void * -> instance* #ifdef Py_GIL_DISABLED
std::unique_ptr<instance_map_shard[]> instance_shards; // void * -> instance*
size_t instance_shards_mask;
#else
instance_map registered_instances; // void * -> instance*
#endif
std::unordered_set<std::pair<const PyObject *, const char *>, override_hash> std::unordered_set<std::pair<const PyObject *, const char *>, override_hash>
inactive_override_cache; inactive_override_cache;
type_map<std::vector<bool (*)(PyObject *, void *&)>> direct_conversions; type_map<std::vector<bool (*)(PyObject *, void *&)>> direct_conversions;
@ -192,28 +208,27 @@ struct internals {
PyTypeObject *static_property_type; PyTypeObject *static_property_type;
PyTypeObject *default_metaclass; PyTypeObject *default_metaclass;
PyObject *instance_base; PyObject *instance_base;
#if defined(WITH_THREAD)
// Unused if PYBIND11_SIMPLE_GIL_MANAGEMENT is defined: // Unused if PYBIND11_SIMPLE_GIL_MANAGEMENT is defined:
PYBIND11_TLS_KEY_INIT(tstate) PYBIND11_TLS_KEY_INIT(tstate)
# if PYBIND11_INTERNALS_VERSION > 4 #if PYBIND11_INTERNALS_VERSION > 4
PYBIND11_TLS_KEY_INIT(loader_life_support_tls_key) PYBIND11_TLS_KEY_INIT(loader_life_support_tls_key)
# endif // PYBIND11_INTERNALS_VERSION > 4 #endif // PYBIND11_INTERNALS_VERSION > 4
// Unused if PYBIND11_SIMPLE_GIL_MANAGEMENT is defined: // Unused if PYBIND11_SIMPLE_GIL_MANAGEMENT is defined:
PyInterpreterState *istate = nullptr; PyInterpreterState *istate = nullptr;
# if PYBIND11_INTERNALS_VERSION > 4 #if PYBIND11_INTERNALS_VERSION > 4
// Note that we have to use a std::string to allocate memory to ensure a unique address // Note that we have to use a std::string to allocate memory to ensure a unique address
// We want unique addresses since we use pointer equality to compare function records // We want unique addresses since we use pointer equality to compare function records
std::string function_record_capsule_name = internals_function_record_capsule_name; std::string function_record_capsule_name = internals_function_record_capsule_name;
# endif #endif
internals() = default; internals() = default;
internals(const internals &other) = delete; internals(const internals &other) = delete;
internals &operator=(const internals &other) = delete; internals &operator=(const internals &other) = delete;
~internals() { ~internals() {
# if PYBIND11_INTERNALS_VERSION > 4 #if PYBIND11_INTERNALS_VERSION > 4
PYBIND11_TLS_FREE(loader_life_support_tls_key); PYBIND11_TLS_FREE(loader_life_support_tls_key);
# endif // PYBIND11_INTERNALS_VERSION > 4 #endif // PYBIND11_INTERNALS_VERSION > 4
// This destructor is called *after* Py_Finalize() in finalize_interpreter(). // This destructor is called *after* Py_Finalize() in finalize_interpreter().
// That *SHOULD BE* fine. The following details what happens when PyThread_tss_free is // That *SHOULD BE* fine. The following details what happens when PyThread_tss_free is
@ -224,7 +239,6 @@ struct internals {
// that the `tstate` be allocated with the CPython allocator. // that the `tstate` be allocated with the CPython allocator.
PYBIND11_TLS_FREE(tstate); PYBIND11_TLS_FREE(tstate);
} }
#endif
}; };
/// Additional type information which does not fit into the PyTypeObject. /// Additional type information which does not fit into the PyTypeObject.
@ -309,22 +323,20 @@ struct type_info {
#endif #endif
#ifndef PYBIND11_INTERNALS_KIND #ifndef PYBIND11_INTERNALS_KIND
# if defined(WITH_THREAD) # define PYBIND11_INTERNALS_KIND ""
# define PYBIND11_INTERNALS_KIND ""
# else
# define PYBIND11_INTERNALS_KIND "_without_thread"
# endif
#endif #endif
#define PYBIND11_PLATFORM_ABI_ID \
PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI \
PYBIND11_BUILD_TYPE
#define PYBIND11_INTERNALS_ID \ #define PYBIND11_INTERNALS_ID \
"__pybind11_internals_v" PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) \ "__pybind11_internals_v" PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) \
PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB \ PYBIND11_PLATFORM_ABI_ID "__"
PYBIND11_BUILD_ABI PYBIND11_BUILD_TYPE "__"
#define PYBIND11_MODULE_LOCAL_ID \ #define PYBIND11_MODULE_LOCAL_ID \
"__pybind11_module_local_v" PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) \ "__pybind11_module_local_v" PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) \
PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB \ PYBIND11_PLATFORM_ABI_ID "__"
PYBIND11_BUILD_ABI PYBIND11_BUILD_TYPE "__"
/// Each module locally stores a pointer to the `internals` data. The data /// Each module locally stores a pointer to the `internals` data. The data
/// itself is shared among modules with the same `PYBIND11_INTERNALS_ID`. /// itself is shared among modules with the same `PYBIND11_INTERNALS_ID`.
@ -442,7 +454,7 @@ inline void translate_local_exception(std::exception_ptr p) {
inline object get_python_state_dict() { inline object get_python_state_dict() {
object state_dict; object state_dict;
#if PYBIND11_INTERNALS_VERSION <= 4 || PY_VERSION_HEX < 0x03080000 || defined(PYPY_VERSION) #if PYBIND11_INTERNALS_VERSION <= 4 || defined(PYPY_VERSION)
state_dict = reinterpret_borrow<object>(PyEval_GetBuiltins()); state_dict = reinterpret_borrow<object>(PyEval_GetBuiltins());
#else #else
# if PY_VERSION_HEX < 0x03090000 # if PY_VERSION_HEX < 0x03090000
@ -462,7 +474,8 @@ inline object get_python_state_dict() {
} }
inline object get_internals_obj_from_state_dict(handle state_dict) { inline object get_internals_obj_from_state_dict(handle state_dict) {
return reinterpret_borrow<object>(dict_getitemstring(state_dict.ptr(), PYBIND11_INTERNALS_ID)); return reinterpret_steal<object>(
dict_getitemstringref(state_dict.ptr(), PYBIND11_INTERNALS_ID));
} }
inline internals **get_internals_pp_from_capsule(handle obj) { inline internals **get_internals_pp_from_capsule(handle obj) {
@ -474,6 +487,20 @@ inline internals **get_internals_pp_from_capsule(handle obj) {
return static_cast<internals **>(raw_ptr); return static_cast<internals **>(raw_ptr);
} }
inline uint64_t round_up_to_next_pow2(uint64_t x) {
// Round-up to the next power of two.
// See https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
x--;
x |= (x >> 1);
x |= (x >> 2);
x |= (x >> 4);
x |= (x >> 8);
x |= (x >> 16);
x |= (x >> 32);
x++;
return x;
}
/// Return a reference to the current `internals` data /// Return a reference to the current `internals` data
PYBIND11_NOINLINE internals &get_internals() { PYBIND11_NOINLINE internals &get_internals() {
auto **&internals_pp = get_internals_pp(); auto **&internals_pp = get_internals_pp();
@ -481,10 +508,9 @@ PYBIND11_NOINLINE internals &get_internals() {
return **internals_pp; return **internals_pp;
} }
#if defined(WITH_THREAD) #if defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
# if defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
gil_scoped_acquire gil; gil_scoped_acquire gil;
# else #else
// Ensure that the GIL is held since we will need to make Python calls. // Ensure that the GIL is held since we will need to make Python calls.
// Cannot use py::gil_scoped_acquire here since that constructor calls get_internals. // Cannot use py::gil_scoped_acquire here since that constructor calls get_internals.
struct gil_scoped_acquire_local { struct gil_scoped_acquire_local {
@ -494,7 +520,6 @@ PYBIND11_NOINLINE internals &get_internals() {
~gil_scoped_acquire_local() { PyGILState_Release(state); } ~gil_scoped_acquire_local() { PyGILState_Release(state); }
const PyGILState_STATE state; const PyGILState_STATE state;
} gil; } gil;
# endif
#endif #endif
error_scope err_scope; error_scope err_scope;
@ -519,7 +544,6 @@ PYBIND11_NOINLINE internals &get_internals() {
} }
auto *&internals_ptr = *internals_pp; auto *&internals_ptr = *internals_pp;
internals_ptr = new internals(); internals_ptr = new internals();
#if defined(WITH_THREAD)
PyThreadState *tstate = PyThreadState_Get(); PyThreadState *tstate = PyThreadState_Get();
// NOLINTNEXTLINE(bugprone-assignment-in-if-condition) // NOLINTNEXTLINE(bugprone-assignment-in-if-condition)
@ -528,20 +552,29 @@ PYBIND11_NOINLINE internals &get_internals() {
} }
PYBIND11_TLS_REPLACE_VALUE(internals_ptr->tstate, tstate); PYBIND11_TLS_REPLACE_VALUE(internals_ptr->tstate, tstate);
# if PYBIND11_INTERNALS_VERSION > 4 #if PYBIND11_INTERNALS_VERSION > 4
// NOLINTNEXTLINE(bugprone-assignment-in-if-condition) // NOLINTNEXTLINE(bugprone-assignment-in-if-condition)
if (!PYBIND11_TLS_KEY_CREATE(internals_ptr->loader_life_support_tls_key)) { if (!PYBIND11_TLS_KEY_CREATE(internals_ptr->loader_life_support_tls_key)) {
pybind11_fail("get_internals: could not successfully initialize the " pybind11_fail("get_internals: could not successfully initialize the "
"loader_life_support TSS key!"); "loader_life_support TSS key!");
} }
# endif
internals_ptr->istate = tstate->interp;
#endif #endif
state_dict[PYBIND11_INTERNALS_ID] = capsule(internals_pp); internals_ptr->istate = tstate->interp;
state_dict[PYBIND11_INTERNALS_ID] = capsule(reinterpret_cast<void *>(internals_pp));
internals_ptr->registered_exception_translators.push_front(&translate_exception); internals_ptr->registered_exception_translators.push_front(&translate_exception);
internals_ptr->static_property_type = make_static_property_type(); internals_ptr->static_property_type = make_static_property_type();
internals_ptr->default_metaclass = make_default_metaclass(); internals_ptr->default_metaclass = make_default_metaclass();
internals_ptr->instance_base = make_object_base_type(internals_ptr->default_metaclass); internals_ptr->instance_base = make_object_base_type(internals_ptr->default_metaclass);
#ifdef Py_GIL_DISABLED
// Scale proportional to the number of cores. 2x is a heuristic to reduce contention.
auto num_shards
= static_cast<size_t>(round_up_to_next_pow2(2 * std::thread::hardware_concurrency()));
if (num_shards == 0) {
num_shards = 1;
}
internals_ptr->instance_shards.reset(new instance_map_shard[num_shards]);
internals_ptr->instance_shards_mask = num_shards - 1;
#endif // Py_GIL_DISABLED
} }
return **internals_pp; return **internals_pp;
} }
@ -555,7 +588,7 @@ PYBIND11_NOINLINE internals &get_internals() {
struct local_internals { struct local_internals {
type_map<type_info *> registered_types_cpp; type_map<type_info *> registered_types_cpp;
std::forward_list<ExceptionTranslator> registered_exception_translators; std::forward_list<ExceptionTranslator> registered_exception_translators;
#if defined(WITH_THREAD) && PYBIND11_INTERNALS_VERSION == 4 #if PYBIND11_INTERNALS_VERSION == 4
// For ABI compatibility, we can't store the loader_life_support TLS key in // For ABI compatibility, we can't store the loader_life_support TLS key in
// the `internals` struct directly. Instead, we store it in `shared_data` and // the `internals` struct directly. Instead, we store it in `shared_data` and
@ -588,7 +621,7 @@ struct local_internals {
loader_life_support_tls_key loader_life_support_tls_key
= static_cast<shared_loader_life_support_data *>(ptr)->loader_life_support_tls_key; = static_cast<shared_loader_life_support_data *>(ptr)->loader_life_support_tls_key;
} }
#endif // defined(WITH_THREAD) && PYBIND11_INTERNALS_VERSION == 4 #endif // PYBIND11_INTERNALS_VERSION == 4
}; };
/// Works like `get_internals`, but for things which are locally registered. /// Works like `get_internals`, but for things which are locally registered.
@ -602,13 +635,93 @@ inline local_internals &get_local_internals() {
return *locals; return *locals;
} }
#ifdef Py_GIL_DISABLED
# define PYBIND11_LOCK_INTERNALS(internals) std::unique_lock<pymutex> lock((internals).mutex)
#else
# define PYBIND11_LOCK_INTERNALS(internals)
#endif
template <typename F>
inline auto with_internals(const F &cb) -> decltype(cb(get_internals())) {
auto &internals = get_internals();
PYBIND11_LOCK_INTERNALS(internals);
return cb(internals);
}
template <typename F>
inline auto with_exception_translators(const F &cb)
-> decltype(cb(get_internals().registered_exception_translators,
get_local_internals().registered_exception_translators)) {
auto &internals = get_internals();
#ifdef Py_GIL_DISABLED
std::unique_lock<pymutex> lock((internals).exception_translator_mutex);
#endif
auto &local_internals = get_local_internals();
return cb(internals.registered_exception_translators,
local_internals.registered_exception_translators);
}
inline std::uint64_t mix64(std::uint64_t z) {
// David Stafford's variant 13 of the MurmurHash3 finalizer popularized
// by the SplitMix PRNG.
// https://zimbry.blogspot.com/2011/09/better-bit-mixing-improving-on.html
z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
return z ^ (z >> 31);
}
template <typename F>
inline auto with_instance_map(const void *ptr,
const F &cb) -> decltype(cb(std::declval<instance_map &>())) {
auto &internals = get_internals();
#ifdef Py_GIL_DISABLED
// Hash address to compute shard, but ignore low bits. We'd like allocations
// from the same thread/core to map to the same shard and allocations from
// other threads/cores to map to other shards. Using the high bits is a good
// heuristic because memory allocators often have a per-thread
// arena/superblock/segment from which smaller allocations are served.
auto addr = reinterpret_cast<std::uintptr_t>(ptr);
auto hash = mix64(static_cast<std::uint64_t>(addr >> 20));
auto idx = static_cast<size_t>(hash & internals.instance_shards_mask);
auto &shard = internals.instance_shards[idx];
std::unique_lock<pymutex> lock(shard.mutex);
return cb(shard.registered_instances);
#else
(void) ptr;
return cb(internals.registered_instances);
#endif
}
// Returns the number of registered instances for testing purposes. The result may not be
// consistent if other threads are registering or unregistering instances concurrently.
inline size_t num_registered_instances() {
auto &internals = get_internals();
#ifdef Py_GIL_DISABLED
size_t count = 0;
for (size_t i = 0; i <= internals.instance_shards_mask; ++i) {
auto &shard = internals.instance_shards[i];
std::unique_lock<pymutex> lock(shard.mutex);
count += shard.registered_instances.size();
}
return count;
#else
return internals.registered_instances.size();
#endif
}
/// Constructs a std::string with the given arguments, stores it in `internals`, and returns its /// Constructs a std::string with the given arguments, stores it in `internals`, and returns its
/// `c_str()`. Such strings objects have a long storage duration -- the internal strings are only /// `c_str()`. Such strings objects have a long storage duration -- the internal strings are only
/// cleared when the program exits or after interpreter shutdown (when embedding), and so are /// cleared when the program exits or after interpreter shutdown (when embedding), and so are
/// suitable for c-style strings needed by Python internals (such as PyTypeObject's tp_name). /// suitable for c-style strings needed by Python internals (such as PyTypeObject's tp_name).
template <typename... Args> template <typename... Args>
const char *c_str(Args &&...args) { const char *c_str(Args &&...args) {
auto &strings = get_internals().static_strings; // GCC 4.8 doesn't like parameter unpack within lambda capture, so use
// PYBIND11_LOCK_INTERNALS.
auto &internals = get_internals();
PYBIND11_LOCK_INTERNALS(internals);
auto &strings = internals.static_strings;
strings.emplace_front(std::forward<Args>(args)...); strings.emplace_front(std::forward<Args>(args)...);
return strings.front().c_str(); return strings.front().c_str();
} }
@ -638,15 +751,18 @@ PYBIND11_NAMESPACE_END(detail)
/// pybind11 version) running in the current interpreter. Names starting with underscores /// pybind11 version) running in the current interpreter. Names starting with underscores
/// are reserved for internal usage. Returns `nullptr` if no matching entry was found. /// are reserved for internal usage. Returns `nullptr` if no matching entry was found.
PYBIND11_NOINLINE void *get_shared_data(const std::string &name) { PYBIND11_NOINLINE void *get_shared_data(const std::string &name) {
auto &internals = detail::get_internals(); return detail::with_internals([&](detail::internals &internals) {
auto it = internals.shared_data.find(name); auto it = internals.shared_data.find(name);
return it != internals.shared_data.end() ? it->second : nullptr; return it != internals.shared_data.end() ? it->second : nullptr;
});
} }
/// Set the shared data that can be later recovered by `get_shared_data()`. /// Set the shared data that can be later recovered by `get_shared_data()`.
PYBIND11_NOINLINE void *set_shared_data(const std::string &name, void *data) { PYBIND11_NOINLINE void *set_shared_data(const std::string &name, void *data) {
detail::get_internals().shared_data[name] = data; return detail::with_internals([&](detail::internals &internals) {
return data; internals.shared_data[name] = data;
return data;
});
} }
/// Returns a typed reference to a shared data entry (by using `get_shared_data()`) if /// Returns a typed reference to a shared data entry (by using `get_shared_data()`) if
@ -654,14 +770,15 @@ PYBIND11_NOINLINE void *set_shared_data(const std::string &name, void *data) {
/// added to the shared data under the given name and a reference to it is returned. /// added to the shared data under the given name and a reference to it is returned.
template <typename T> template <typename T>
T &get_or_create_shared_data(const std::string &name) { T &get_or_create_shared_data(const std::string &name) {
auto &internals = detail::get_internals(); return *detail::with_internals([&](detail::internals &internals) {
auto it = internals.shared_data.find(name); auto it = internals.shared_data.find(name);
T *ptr = (T *) (it != internals.shared_data.end() ? it->second : nullptr); T *ptr = (T *) (it != internals.shared_data.end() ? it->second : nullptr);
if (!ptr) { if (!ptr) {
ptr = new T(); ptr = new T();
internals.shared_data[name] = ptr; internals.shared_data[name] = ptr;
} }
return *ptr; return ptr;
});
} }
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)

View File

@ -9,15 +9,20 @@
#pragma once #pragma once
#include "../pytypes.h" #include <pybind11/pytypes.h>
#include "common.h" #include "common.h"
#include "cpp_conduit.h"
#include "descr.h" #include "descr.h"
#include "internals.h" #include "internals.h"
#include "typeid.h" #include "typeid.h"
#include "value_and_holder.h"
#include <cstdint> #include <cstdint>
#include <cstring>
#include <iterator> #include <iterator>
#include <new> #include <new>
#include <stdexcept>
#include <string> #include <string>
#include <type_traits> #include <type_traits>
#include <typeindex> #include <typeindex>
@ -36,14 +41,13 @@ private:
loader_life_support *parent = nullptr; loader_life_support *parent = nullptr;
std::unordered_set<PyObject *> keep_alive; std::unordered_set<PyObject *> keep_alive;
#if defined(WITH_THREAD)
// Store stack pointer in thread-local storage. // Store stack pointer in thread-local storage.
static PYBIND11_TLS_KEY_REF get_stack_tls_key() { static PYBIND11_TLS_KEY_REF get_stack_tls_key() {
# if PYBIND11_INTERNALS_VERSION == 4 #if PYBIND11_INTERNALS_VERSION == 4
return get_local_internals().loader_life_support_tls_key; return get_local_internals().loader_life_support_tls_key;
# else #else
return get_internals().loader_life_support_tls_key; return get_internals().loader_life_support_tls_key;
# endif #endif
} }
static loader_life_support *get_stack_top() { static loader_life_support *get_stack_top() {
return static_cast<loader_life_support *>(PYBIND11_TLS_GET_VALUE(get_stack_tls_key())); return static_cast<loader_life_support *>(PYBIND11_TLS_GET_VALUE(get_stack_tls_key()));
@ -51,15 +55,6 @@ private:
static void set_stack_top(loader_life_support *value) { static void set_stack_top(loader_life_support *value) {
PYBIND11_TLS_REPLACE_VALUE(get_stack_tls_key(), value); PYBIND11_TLS_REPLACE_VALUE(get_stack_tls_key(), value);
} }
#else
// Use single global variable for stack.
static loader_life_support **get_stack_pp() {
static loader_life_support *global_stack = nullptr;
return global_stack;
}
static loader_life_support *get_stack_top() { return *get_stack_pp(); }
static void set_stack_top(loader_life_support *value) { *get_stack_pp() = value; }
#endif
public: public:
/// A new patient frame is created when a function is entered /// A new patient frame is created when a function is entered
@ -217,12 +212,15 @@ inline detail::type_info *get_local_type_info(const std::type_index &tp) {
} }
inline detail::type_info *get_global_type_info(const std::type_index &tp) { inline detail::type_info *get_global_type_info(const std::type_index &tp) {
auto &types = get_internals().registered_types_cpp; return with_internals([&](internals &internals) {
auto it = types.find(tp); detail::type_info *type_info = nullptr;
if (it != types.end()) { auto &types = internals.registered_types_cpp;
return it->second; auto it = types.find(tp);
} if (it != types.end()) {
return nullptr; type_info = it->second;
}
return type_info;
});
} }
/// Return the type info for a given C++ type; on lookup failure can either throw or return /// Return the type info for a given C++ type; on lookup failure can either throw or return
@ -253,78 +251,19 @@ PYBIND11_NOINLINE handle get_type_handle(const std::type_info &tp, bool throw_if
// Searches the inheritance graph for a registered Python instance, using all_type_info(). // Searches the inheritance graph for a registered Python instance, using all_type_info().
PYBIND11_NOINLINE handle find_registered_python_instance(void *src, PYBIND11_NOINLINE handle find_registered_python_instance(void *src,
const detail::type_info *tinfo) { const detail::type_info *tinfo) {
auto it_instances = get_internals().registered_instances.equal_range(src); return with_instance_map(src, [&](instance_map &instances) {
for (auto it_i = it_instances.first; it_i != it_instances.second; ++it_i) { auto it_instances = instances.equal_range(src);
for (auto *instance_type : detail::all_type_info(Py_TYPE(it_i->second))) { for (auto it_i = it_instances.first; it_i != it_instances.second; ++it_i) {
if (instance_type && same_type(*instance_type->cpptype, *tinfo->cpptype)) { for (auto *instance_type : detail::all_type_info(Py_TYPE(it_i->second))) {
return handle((PyObject *) it_i->second).inc_ref(); if (instance_type && same_type(*instance_type->cpptype, *tinfo->cpptype)) {
return handle((PyObject *) it_i->second).inc_ref();
}
} }
} }
} return handle();
return handle(); });
} }
struct value_and_holder {
instance *inst = nullptr;
size_t index = 0u;
const detail::type_info *type = nullptr;
void **vh = nullptr;
// Main constructor for a found value/holder:
value_and_holder(instance *i, const detail::type_info *type, size_t vpos, size_t index)
: inst{i}, index{index}, type{type},
vh{inst->simple_layout ? inst->simple_value_holder
: &inst->nonsimple.values_and_holders[vpos]} {}
// Default constructor (used to signal a value-and-holder not found by get_value_and_holder())
value_and_holder() = default;
// Used for past-the-end iterator
explicit value_and_holder(size_t index) : index{index} {}
template <typename V = void>
V *&value_ptr() const {
return reinterpret_cast<V *&>(vh[0]);
}
// True if this `value_and_holder` has a non-null value pointer
explicit operator bool() const { return value_ptr() != nullptr; }
template <typename H>
H &holder() const {
return reinterpret_cast<H &>(vh[1]);
}
bool holder_constructed() const {
return inst->simple_layout
? inst->simple_holder_constructed
: (inst->nonsimple.status[index] & instance::status_holder_constructed) != 0u;
}
// NOLINTNEXTLINE(readability-make-member-function-const)
void set_holder_constructed(bool v = true) {
if (inst->simple_layout) {
inst->simple_holder_constructed = v;
} else if (v) {
inst->nonsimple.status[index] |= instance::status_holder_constructed;
} else {
inst->nonsimple.status[index] &= (std::uint8_t) ~instance::status_holder_constructed;
}
}
bool instance_registered() const {
return inst->simple_layout
? inst->simple_instance_registered
: ((inst->nonsimple.status[index] & instance::status_instance_registered) != 0);
}
// NOLINTNEXTLINE(readability-make-member-function-const)
void set_instance_registered(bool v = true) {
if (inst->simple_layout) {
inst->simple_instance_registered = v;
} else if (v) {
inst->nonsimple.status[index] |= instance::status_instance_registered;
} else {
inst->nonsimple.status[index] &= (std::uint8_t) ~instance::status_instance_registered;
}
}
};
// Container for accessing and iterating over an instance's values/holders // Container for accessing and iterating over an instance's values/holders
struct values_and_holders { struct values_and_holders {
private: private:
@ -493,7 +432,7 @@ PYBIND11_NOINLINE void instance::allocate_layout() {
// NOLINTNEXTLINE(readability-make-member-function-const) // NOLINTNEXTLINE(readability-make-member-function-const)
PYBIND11_NOINLINE void instance::deallocate_layout() { PYBIND11_NOINLINE void instance::deallocate_layout() {
if (!simple_layout) { if (!simple_layout) {
PyMem_Free(nonsimple.values_and_holders); PyMem_Free(reinterpret_cast<void *>(nonsimple.values_and_holders));
} }
} }
@ -506,16 +445,17 @@ PYBIND11_NOINLINE bool isinstance_generic(handle obj, const std::type_info &tp)
} }
PYBIND11_NOINLINE handle get_object_handle(const void *ptr, const detail::type_info *type) { PYBIND11_NOINLINE handle get_object_handle(const void *ptr, const detail::type_info *type) {
auto &instances = get_internals().registered_instances; return with_instance_map(ptr, [&](instance_map &instances) {
auto range = instances.equal_range(ptr); auto range = instances.equal_range(ptr);
for (auto it = range.first; it != range.second; ++it) { for (auto it = range.first; it != range.second; ++it) {
for (const auto &vh : values_and_holders(it->second)) { for (const auto &vh : values_and_holders(it->second)) {
if (vh.type == type) { if (vh.type == type) {
return handle((PyObject *) it->second); return handle((PyObject *) it->second);
}
} }
} }
} return handle();
return handle(); });
} }
inline PyThreadState *get_thread_state_unchecked() { inline PyThreadState *get_thread_state_unchecked() {
@ -674,6 +614,13 @@ public:
} }
return false; return false;
} }
bool try_cpp_conduit(handle src) {
value = try_raw_pointer_ephemeral_from_cpp_conduit(src, cpptype);
if (value != nullptr) {
return true;
}
return false;
}
void check_holder_compat() {} void check_holder_compat() {}
PYBIND11_NOINLINE static void *local_load(PyObject *src, const type_info *ti) { PYBIND11_NOINLINE static void *local_load(PyObject *src, const type_info *ti) {
@ -805,6 +752,10 @@ public:
return true; return true;
} }
if (convert && cpptype && this_.try_cpp_conduit(src)) {
return true;
}
return false; return false;
} }
@ -832,6 +783,32 @@ public:
void *value = nullptr; void *value = nullptr;
}; };
inline object cpp_conduit_method(handle self,
const bytes &pybind11_platform_abi_id,
const capsule &cpp_type_info_capsule,
const bytes &pointer_kind) {
#ifdef PYBIND11_HAS_STRING_VIEW
using cpp_str = std::string_view;
#else
using cpp_str = std::string;
#endif
if (cpp_str(pybind11_platform_abi_id) != PYBIND11_PLATFORM_ABI_ID) {
return none();
}
if (std::strcmp(cpp_type_info_capsule.name(), typeid(std::type_info).name()) != 0) {
return none();
}
if (cpp_str(pointer_kind) != "raw_pointer_ephemeral") {
throw std::runtime_error("Invalid pointer_kind: \"" + std::string(pointer_kind) + "\"");
}
const auto *cpp_type_info = cpp_type_info_capsule.get_pointer<const std::type_info>();
type_caster_generic caster(*cpp_type_info);
if (!caster.load(self, false)) {
return none();
}
return capsule(caster.value, cpp_type_info->name());
}
/** /**
* Determine suitable casting operator for pointer-or-lvalue-casting type casters. The type caster * Determine suitable casting operator for pointer-or-lvalue-casting type casters. The type caster
* needs to provide `operator T*()` and `operator T&()` operators. * needs to provide `operator T*()` and `operator T&()` operators.
@ -1111,11 +1088,11 @@ public:
|| policy == return_value_policy::automatic_reference) { || policy == return_value_policy::automatic_reference) {
policy = return_value_policy::copy; policy = return_value_policy::copy;
} }
return cast(&src, policy, parent); return cast(std::addressof(src), policy, parent);
} }
static handle cast(itype &&src, return_value_policy, handle parent) { static handle cast(itype &&src, return_value_policy, handle parent) {
return cast(&src, return_value_policy::move, parent); return cast(std::addressof(src), return_value_policy::move, parent);
} }
// Returns a (pointer, type_info) pair taking care of necessary type lookup for a // Returns a (pointer, type_info) pair taking care of necessary type lookup for a

View File

@ -0,0 +1,77 @@
// Copyright (c) 2016-2024 The Pybind Development Team.
// All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#pragma once
#include "common.h"
#include <cstddef>
#include <typeinfo>
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail)
struct value_and_holder {
instance *inst = nullptr;
size_t index = 0u;
const detail::type_info *type = nullptr;
void **vh = nullptr;
// Main constructor for a found value/holder:
value_and_holder(instance *i, const detail::type_info *type, size_t vpos, size_t index)
: inst{i}, index{index}, type{type},
vh{inst->simple_layout ? inst->simple_value_holder
: &inst->nonsimple.values_and_holders[vpos]} {}
// Default constructor (used to signal a value-and-holder not found by get_value_and_holder())
value_and_holder() = default;
// Used for past-the-end iterator
explicit value_and_holder(size_t index) : index{index} {}
template <typename V = void>
V *&value_ptr() const {
return reinterpret_cast<V *&>(vh[0]);
}
// True if this `value_and_holder` has a non-null value pointer
explicit operator bool() const { return value_ptr() != nullptr; }
template <typename H>
H &holder() const {
return reinterpret_cast<H &>(vh[1]);
}
bool holder_constructed() const {
return inst->simple_layout
? inst->simple_holder_constructed
: (inst->nonsimple.status[index] & instance::status_holder_constructed) != 0u;
}
// NOLINTNEXTLINE(readability-make-member-function-const)
void set_holder_constructed(bool v = true) {
if (inst->simple_layout) {
inst->simple_holder_constructed = v;
} else if (v) {
inst->nonsimple.status[index] |= instance::status_holder_constructed;
} else {
inst->nonsimple.status[index] &= (std::uint8_t) ~instance::status_holder_constructed;
}
}
bool instance_registered() const {
return inst->simple_layout
? inst->simple_instance_registered
: ((inst->nonsimple.status[index] & instance::status_instance_registered) != 0);
}
// NOLINTNEXTLINE(readability-make-member-function-const)
void set_instance_registered(bool v = true) {
if (inst->simple_layout) {
inst->simple_instance_registered = v;
} else if (v) {
inst->nonsimple.status[index] |= instance::status_instance_registered;
} else {
inst->nonsimple.status[index] &= (std::uint8_t) ~instance::status_instance_registered;
}
}
};
PYBIND11_NAMESPACE_END(detail)
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)

View File

@ -9,7 +9,8 @@
#pragma once #pragma once
#include "../numpy.h" #include <pybind11/numpy.h>
#include "common.h" #include "common.h"
/* HINT: To suppress warnings originating from the Eigen headers, use -isystem. /* HINT: To suppress warnings originating from the Eigen headers, use -isystem.

View File

@ -7,7 +7,8 @@
#pragma once #pragma once
#include "../numpy.h" #include <pybind11/numpy.h>
#include "common.h" #include "common.h"
#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) #if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)
@ -469,9 +470,6 @@ struct type_caster<Eigen::TensorMap<Type, Options>,
parent_object = reinterpret_borrow<object>(parent); parent_object = reinterpret_borrow<object>(parent);
break; break;
case return_value_policy::take_ownership:
delete src;
// fallthrough
default: default:
// move, take_ownership don't make any sense for a ref/map: // move, take_ownership don't make any sense for a ref/map:
pybind11_fail("Invalid return_value_policy for Eigen Map type, must be either " pybind11_fail("Invalid return_value_policy for Eigen Map type, must be either "

View File

@ -103,19 +103,6 @@ inline void initialize_interpreter_pre_pyconfig(bool init_signal_handlers,
bool add_program_dir_to_path) { bool add_program_dir_to_path) {
detail::precheck_interpreter(); detail::precheck_interpreter();
Py_InitializeEx(init_signal_handlers ? 1 : 0); Py_InitializeEx(init_signal_handlers ? 1 : 0);
# if defined(WITH_THREAD) && PY_VERSION_HEX < 0x03070000
PyEval_InitThreads();
# endif
// Before it was special-cased in python 3.8, passing an empty or null argv
// caused a segfault, so we have to reimplement the special case ourselves.
bool special_case = (argv == nullptr || argc <= 0);
const char *const empty_argv[]{"\0"};
const char *const *safe_argv = special_case ? empty_argv : argv;
if (special_case) {
argc = 1;
}
auto argv_size = static_cast<size_t>(argc); auto argv_size = static_cast<size_t>(argc);
// SetArgv* on python 3 takes wchar_t, so we have to convert. // SetArgv* on python 3 takes wchar_t, so we have to convert.
@ -123,7 +110,7 @@ inline void initialize_interpreter_pre_pyconfig(bool init_signal_handlers,
std::vector<std::unique_ptr<wchar_t[], detail::wide_char_arg_deleter>> widened_argv_entries; std::vector<std::unique_ptr<wchar_t[], detail::wide_char_arg_deleter>> widened_argv_entries;
widened_argv_entries.reserve(argv_size); widened_argv_entries.reserve(argv_size);
for (size_t ii = 0; ii < argv_size; ++ii) { for (size_t ii = 0; ii < argv_size; ++ii) {
widened_argv_entries.emplace_back(detail::widen_chars(safe_argv[ii])); widened_argv_entries.emplace_back(detail::widen_chars(argv[ii]));
if (!widened_argv_entries.back()) { if (!widened_argv_entries.back()) {
// A null here indicates a character-encoding failure or the python // A null here indicates a character-encoding failure or the python
// interpreter out of memory. Give up. // interpreter out of memory. Give up.

View File

@ -19,7 +19,7 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
inline void ensure_builtins_in_globals(object &global) { inline void ensure_builtins_in_globals(object &global) {
#if defined(PYPY_VERSION) || PY_VERSION_HEX < 0x03080000 #if defined(PYPY_VERSION)
// Running exec and eval adds `builtins` module under `__builtins__` key to // Running exec and eval adds `builtins` module under `__builtins__` key to
// globals if not yet present. Python 3.8 made PyRun_String behave // globals if not yet present. Python 3.8 made PyRun_String behave
// similarly. Let's also do that for older versions, for consistency. This // similarly. Let's also do that for older versions, for consistency. This

View File

@ -9,12 +9,55 @@
#pragma once #pragma once
#define PYBIND11_HAS_TYPE_CASTER_STD_FUNCTION_SPECIALIZATIONS
#include "pybind11.h" #include "pybind11.h"
#include <functional> #include <functional>
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
PYBIND11_NAMESPACE_BEGIN(type_caster_std_function_specializations)
// ensure GIL is held during functor destruction
struct func_handle {
function f;
#if !(defined(_MSC_VER) && _MSC_VER == 1916 && defined(PYBIND11_CPP17))
// This triggers a syntax error under very special conditions (very weird indeed).
explicit
#endif
func_handle(function &&f_) noexcept
: f(std::move(f_)) {
}
func_handle(const func_handle &f_) { operator=(f_); }
func_handle &operator=(const func_handle &f_) {
gil_scoped_acquire acq;
f = f_.f;
return *this;
}
~func_handle() {
gil_scoped_acquire acq;
function kill_f(std::move(f));
}
};
// to emulate 'move initialization capture' in C++11
struct func_wrapper_base {
func_handle hfunc;
explicit func_wrapper_base(func_handle &&hf) noexcept : hfunc(hf) {}
};
template <typename Return, typename... Args>
struct func_wrapper : func_wrapper_base {
using func_wrapper_base::func_wrapper_base;
Return operator()(Args... args) const {
gil_scoped_acquire acq;
// casts the returned object as a rvalue to the return type
return hfunc.f(std::forward<Args>(args)...).template cast<Return>();
}
};
PYBIND11_NAMESPACE_END(type_caster_std_function_specializations)
template <typename Return, typename... Args> template <typename Return, typename... Args>
struct type_caster<std::function<Return(Args...)>> { struct type_caster<std::function<Return(Args...)>> {
@ -77,40 +120,8 @@ public:
// See PR #1413 for full details // See PR #1413 for full details
} }
// ensure GIL is held during functor destruction value = type_caster_std_function_specializations::func_wrapper<Return, Args...>(
struct func_handle { type_caster_std_function_specializations::func_handle(std::move(func)));
function f;
#if !(defined(_MSC_VER) && _MSC_VER == 1916 && defined(PYBIND11_CPP17))
// This triggers a syntax error under very special conditions (very weird indeed).
explicit
#endif
func_handle(function &&f_) noexcept
: f(std::move(f_)) {
}
func_handle(const func_handle &f_) { operator=(f_); }
func_handle &operator=(const func_handle &f_) {
gil_scoped_acquire acq;
f = f_.f;
return *this;
}
~func_handle() {
gil_scoped_acquire acq;
function kill_f(std::move(f));
}
};
// to emulate 'move initialization capture' in C++11
struct func_wrapper {
func_handle hfunc;
explicit func_wrapper(func_handle &&hf) noexcept : hfunc(std::move(hf)) {}
Return operator()(Args... args) const {
gil_scoped_acquire acq;
// casts the returned object as a rvalue to the return type
return hfunc.f(std::forward<Args>(args)...).template cast<Return>();
}
};
value = func_wrapper(func_handle(std::move(func)));
return true; return true;
} }

View File

@ -13,7 +13,7 @@
#include <cassert> #include <cassert>
#if defined(WITH_THREAD) && !defined(PYBIND11_SIMPLE_GIL_MANAGEMENT) #if !defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
# include "detail/internals.h" # include "detail/internals.h"
#endif #endif
@ -26,9 +26,7 @@ PyThreadState *get_thread_state_unchecked();
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
#if defined(WITH_THREAD) #if !defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
# if !defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
/* The functions below essentially reproduce the PyGILState_* API using a RAII /* The functions below essentially reproduce the PyGILState_* API using a RAII
* pattern, but there are a few important differences: * pattern, but there are a few important differences:
@ -69,11 +67,11 @@ public:
if (!tstate) { if (!tstate) {
tstate = PyThreadState_New(internals.istate); tstate = PyThreadState_New(internals.istate);
# if defined(PYBIND11_DETAILED_ERROR_MESSAGES) # if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
if (!tstate) { if (!tstate) {
pybind11_fail("scoped_acquire: could not create thread state!"); pybind11_fail("scoped_acquire: could not create thread state!");
} }
# endif # endif
tstate->gilstate_counter = 0; tstate->gilstate_counter = 0;
PYBIND11_TLS_REPLACE_VALUE(internals.tstate, tstate); PYBIND11_TLS_REPLACE_VALUE(internals.tstate, tstate);
} else { } else {
@ -94,20 +92,20 @@ public:
PYBIND11_NOINLINE void dec_ref() { PYBIND11_NOINLINE void dec_ref() {
--tstate->gilstate_counter; --tstate->gilstate_counter;
# if defined(PYBIND11_DETAILED_ERROR_MESSAGES) # if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
if (detail::get_thread_state_unchecked() != tstate) { if (detail::get_thread_state_unchecked() != tstate) {
pybind11_fail("scoped_acquire::dec_ref(): thread state must be current!"); pybind11_fail("scoped_acquire::dec_ref(): thread state must be current!");
} }
if (tstate->gilstate_counter < 0) { if (tstate->gilstate_counter < 0) {
pybind11_fail("scoped_acquire::dec_ref(): reference count underflow!"); pybind11_fail("scoped_acquire::dec_ref(): reference count underflow!");
} }
# endif # endif
if (tstate->gilstate_counter == 0) { if (tstate->gilstate_counter == 0) {
# if defined(PYBIND11_DETAILED_ERROR_MESSAGES) # if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
if (!release) { if (!release) {
pybind11_fail("scoped_acquire::dec_ref(): internal error!"); pybind11_fail("scoped_acquire::dec_ref(): internal error!");
} }
# endif # endif
PyThreadState_Clear(tstate); PyThreadState_Clear(tstate);
if (active) { if (active) {
PyThreadState_DeleteCurrent(); PyThreadState_DeleteCurrent();
@ -149,9 +147,7 @@ public:
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
tstate = PyEval_SaveThread(); tstate = PyEval_SaveThread();
if (disassoc) { if (disassoc) {
// Python >= 3.7 can remove this, it's an int before 3.7 auto key = internals.tstate; // NOLINT(readability-qualified-auto)
// NOLINTNEXTLINE(readability-qualified-auto)
auto key = internals.tstate;
PYBIND11_TLS_DELETE_VALUE(key); PYBIND11_TLS_DELETE_VALUE(key);
} }
} }
@ -175,9 +171,7 @@ public:
PyEval_RestoreThread(tstate); PyEval_RestoreThread(tstate);
} }
if (disassoc) { if (disassoc) {
// Python >= 3.7 can remove this, it's an int before 3.7 auto key = detail::get_internals().tstate; // NOLINT(readability-qualified-auto)
// NOLINTNEXTLINE(readability-qualified-auto)
auto key = detail::get_internals().tstate;
PYBIND11_TLS_REPLACE_VALUE(key, tstate); PYBIND11_TLS_REPLACE_VALUE(key, tstate);
} }
} }
@ -188,7 +182,7 @@ private:
bool active = true; bool active = true;
}; };
# else // PYBIND11_SIMPLE_GIL_MANAGEMENT #else // PYBIND11_SIMPLE_GIL_MANAGEMENT
class gil_scoped_acquire { class gil_scoped_acquire {
PyGILState_STATE state; PyGILState_STATE state;
@ -216,32 +210,6 @@ public:
void disarm() {} void disarm() {}
}; };
# endif // PYBIND11_SIMPLE_GIL_MANAGEMENT #endif // PYBIND11_SIMPLE_GIL_MANAGEMENT
#else // WITH_THREAD
class gil_scoped_acquire {
public:
gil_scoped_acquire() {
// Trick to suppress `unused variable` error messages (at call sites).
(void) (this != (this + 1));
}
gil_scoped_acquire(const gil_scoped_acquire &) = delete;
gil_scoped_acquire &operator=(const gil_scoped_acquire &) = delete;
void disarm() {}
};
class gil_scoped_release {
public:
gil_scoped_release() {
// Trick to suppress `unused variable` error messages (at call sites).
(void) (this != (this + 1));
}
gil_scoped_release(const gil_scoped_release &) = delete;
gil_scoped_release &operator=(const gil_scoped_release &) = delete;
void disarm() {}
};
#endif // WITH_THREAD
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)

View File

@ -8,6 +8,10 @@
#include <cassert> #include <cassert>
#include <mutex> #include <mutex>
#ifdef Py_GIL_DISABLED
# include <atomic>
#endif
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
// Use the `gil_safe_call_once_and_store` class below instead of the naive // Use the `gil_safe_call_once_and_store` class below instead of the naive
@ -82,7 +86,12 @@ public:
private: private:
alignas(T) char storage_[sizeof(T)] = {}; alignas(T) char storage_[sizeof(T)] = {};
std::once_flag once_flag_ = {}; std::once_flag once_flag_ = {};
bool is_initialized_ = false; #ifdef Py_GIL_DISABLED
std::atomic_bool
#else
bool
#endif
is_initialized_{false};
// The `is_initialized_`-`storage_` pair is very similar to `std::optional`, // The `is_initialized_`-`storage_` pair is very similar to `std::optional`,
// but the latter does not have the triviality properties of former, // but the latter does not have the triviality properties of former,
// therefore `std::optional` is not a viable alternative here. // therefore `std::optional` is not a viable alternative here.

View File

@ -901,7 +901,11 @@ public:
template <typename T> template <typename T>
array(ShapeContainer shape, StridesContainer strides, const T *ptr, handle base = handle()) array(ShapeContainer shape, StridesContainer strides, const T *ptr, handle base = handle())
: array(pybind11::dtype::of<T>(), std::move(shape), std::move(strides), ptr, base) {} : array(pybind11::dtype::of<T>(),
std::move(shape),
std::move(strides),
reinterpret_cast<const void *>(ptr),
base) {}
template <typename T> template <typename T>
array(ShapeContainer shape, const T *ptr, handle base = handle()) array(ShapeContainer shape, const T *ptr, handle base = handle())
@ -1553,7 +1557,9 @@ PYBIND11_NOINLINE void register_structured_dtype(any_container<field_descriptor>
auto tindex = std::type_index(tinfo); auto tindex = std::type_index(tinfo);
numpy_internals.registered_dtypes[tindex] = {dtype_ptr, std::move(format_str)}; numpy_internals.registered_dtypes[tindex] = {dtype_ptr, std::move(format_str)};
get_internals().direct_conversions[tindex].push_back(direct_converter); with_internals([tindex, &direct_converter](internals &internals) {
internals.direct_conversions[tindex].push_back(direct_converter);
});
} }
template <typename T, typename SFINAE> template <typename T, typename SFINAE>
@ -1984,7 +1990,7 @@ private:
// Pointers to values the function was called with; the vectorized ones set here will start // Pointers to values the function was called with; the vectorized ones set here will start
// out as array_t<T> pointers, but they will be changed them to T pointers before we make // out as array_t<T> pointers, but they will be changed them to T pointers before we make
// call the wrapped function. Non-vectorized pointers are left as-is. // call the wrapped function. Non-vectorized pointers are left as-is.
std::array<void *, N> params{{&args...}}; std::array<void *, N> params{{reinterpret_cast<void *>(&args)...}};
// The array of `buffer_info`s of vectorized arguments: // The array of `buffer_info`s of vectorized arguments:
std::array<buffer_info, NVectorized> buffers{ std::array<buffer_info, NVectorized> buffers{

View File

@ -9,8 +9,8 @@
*/ */
#pragma once #pragma once
#include "detail/class.h" #include "detail/class.h"
#include "detail/exception_translation.h"
#include "detail/init.h" #include "detail/init.h"
#include "attr.h" #include "attr.h"
#include "gil.h" #include "gil.h"
@ -95,24 +95,6 @@ inline std::string replace_newlines_and_squash(const char *text) {
return result.substr(str_begin, str_range); return result.substr(str_begin, str_range);
} }
// Apply all the extensions translators from a list
// Return true if one of the translators completed without raising an exception
// itself. Return of false indicates that if there are other translators
// available, they should be tried.
inline bool apply_exception_translators(std::forward_list<ExceptionTranslator> &translators) {
auto last_exception = std::current_exception();
for (auto &translator : translators) {
try {
translator(last_exception);
return true;
} catch (...) {
last_exception = std::current_exception();
}
}
return false;
}
#if defined(_MSC_VER) #if defined(_MSC_VER)
# define PYBIND11_COMPAT_STRDUP _strdup # define PYBIND11_COMPAT_STRDUP _strdup
#else #else
@ -610,7 +592,8 @@ protected:
int index = 0; int index = 0;
/* Create a nice pydoc rec including all signatures and /* Create a nice pydoc rec including all signatures and
docstrings of the functions in the overload chain */ docstrings of the functions in the overload chain */
if (chain && options::show_function_signatures()) { if (chain && options::show_function_signatures()
&& std::strcmp(rec->name, "_pybind11_conduit_v1_") != 0) {
// First a generic signature // First a generic signature
signatures += rec->name; signatures += rec->name;
signatures += "(*args, **kwargs)\n"; signatures += "(*args, **kwargs)\n";
@ -619,7 +602,8 @@ protected:
// Then specific overload signatures // Then specific overload signatures
bool first_user_def = true; bool first_user_def = true;
for (auto *it = chain_start; it != nullptr; it = it->next) { for (auto *it = chain_start; it != nullptr; it = it->next) {
if (options::show_function_signatures()) { if (options::show_function_signatures()
&& std::strcmp(rec->name, "_pybind11_conduit_v1_") != 0) {
if (index > 0) { if (index > 0) {
signatures += '\n'; signatures += '\n';
} }
@ -1038,33 +1022,7 @@ protected:
throw; throw;
#endif #endif
} catch (...) { } catch (...) {
/* When an exception is caught, give each registered exception try_translate_exceptions();
translator a chance to translate it to a Python exception. First
all module-local translators will be tried in reverse order of
registration. If none of the module-locale translators handle
the exception (or there are no module-locale translators) then
the global translators will be tried, also in reverse order of
registration.
A translator may choose to do one of the following:
- catch the exception and call py::set_error()
to set a standard (or custom) Python exception, or
- do nothing and let the exception fall through to the next translator, or
- delegate translation to the next translator by throwing a new type of exception.
*/
auto &local_exception_translators
= get_local_internals().registered_exception_translators;
if (detail::apply_exception_translators(local_exception_translators)) {
return nullptr;
}
auto &exception_translators = get_internals().registered_exception_translators;
if (detail::apply_exception_translators(exception_translators)) {
return nullptr;
}
set_error(PyExc_SystemError, "Exception escaped from default exception translator!");
return nullptr; return nullptr;
} }
@ -1199,6 +1157,16 @@ struct handle_type_name<cpp_function> {
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
// Use to activate Py_MOD_GIL_NOT_USED.
class mod_gil_not_used {
public:
explicit mod_gil_not_used(bool flag = true) : flag_(flag) {}
bool flag() const { return flag_; }
private:
bool flag_;
};
/// Wrapper for Python extension modules /// Wrapper for Python extension modules
class module_ : public object { class module_ : public object {
public: public:
@ -1299,7 +1267,11 @@ public:
``def`` should point to a statically allocated module_def. ``def`` should point to a statically allocated module_def.
\endrst */ \endrst */
static module_ create_extension_module(const char *name, const char *doc, module_def *def) { static module_ create_extension_module(const char *name,
const char *doc,
module_def *def,
mod_gil_not_used gil_not_used
= mod_gil_not_used(false)) {
// module_def is PyModuleDef // module_def is PyModuleDef
// Placement new (not an allocation). // Placement new (not an allocation).
def = new (def) def = new (def)
@ -1319,6 +1291,11 @@ public:
} }
pybind11_fail("Internal error in module_::create_extension_module()"); pybind11_fail("Internal error in module_::create_extension_module()");
} }
if (gil_not_used.flag()) {
#ifdef Py_GIL_DISABLED
PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED);
#endif
}
// TODO: Should be reinterpret_steal for Python 3, but Python also steals it again when // TODO: Should be reinterpret_steal for Python 3, but Python also steals it again when
// returned from PyInit_... // returned from PyInit_...
// For Python 2, reinterpret_borrow was correct. // For Python 2, reinterpret_borrow was correct.
@ -1397,15 +1374,26 @@ protected:
tinfo->default_holder = rec.default_holder; tinfo->default_holder = rec.default_holder;
tinfo->module_local = rec.module_local; tinfo->module_local = rec.module_local;
auto &internals = get_internals(); with_internals([&](internals &internals) {
auto tindex = std::type_index(*rec.type); auto tindex = std::type_index(*rec.type);
tinfo->direct_conversions = &internals.direct_conversions[tindex]; tinfo->direct_conversions = &internals.direct_conversions[tindex];
if (rec.module_local) { if (rec.module_local) {
get_local_internals().registered_types_cpp[tindex] = tinfo; get_local_internals().registered_types_cpp[tindex] = tinfo;
} else { } else {
internals.registered_types_cpp[tindex] = tinfo; internals.registered_types_cpp[tindex] = tinfo;
} }
internals.registered_types_py[(PyTypeObject *) m_ptr] = {tinfo};
PYBIND11_WARNING_PUSH
#if defined(__GNUC__) && __GNUC__ == 12
// When using GCC 12 these warnings are disabled as they trigger
// false positive warnings. Discussed here:
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=115824.
PYBIND11_WARNING_DISABLE_GCC("-Warray-bounds")
PYBIND11_WARNING_DISABLE_GCC("-Wstringop-overread")
#endif
internals.registered_types_py[(PyTypeObject *) m_ptr] = {tinfo};
PYBIND11_WARNING_POP
});
if (rec.bases.size() > 1 || rec.multiple_inheritance) { if (rec.bases.size() > 1 || rec.multiple_inheritance) {
mark_parents_nonsimple(tinfo->type); mark_parents_nonsimple(tinfo->type);
@ -1618,11 +1606,14 @@ public:
generic_type::initialize(record); generic_type::initialize(record);
if (has_alias) { if (has_alias) {
auto &instances = record.module_local ? get_local_internals().registered_types_cpp with_internals([&](internals &internals) {
: get_internals().registered_types_cpp; auto &instances = record.module_local ? get_local_internals().registered_types_cpp
instances[std::type_index(typeid(type_alias))] : internals.registered_types_cpp;
= instances[std::type_index(typeid(type))]; instances[std::type_index(typeid(type_alias))]
= instances[std::type_index(typeid(type))];
});
} }
def("_pybind11_conduit_v1_", cpp_conduit_method);
} }
template <typename Base, detail::enable_if_t<is_base<Base>::value, int> = 0> template <typename Base, detail::enable_if_t<is_base<Base>::value, int> = 0>
@ -2336,28 +2327,32 @@ keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret) {
inline std::pair<decltype(internals::registered_types_py)::iterator, bool> inline std::pair<decltype(internals::registered_types_py)::iterator, bool>
all_type_info_get_cache(PyTypeObject *type) { all_type_info_get_cache(PyTypeObject *type) {
auto res = get_internals() auto res = with_internals([type](internals &internals) {
.registered_types_py return internals
.registered_types_py
#ifdef __cpp_lib_unordered_map_try_emplace #ifdef __cpp_lib_unordered_map_try_emplace
.try_emplace(type); .try_emplace(type);
#else #else
.emplace(type, std::vector<detail::type_info *>()); .emplace(type, std::vector<detail::type_info *>());
#endif #endif
});
if (res.second) { if (res.second) {
// New cache entry created; set up a weak reference to automatically remove it if the type // New cache entry created; set up a weak reference to automatically remove it if the type
// gets destroyed: // gets destroyed:
weakref((PyObject *) type, cpp_function([type](handle wr) { weakref((PyObject *) type, cpp_function([type](handle wr) {
get_internals().registered_types_py.erase(type); with_internals([type](internals &internals) {
internals.registered_types_py.erase(type);
// TODO consolidate the erasure code in pybind11_meta_dealloc() in class.h // TODO consolidate the erasure code in pybind11_meta_dealloc() in class.h
auto &cache = get_internals().inactive_override_cache; auto &cache = internals.inactive_override_cache;
for (auto it = cache.begin(), last = cache.end(); it != last;) { for (auto it = cache.begin(), last = cache.end(); it != last;) {
if (it->first == reinterpret_cast<PyObject *>(type)) { if (it->first == reinterpret_cast<PyObject *>(type)) {
it = cache.erase(it); it = cache.erase(it);
} else { } else {
++it; ++it;
}
} }
} });
wr.dec_ref(); wr.dec_ref();
})) }))
@ -2562,7 +2557,11 @@ void implicitly_convertible() {
~set_flag() { flag = false; } ~set_flag() { flag = false; }
}; };
auto implicit_caster = [](PyObject *obj, PyTypeObject *type) -> PyObject * { auto implicit_caster = [](PyObject *obj, PyTypeObject *type) -> PyObject * {
#ifdef Py_GIL_DISABLED
thread_local bool currently_used = false;
#else
static bool currently_used = false; static bool currently_used = false;
#endif
if (currently_used) { // implicit conversions are non-reentrant if (currently_used) { // implicit conversions are non-reentrant
return nullptr; return nullptr;
} }
@ -2587,8 +2586,12 @@ void implicitly_convertible() {
} }
inline void register_exception_translator(ExceptionTranslator &&translator) { inline void register_exception_translator(ExceptionTranslator &&translator) {
detail::get_internals().registered_exception_translators.push_front( detail::with_exception_translators(
std::forward<ExceptionTranslator>(translator)); [&](std::forward_list<ExceptionTranslator> &exception_translators,
std::forward_list<ExceptionTranslator> &local_exception_translators) {
(void) local_exception_translators;
exception_translators.push_front(std::forward<ExceptionTranslator>(translator));
});
} }
/** /**
@ -2598,8 +2601,12 @@ inline void register_exception_translator(ExceptionTranslator &&translator) {
* the exception. * the exception.
*/ */
inline void register_local_exception_translator(ExceptionTranslator &&translator) { inline void register_local_exception_translator(ExceptionTranslator &&translator) {
detail::get_local_internals().registered_exception_translators.push_front( detail::with_exception_translators(
std::forward<ExceptionTranslator>(translator)); [&](std::forward_list<ExceptionTranslator> &exception_translators,
std::forward_list<ExceptionTranslator> &local_exception_translators) {
(void) exception_translators;
local_exception_translators.push_front(std::forward<ExceptionTranslator>(translator));
});
} }
/** /**
@ -2756,14 +2763,19 @@ get_type_override(const void *this_ptr, const type_info *this_type, const char *
/* Cache functions that aren't overridden in Python to avoid /* Cache functions that aren't overridden in Python to avoid
many costly Python dictionary lookups below */ many costly Python dictionary lookups below */
auto &cache = get_internals().inactive_override_cache; bool not_overridden = with_internals([&key](internals &internals) {
if (cache.find(key) != cache.end()) { auto &cache = internals.inactive_override_cache;
return cache.find(key) != cache.end();
});
if (not_overridden) {
return function(); return function();
} }
function override = getattr(self, name, function()); function override = getattr(self, name, function());
if (override.is_cpp_function()) { if (override.is_cpp_function()) {
cache.insert(std::move(key)); with_internals([&](internals &internals) {
internals.inactive_override_cache.insert(std::move(key));
});
return function(); return function();
} }

View File

@ -980,6 +980,23 @@ inline PyObject *dict_getitem(PyObject *v, PyObject *key) {
return rv; return rv;
} }
inline PyObject *dict_getitemstringref(PyObject *v, const char *key) {
#if PY_VERSION_HEX >= 0x030D0000
PyObject *rv;
if (PyDict_GetItemStringRef(v, key, &rv) < 0) {
throw error_already_set();
}
return rv;
#else
PyObject *rv = dict_getitemstring(v, key);
if (rv == nullptr && PyErr_Occurred()) {
throw error_already_set();
}
Py_XINCREF(rv);
return rv;
#endif
}
// Helper aliases/functions to support implicit casting of values given to python // Helper aliases/functions to support implicit casting of values given to python
// accessors/methods. When given a pyobject, this simply returns the pyobject as-is; for other C++ // accessors/methods. When given a pyobject, this simply returns the pyobject as-is; for other C++
// type, the value goes through pybind11::cast(obj) to convert it to an `object`. // type, the value goes through pybind11::cast(obj) to convert it to an `object`.
@ -1242,6 +1259,7 @@ protected:
using pointer = arrow_proxy<const handle>; using pointer = arrow_proxy<const handle>;
sequence_fast_readonly(handle obj, ssize_t n) : ptr(PySequence_Fast_ITEMS(obj.ptr()) + n) {} sequence_fast_readonly(handle obj, ssize_t n) : ptr(PySequence_Fast_ITEMS(obj.ptr()) + n) {}
sequence_fast_readonly() = default;
// NOLINTNEXTLINE(readability-const-return-type) // PR #3263 // NOLINTNEXTLINE(readability-const-return-type) // PR #3263
reference dereference() const { return *ptr; } reference dereference() const { return *ptr; }
@ -1264,6 +1282,7 @@ protected:
using pointer = arrow_proxy<const sequence_accessor>; using pointer = arrow_proxy<const sequence_accessor>;
sequence_slow_readwrite(handle obj, ssize_t index) : obj(obj), index(index) {} sequence_slow_readwrite(handle obj, ssize_t index) : obj(obj), index(index) {}
sequence_slow_readwrite() = default;
reference dereference() const { return {obj, static_cast<size_t>(index)}; } reference dereference() const { return {obj, static_cast<size_t>(index)}; }
void increment() { ++index; } void increment() { ++index; }

View File

@ -11,10 +11,14 @@
#include "pybind11.h" #include "pybind11.h"
#include "detail/common.h" #include "detail/common.h"
#include "detail/descr.h"
#include "detail/type_caster_base.h"
#include <deque> #include <deque>
#include <initializer_list>
#include <list> #include <list>
#include <map> #include <map>
#include <memory>
#include <ostream> #include <ostream>
#include <set> #include <set>
#include <unordered_map> #include <unordered_map>
@ -35,6 +39,89 @@
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
//
// Begin: Equivalent of
// https://github.com/google/clif/blob/ae4eee1de07cdf115c0c9bf9fec9ff28efce6f6c/clif/python/runtime.cc#L388-L438
/*
The three `PyObjectTypeIsConvertibleTo*()` functions below are
the result of converging the behaviors of pybind11 and PyCLIF
(http://github.com/google/clif).
Originally PyCLIF was extremely far on the permissive side of the spectrum,
while pybind11 was very far on the strict side. Originally PyCLIF accepted any
Python iterable as input for a C++ `vector`/`set`/`map` argument, as long as
the elements were convertible. The obvious (in hindsight) problem was that
any empty Python iterable could be passed to any of these C++ types, e.g. `{}`
was accepted for C++ `vector`/`set` arguments, or `[]` for C++ `map` arguments.
The functions below strike a practical permissive-vs-strict compromise,
informed by tens of thousands of use cases in the wild. A main objective is
to prevent accidents and improve readability:
- Python literals must match the C++ types.
- For C++ `set`: The potentially reducing conversion from a Python sequence
(e.g. Python `list` or `tuple`) to a C++ `set` must be explicit, by going
through a Python `set`.
- However, a Python `set` can still be passed to a C++ `vector`. The rationale
is that this conversion is not reducing. Implicit conversions of this kind
are also fairly commonly used, therefore enforcing explicit conversions
would have an unfavorable cost : benefit ratio; more sloppily speaking,
such an enforcement would be more annoying than helpful.
*/
inline bool PyObjectIsInstanceWithOneOfTpNames(PyObject *obj,
std::initializer_list<const char *> tp_names) {
if (PyType_Check(obj)) {
return false;
}
const char *obj_tp_name = Py_TYPE(obj)->tp_name;
for (const auto *tp_name : tp_names) {
if (std::strcmp(obj_tp_name, tp_name) == 0) {
return true;
}
}
return false;
}
inline bool PyObjectTypeIsConvertibleToStdVector(PyObject *obj) {
if (PySequence_Check(obj) != 0) {
return !PyUnicode_Check(obj) && !PyBytes_Check(obj);
}
return (PyGen_Check(obj) != 0) || (PyAnySet_Check(obj) != 0)
|| PyObjectIsInstanceWithOneOfTpNames(
obj, {"dict_keys", "dict_values", "dict_items", "map", "zip"});
}
inline bool PyObjectTypeIsConvertibleToStdSet(PyObject *obj) {
return (PyAnySet_Check(obj) != 0) || PyObjectIsInstanceWithOneOfTpNames(obj, {"dict_keys"});
}
inline bool PyObjectTypeIsConvertibleToStdMap(PyObject *obj) {
if (PyDict_Check(obj)) {
return true;
}
// Implicit requirement in the conditions below:
// A type with `.__getitem__()` & `.items()` methods must implement these
// to be compatible with https://docs.python.org/3/c-api/mapping.html
if (PyMapping_Check(obj) == 0) {
return false;
}
PyObject *items = PyObject_GetAttrString(obj, "items");
if (items == nullptr) {
PyErr_Clear();
return false;
}
bool is_convertible = (PyCallable_Check(items) != 0);
Py_DECREF(items);
return is_convertible;
}
//
// End: Equivalent of clif/python/runtime.cc
//
/// Extracts an const lvalue reference or rvalue reference for U based on the type of T (e.g. for /// Extracts an const lvalue reference or rvalue reference for U based on the type of T (e.g. for
/// forwarding a container element). Typically used indirect via forwarded_type(), below. /// forwarding a container element). Typically used indirect via forwarded_type(), below.
template <typename T, typename U> template <typename T, typename U>
@ -66,17 +153,10 @@ private:
} }
void reserve_maybe(const anyset &, void *) {} void reserve_maybe(const anyset &, void *) {}
public: bool convert_iterable(const iterable &itbl, bool convert) {
bool load(handle src, bool convert) { for (const auto &it : itbl) {
if (!isinstance<anyset>(src)) {
return false;
}
auto s = reinterpret_borrow<anyset>(src);
value.clear();
reserve_maybe(s, &value);
for (auto entry : s) {
key_conv conv; key_conv conv;
if (!conv.load(entry, convert)) { if (!conv.load(it, convert)) {
return false; return false;
} }
value.insert(cast_op<Key &&>(std::move(conv))); value.insert(cast_op<Key &&>(std::move(conv)));
@ -84,6 +164,29 @@ public:
return true; return true;
} }
bool convert_anyset(anyset s, bool convert) {
value.clear();
reserve_maybe(s, &value);
return convert_iterable(s, convert);
}
public:
bool load(handle src, bool convert) {
if (!PyObjectTypeIsConvertibleToStdSet(src.ptr())) {
return false;
}
if (isinstance<anyset>(src)) {
value.clear();
return convert_anyset(reinterpret_borrow<anyset>(src), convert);
}
if (!convert) {
return false;
}
assert(isinstance<iterable>(src));
value.clear();
return convert_iterable(reinterpret_borrow<iterable>(src), convert);
}
template <typename T> template <typename T>
static handle cast(T &&src, return_value_policy policy, handle parent) { static handle cast(T &&src, return_value_policy policy, handle parent) {
if (!std::is_lvalue_reference<T>::value) { if (!std::is_lvalue_reference<T>::value) {
@ -115,15 +218,10 @@ private:
} }
void reserve_maybe(const dict &, void *) {} void reserve_maybe(const dict &, void *) {}
public: bool convert_elements(const dict &d, bool convert) {
bool load(handle src, bool convert) {
if (!isinstance<dict>(src)) {
return false;
}
auto d = reinterpret_borrow<dict>(src);
value.clear(); value.clear();
reserve_maybe(d, &value); reserve_maybe(d, &value);
for (auto it : d) { for (const auto &it : d) {
key_conv kconv; key_conv kconv;
value_conv vconv; value_conv vconv;
if (!kconv.load(it.first.ptr(), convert) || !vconv.load(it.second.ptr(), convert)) { if (!kconv.load(it.first.ptr(), convert) || !vconv.load(it.second.ptr(), convert)) {
@ -134,6 +232,25 @@ public:
return true; return true;
} }
public:
bool load(handle src, bool convert) {
if (!PyObjectTypeIsConvertibleToStdMap(src.ptr())) {
return false;
}
if (isinstance<dict>(src)) {
return convert_elements(reinterpret_borrow<dict>(src), convert);
}
if (!convert) {
return false;
}
auto items = reinterpret_steal<object>(PyMapping_Items(src.ptr()));
if (!items) {
throw error_already_set();
}
assert(isinstance<iterable>(items));
return convert_elements(dict(reinterpret_borrow<iterable>(items)), convert);
}
template <typename T> template <typename T>
static handle cast(T &&src, return_value_policy policy, handle parent) { static handle cast(T &&src, return_value_policy policy, handle parent) {
dict d; dict d;
@ -166,20 +283,21 @@ struct list_caster {
using value_conv = make_caster<Value>; using value_conv = make_caster<Value>;
bool load(handle src, bool convert) { bool load(handle src, bool convert) {
if (!isinstance<sequence>(src) || isinstance<bytes>(src) || isinstance<str>(src)) { if (!PyObjectTypeIsConvertibleToStdVector(src.ptr())) {
return false; return false;
} }
auto s = reinterpret_borrow<sequence>(src); if (isinstance<sequence>(src)) {
value.clear(); return convert_elements(src, convert);
reserve_maybe(s, &value);
for (const auto &it : s) {
value_conv conv;
if (!conv.load(it, convert)) {
return false;
}
value.push_back(cast_op<Value &&>(std::move(conv)));
} }
return true; if (!convert) {
return false;
}
// Designed to be behavior-equivalent to passing tuple(src) from Python:
// The conversion to a tuple will first exhaust the generator object, to ensure that
// the generator is not left in an unpredictable (to the caller) partially-consumed
// state.
assert(isinstance<iterable>(src));
return convert_elements(tuple(reinterpret_borrow<iterable>(src)), convert);
} }
private: private:
@ -189,6 +307,20 @@ private:
} }
void reserve_maybe(const sequence &, void *) {} void reserve_maybe(const sequence &, void *) {}
bool convert_elements(handle seq, bool convert) {
auto s = reinterpret_borrow<sequence>(seq);
value.clear();
reserve_maybe(s, &value);
for (const auto &it : seq) {
value_conv conv;
if (!conv.load(it, convert)) {
return false;
}
value.push_back(cast_op<Value &&>(std::move(conv)));
}
return true;
}
public: public:
template <typename T> template <typename T>
static handle cast(T &&src, return_value_policy policy, handle parent) { static handle cast(T &&src, return_value_policy policy, handle parent) {
@ -220,43 +352,87 @@ struct type_caster<std::deque<Type, Alloc>> : list_caster<std::deque<Type, Alloc
template <typename Type, typename Alloc> template <typename Type, typename Alloc>
struct type_caster<std::list<Type, Alloc>> : list_caster<std::list<Type, Alloc>, Type> {}; struct type_caster<std::list<Type, Alloc>> : list_caster<std::list<Type, Alloc>, Type> {};
template <typename ArrayType, typename V, size_t... I>
ArrayType vector_to_array_impl(V &&v, index_sequence<I...>) {
return {{std::move(v[I])...}};
}
// Based on https://en.cppreference.com/w/cpp/container/array/to_array
template <typename ArrayType, size_t N, typename V>
ArrayType vector_to_array(V &&v) {
return vector_to_array_impl<ArrayType, V>(std::forward<V>(v), make_index_sequence<N>{});
}
template <typename ArrayType, typename Value, bool Resizable, size_t Size = 0> template <typename ArrayType, typename Value, bool Resizable, size_t Size = 0>
struct array_caster { struct array_caster {
using value_conv = make_caster<Value>; using value_conv = make_caster<Value>;
private: private:
template <bool R = Resizable> std::unique_ptr<ArrayType> value;
bool require_size(enable_if_t<R, size_t> size) {
if (value.size() != size) {
value.resize(size);
}
return true;
}
template <bool R = Resizable>
bool require_size(enable_if_t<!R, size_t> size) {
return size == Size;
}
public: template <bool R = Resizable, enable_if_t<R, int> = 0>
bool load(handle src, bool convert) { bool convert_elements(handle seq, bool convert) {
if (!isinstance<sequence>(src)) { auto l = reinterpret_borrow<sequence>(seq);
return false; value.reset(new ArrayType{});
} // Using `resize` to preserve the behavior exactly as it was before PR #5305
auto l = reinterpret_borrow<sequence>(src); // For the `resize` to work, `Value` must be default constructible.
if (!require_size(l.size())) { // For `std::valarray`, this is a requirement:
return false; // https://en.cppreference.com/w/cpp/named_req/NumericType
} value->resize(l.size());
size_t ctr = 0; size_t ctr = 0;
for (const auto &it : l) { for (const auto &it : l) {
value_conv conv; value_conv conv;
if (!conv.load(it, convert)) { if (!conv.load(it, convert)) {
return false; return false;
} }
value[ctr++] = cast_op<Value &&>(std::move(conv)); (*value)[ctr++] = cast_op<Value &&>(std::move(conv));
} }
return true; return true;
} }
template <bool R = Resizable, enable_if_t<!R, int> = 0>
bool convert_elements(handle seq, bool convert) {
auto l = reinterpret_borrow<sequence>(seq);
if (l.size() != Size) {
return false;
}
// The `temp` storage is needed to support `Value` types that are not
// default-constructible.
// Deliberate choice: no template specializations, for simplicity, and
// because the compile time overhead for the specializations is deemed
// more significant than the runtime overhead for the `temp` storage.
std::vector<Value> temp;
temp.reserve(l.size());
for (auto it : l) {
value_conv conv;
if (!conv.load(it, convert)) {
return false;
}
temp.emplace_back(cast_op<Value &&>(std::move(conv)));
}
value.reset(new ArrayType(vector_to_array<ArrayType, Size>(std::move(temp))));
return true;
}
public:
bool load(handle src, bool convert) {
if (!PyObjectTypeIsConvertibleToStdVector(src.ptr())) {
return false;
}
if (isinstance<sequence>(src)) {
return convert_elements(src, convert);
}
if (!convert) {
return false;
}
// Designed to be behavior-equivalent to passing tuple(src) from Python:
// The conversion to a tuple will first exhaust the generator object, to ensure that
// the generator is not left in an unpredictable (to the caller) partially-consumed
// state.
assert(isinstance<iterable>(src));
return convert_elements(tuple(reinterpret_borrow<iterable>(src)), convert);
}
template <typename T> template <typename T>
static handle cast(T &&src, return_value_policy policy, handle parent) { static handle cast(T &&src, return_value_policy policy, handle parent) {
list l(src.size()); list l(src.size());
@ -272,12 +448,36 @@ public:
return l.release(); return l.release();
} }
PYBIND11_TYPE_CASTER(ArrayType, // Code copied from PYBIND11_TYPE_CASTER macro.
const_name<Resizable>(const_name(""), const_name("Annotated[")) // Intentionally preserving the behavior exactly as it was before PR #5305
+ const_name("list[") + value_conv::name + const_name("]") template <typename T_, enable_if_t<std::is_same<ArrayType, remove_cv_t<T_>>::value, int> = 0>
+ const_name<Resizable>(const_name(""), static handle cast(T_ *src, return_value_policy policy, handle parent) {
const_name(", FixedSize(") if (!src) {
+ const_name<Size>() + const_name(")]"))); return none().release();
}
if (policy == return_value_policy::take_ownership) {
auto h = cast(std::move(*src), policy, parent);
delete src; // WARNING: Assumes `src` was allocated with `new`.
return h;
}
return cast(*src, policy, parent);
}
// NOLINTNEXTLINE(google-explicit-constructor)
operator ArrayType *() { return &(*value); }
// NOLINTNEXTLINE(google-explicit-constructor)
operator ArrayType &() { return *value; }
// NOLINTNEXTLINE(google-explicit-constructor)
operator ArrayType &&() && { return std::move(*value); }
template <typename T_>
using cast_op_type = movable_cast_op_type<T_>;
static constexpr auto name
= const_name<Resizable>(const_name(""), const_name("Annotated[")) + const_name("list[")
+ value_conv::name + const_name("]")
+ const_name<Resizable>(
const_name(""), const_name(", FixedSize(") + const_name<Size>() + const_name(")]"));
}; };
template <typename Type, size_t Size> template <typename Type, size_t Size>

View File

@ -4,18 +4,17 @@
#pragma once #pragma once
#include "../pybind11.h" #include <pybind11/cast.h>
#include "../detail/common.h" #include <pybind11/detail/common.h>
#include "../detail/descr.h" #include <pybind11/detail/descr.h>
#include "../cast.h" #include <pybind11/pybind11.h>
#include "../pytypes.h" #include <pybind11/pytypes.h>
#include <string> #include <string>
#ifdef __has_include #ifdef __has_include
# if defined(PYBIND11_CPP17) # if defined(PYBIND11_CPP17)
# if __has_include(<filesystem>) && \ # if __has_include(<filesystem>)
PY_VERSION_HEX >= 0x03060000
# include <filesystem> # include <filesystem>
# define PYBIND11_HAS_FILESYSTEM 1 # define PYBIND11_HAS_FILESYSTEM 1
# elif __has_include(<experimental/filesystem>) # elif __has_include(<experimental/filesystem>)
@ -34,6 +33,13 @@
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
#ifdef PYPY_VERSION
# define PYBIND11_REINTERPRET_CAST_VOID_PTR_IF_NOT_PYPY(...) (__VA_ARGS__)
#else
# define PYBIND11_REINTERPRET_CAST_VOID_PTR_IF_NOT_PYPY(...) \
(reinterpret_cast<void *>(__VA_ARGS__))
#endif
#if defined(PYBIND11_HAS_FILESYSTEM) || defined(PYBIND11_HAS_EXPERIMENTAL_FILESYSTEM) #if defined(PYBIND11_HAS_FILESYSTEM) || defined(PYBIND11_HAS_EXPERIMENTAL_FILESYSTEM)
template <typename T> template <typename T>
struct path_caster { struct path_caster {
@ -73,7 +79,8 @@ public:
} }
PyObject *native = nullptr; PyObject *native = nullptr;
if constexpr (std::is_same_v<typename T::value_type, char>) { if constexpr (std::is_same_v<typename T::value_type, char>) {
if (PyUnicode_FSConverter(buf, &native) != 0) { if (PyUnicode_FSConverter(buf, PYBIND11_REINTERPRET_CAST_VOID_PTR_IF_NOT_PYPY(&native))
!= 0) {
if (auto *c_str = PyBytes_AsString(native)) { if (auto *c_str = PyBytes_AsString(native)) {
// AsString returns a pointer to the internal buffer, which // AsString returns a pointer to the internal buffer, which
// must not be free'd. // must not be free'd.
@ -81,7 +88,8 @@ public:
} }
} }
} else if constexpr (std::is_same_v<typename T::value_type, wchar_t>) { } else if constexpr (std::is_same_v<typename T::value_type, wchar_t>) {
if (PyUnicode_FSDecoder(buf, &native) != 0) { if (PyUnicode_FSDecoder(buf, PYBIND11_REINTERPRET_CAST_VOID_PTR_IF_NOT_PYPY(&native))
!= 0) {
if (auto *c_str = PyUnicode_AsWideCharString(native, nullptr)) { if (auto *c_str = PyUnicode_AsWideCharString(native, nullptr)) {
// AsWideCharString returns a new string that must be free'd. // AsWideCharString returns a new string that must be free'd.
value = c_str; // Copies the string. value = c_str; // Copies the string.

View File

@ -180,7 +180,7 @@ void vector_modifiers(
v.end()); v.end());
try { try {
v.shrink_to_fit(); v.shrink_to_fit();
} catch (const std::exception &) { } catch (const std::exception &) { // NOLINT(bugprone-empty-catch)
// Do nothing // Do nothing
} }
throw; throw;

View File

@ -14,6 +14,8 @@
#include "cast.h" #include "cast.h"
#include "pytypes.h" #include "pytypes.h"
#include <algorithm>
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(typing) PYBIND11_NAMESPACE_BEGIN(typing)
@ -63,6 +65,63 @@ class Callable<Return(Args...)> : public function {
using function::function; using function::function;
}; };
template <typename T>
class Type : public type {
using type::type;
};
template <typename... Types>
class Union : public object {
PYBIND11_OBJECT_DEFAULT(Union, object, PyObject_Type)
using object::object;
};
template <typename T>
class Optional : public object {
PYBIND11_OBJECT_DEFAULT(Optional, object, PyObject_Type)
using object::object;
};
template <typename T>
class TypeGuard : public bool_ {
using bool_::bool_;
};
template <typename T>
class TypeIs : public bool_ {
using bool_::bool_;
};
class NoReturn : public none {
using none::none;
};
class Never : public none {
using none::none;
};
#if defined(__cpp_nontype_template_args) && __cpp_nontype_template_args >= 201911L
# define PYBIND11_TYPING_H_HAS_STRING_LITERAL
template <size_t N>
struct StringLiteral {
constexpr StringLiteral(const char (&str)[N]) { std::copy_n(str, N, name); }
char name[N];
};
template <StringLiteral... StrLits>
class Literal : public object {
PYBIND11_OBJECT_DEFAULT(Literal, object, PyObject_Type)
};
// Example syntax for creating a TypeVar.
// typedef typing::TypeVar<"T"> TypeVarT;
template <StringLiteral>
class TypeVar : public object {
PYBIND11_OBJECT_DEFAULT(TypeVar, object, PyObject_Type)
using object::object;
};
#endif
PYBIND11_NAMESPACE_END(typing) PYBIND11_NAMESPACE_END(typing)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
@ -121,5 +180,63 @@ struct handle_type_name<typing::Callable<Return(Args...)>> {
+ const_name("], ") + make_caster<retval_type>::name + const_name("]"); + const_name("], ") + make_caster<retval_type>::name + const_name("]");
}; };
template <typename Return>
struct handle_type_name<typing::Callable<Return(ellipsis)>> {
// PEP 484 specifies this syntax for defining only return types of callables
using retval_type = conditional_t<std::is_same<Return, void>::value, void_type, Return>;
static constexpr auto name
= const_name("Callable[..., ") + make_caster<retval_type>::name + const_name("]");
};
template <typename T>
struct handle_type_name<typing::Type<T>> {
static constexpr auto name = const_name("type[") + make_caster<T>::name + const_name("]");
};
template <typename... Types>
struct handle_type_name<typing::Union<Types...>> {
static constexpr auto name = const_name("Union[")
+ ::pybind11::detail::concat(make_caster<Types>::name...)
+ const_name("]");
};
template <typename T>
struct handle_type_name<typing::Optional<T>> {
static constexpr auto name = const_name("Optional[") + make_caster<T>::name + const_name("]");
};
template <typename T>
struct handle_type_name<typing::TypeGuard<T>> {
static constexpr auto name = const_name("TypeGuard[") + make_caster<T>::name + const_name("]");
};
template <typename T>
struct handle_type_name<typing::TypeIs<T>> {
static constexpr auto name = const_name("TypeIs[") + make_caster<T>::name + const_name("]");
};
template <>
struct handle_type_name<typing::NoReturn> {
static constexpr auto name = const_name("NoReturn");
};
template <>
struct handle_type_name<typing::Never> {
static constexpr auto name = const_name("Never");
};
#if defined(PYBIND11_TYPING_H_HAS_STRING_LITERAL)
template <typing::StringLiteral... Literals>
struct handle_type_name<typing::Literal<Literals...>> {
static constexpr auto name = const_name("Literal[")
+ pybind11::detail::concat(const_name(Literals.name)...)
+ const_name("]");
};
template <typing::StringLiteral StrLit>
struct handle_type_name<typing::TypeVar<StrLit>> {
static constexpr auto name = const_name(StrLit.name);
};
#endif
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)

View File

@ -0,0 +1,75 @@
/*
pybind11/warnings.h: Python warnings wrappers.
Copyright (c) 2024 Jan Iwaszkiewicz <jiwaszkiewicz6@gmail.com>
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#pragma once
#include "pybind11.h"
#include "detail/common.h"
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail)
inline bool PyWarning_Check(PyObject *obj) {
int result = PyObject_IsSubclass(obj, PyExc_Warning);
if (result == 1) {
return true;
}
if (result == -1) {
raise_from(PyExc_SystemError,
"pybind11::detail::PyWarning_Check(): PyObject_IsSubclass() call failed.");
throw error_already_set();
}
return false;
}
PYBIND11_NAMESPACE_END(detail)
PYBIND11_NAMESPACE_BEGIN(warnings)
inline object
new_warning_type(handle scope, const char *name, handle base = PyExc_RuntimeWarning) {
if (!detail::PyWarning_Check(base.ptr())) {
pybind11_fail("pybind11::warnings::new_warning_type(): cannot create custom warning, base "
"must be a subclass of "
"PyExc_Warning!");
}
if (hasattr(scope, name)) {
pybind11_fail("pybind11::warnings::new_warning_type(): an attribute with name \""
+ std::string(name) + "\" exists already.");
}
std::string full_name = scope.attr("__name__").cast<std::string>() + std::string(".") + name;
handle h(PyErr_NewException(full_name.c_str(), base.ptr(), nullptr));
if (!h) {
raise_from(PyExc_SystemError,
"pybind11::warnings::new_warning_type(): PyErr_NewException() call failed.");
throw error_already_set();
}
auto obj = reinterpret_steal<object>(h);
scope.attr(name) = obj;
return obj;
}
// Similar to Python `warnings.warn()`
inline void
warn(const char *message, handle category = PyExc_RuntimeWarning, int stack_level = 2) {
if (!detail::PyWarning_Check(category.ptr())) {
pybind11_fail(
"pybind11::warnings::warn(): cannot raise warning, category must be a subclass of "
"PyExc_Warning!");
}
if (PyErr_WarnEx(category.ptr(), message, stack_level) == -1) {
throw error_already_set();
}
}
PYBIND11_NAMESPACE_END(warnings)
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)

View File

@ -1,3 +1,5 @@
from __future__ import annotations
import argparse import argparse
import nox import nox
@ -43,7 +45,7 @@ def tests_packaging(session: nox.Session) -> None:
Run the packaging tests. Run the packaging tests.
""" """
session.install("-r", "tests/requirements.txt") session.install("-r", "tests/requirements.txt", "pip")
session.run("pytest", "tests/extra_python_package", *session.posargs) session.run("pytest", "tests/extra_python_package", *session.posargs)

View File

@ -1,7 +1,9 @@
from __future__ import annotations
import sys import sys
if sys.version_info < (3, 6): # noqa: UP036 if sys.version_info < (3, 8): # noqa: UP036
msg = "pybind11 does not support Python < 3.6. 2.9 was the last release supporting Python 2.7 and 3.5." msg = "pybind11 does not support Python < 3.8. v2.13 was the last release supporting Python 3.7."
raise ImportError(msg) raise ImportError(msg)

View File

@ -1,12 +1,36 @@
# pylint: disable=missing-function-docstring # pylint: disable=missing-function-docstring
from __future__ import annotations
import argparse import argparse
import re
import sys import sys
import sysconfig import sysconfig
from ._version import __version__ from ._version import __version__
from .commands import get_cmake_dir, get_include, get_pkgconfig_dir from .commands import get_cmake_dir, get_include, get_pkgconfig_dir
# This is the conditional used for os.path being posixpath
if "posix" in sys.builtin_module_names:
from shlex import quote
elif "nt" in sys.builtin_module_names:
# See https://github.com/mesonbuild/meson/blob/db22551ed9d2dd7889abea01cc1c7bba02bf1c75/mesonbuild/utils/universal.py#L1092-L1121
# and the original documents:
# https://docs.microsoft.com/en-us/cpp/c-language/parsing-c-command-line-arguments and
# https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/
UNSAFE = re.compile("[ \t\n\r]")
def quote(s: str) -> str:
if s and not UNSAFE.search(s):
return s
# Paths cannot contain a '"' on Windows, so we don't need to worry
# about nuanced counting here.
return f'"{s}\\"' if s.endswith("\\") else f'"{s}"'
else:
def quote(s: str) -> str:
return s
def print_includes() -> None: def print_includes() -> None:
dirs = [ dirs = [
@ -21,7 +45,7 @@ def print_includes() -> None:
if d and d not in unique_dirs: if d and d not in unique_dirs:
unique_dirs.append(d) unique_dirs.append(d)
print(" ".join("-I" + d for d in unique_dirs)) print(" ".join(quote(f"-I{d}") for d in unique_dirs))
def main() -> None: def main() -> None:
@ -53,9 +77,9 @@ def main() -> None:
if args.includes: if args.includes:
print_includes() print_includes()
if args.cmakedir: if args.cmakedir:
print(get_cmake_dir()) print(quote(get_cmake_dir()))
if args.pkgconfigdir: if args.pkgconfigdir:
print(get_pkgconfig_dir()) print(quote(get_pkgconfig_dir()))
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -1,12 +1,12 @@
from typing import Union from __future__ import annotations
def _to_int(s: str) -> Union[int, str]: def _to_int(s: str) -> int | str:
try: try:
return int(s) return int(s)
except ValueError: except ValueError:
return s return s
__version__ = "2.13.0.dev1" __version__ = "2.14.0.dev1"
version_info = tuple(_to_int(s) for s in __version__.split(".")) version_info = tuple(_to_int(s) for s in __version__.split("."))

View File

@ -1,3 +1,5 @@
from __future__ import annotations
import os import os
DIR = os.path.abspath(os.path.dirname(__file__)) DIR = os.path.abspath(os.path.dirname(__file__))

View File

@ -36,6 +36,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# #
# If you copy this file in, you don't # If you copy this file in, you don't
# need the .pyi file; it's just an interface file for static type checkers. # need the .pyi file; it's just an interface file for static type checkers.
from __future__ import annotations
import contextlib import contextlib
import os import os
@ -52,7 +53,6 @@ from pathlib import Path
from typing import ( from typing import (
Any, Any,
Callable, Callable,
Dict,
Iterable, Iterable,
Iterator, Iterator,
List, List,
@ -113,10 +113,10 @@ class Pybind11Extension(_Extension):
# flags are prepended, so that they can be further overridden, e.g. by # flags are prepended, so that they can be further overridden, e.g. by
# ``extra_compile_args=["-g"]``. # ``extra_compile_args=["-g"]``.
def _add_cflags(self, flags: List[str]) -> None: def _add_cflags(self, flags: list[str]) -> None:
self.extra_compile_args[:0] = flags self.extra_compile_args[:0] = flags
def _add_ldflags(self, flags: List[str]) -> None: def _add_ldflags(self, flags: list[str]) -> None:
self.extra_link_args[:0] = flags self.extra_link_args[:0] = flags
def __init__(self, *args: Any, **kwargs: Any) -> None: def __init__(self, *args: Any, **kwargs: Any) -> None:
@ -249,8 +249,8 @@ def has_flag(compiler: Any, flag: str) -> bool:
cpp_flag_cache = None cpp_flag_cache = None
@lru_cache() @lru_cache
def auto_cpp_level(compiler: Any) -> Union[str, int]: def auto_cpp_level(compiler: Any) -> str | int:
""" """
Return the max supported C++ std level (17, 14, or 11). Returns latest on Windows. Return the max supported C++ std level (17, 14, or 11). Returns latest on Windows.
""" """
@ -288,8 +288,8 @@ class build_ext(_build_ext): # noqa: N801
def intree_extensions( def intree_extensions(
paths: Iterable[str], package_dir: Optional[Dict[str, str]] = None paths: Iterable[str], package_dir: dict[str, str] | None = None
) -> List[Pybind11Extension]: ) -> list[Pybind11Extension]:
""" """
Generate Pybind11Extensions from source files directly located in a Python Generate Pybind11Extensions from source files directly located in a Python
source tree. source tree.
@ -409,7 +409,7 @@ class ParallelCompile:
def __init__( def __init__(
self, self,
envvar: Optional[str] = None, envvar: str | None = None,
default: int = 0, default: int = 0,
max: int = 0, # pylint: disable=redefined-builtin max: int = 0, # pylint: disable=redefined-builtin
needs_recompile: Callable[[str, str], bool] = no_recompile, needs_recompile: Callable[[str, str], bool] = no_recompile,
@ -418,7 +418,7 @@ class ParallelCompile:
self.default = default self.default = default
self.max = max self.max = max
self.needs_recompile = needs_recompile self.needs_recompile = needs_recompile
self._old: List[CCompilerMethod] = [] self._old: list[CCompilerMethod] = []
def function(self) -> CCompilerMethod: def function(self) -> CCompilerMethod:
""" """
@ -427,14 +427,14 @@ class ParallelCompile:
def compile_function( def compile_function(
compiler: distutils.ccompiler.CCompiler, compiler: distutils.ccompiler.CCompiler,
sources: List[str], sources: list[str],
output_dir: Optional[str] = None, output_dir: str | None = None,
macros: Optional[List[Union[Tuple[str], Tuple[str, Optional[str]]]]] = None, macros: list[tuple[str] | tuple[str, str | None]] | None = None,
include_dirs: Optional[List[str]] = None, include_dirs: list[str] | None = None,
debug: bool = False, debug: bool = False,
extra_preargs: Optional[List[str]] = None, extra_preargs: list[str] | None = None,
extra_postargs: Optional[List[str]] = None, extra_postargs: list[str] | None = None,
depends: Optional[List[str]] = None, depends: list[str] | None = None,
) -> Any: ) -> Any:
# These lines are directly from distutils.ccompiler.CCompiler # These lines are directly from distutils.ccompiler.CCompiler
macros, objects, extra_postargs, pp_opts, build = compiler._setup_compile( # type: ignore[attr-defined] macros, objects, extra_postargs, pp_opts, build = compiler._setup_compile( # type: ignore[attr-defined]

View File

@ -30,7 +30,7 @@ ignore_missing_imports = true
[tool.pylint] [tool.pylint]
master.py-version = "3.6" master.py-version = "3.8"
reports.output-format = "colorized" reports.output-format = "colorized"
messages_control.disable = [ messages_control.disable = [
"design", "design",
@ -45,7 +45,7 @@ messages_control.disable = [
] ]
[tool.ruff] [tool.ruff]
target-version = "py37" target-version = "py38"
src = ["src"] src = ["src"]
[tool.ruff.lint] [tool.ruff.lint]
@ -76,6 +76,8 @@ ignore = [
] ]
unfixable = ["T20"] unfixable = ["T20"]
isort.known-first-party = ["env", "pybind11_cross_module_tests", "pybind11_tests"] isort.known-first-party = ["env", "pybind11_cross_module_tests", "pybind11_tests"]
isort.required-imports = ["from __future__ import annotations"]
[tool.ruff.lint.per-file-ignores] [tool.ruff.lint.per-file-ignores]
"tests/**" = ["EM", "N", "E721"] "tests/**" = ["EM", "N", "E721"]

View File

@ -14,13 +14,12 @@ classifiers =
Topic :: Utilities Topic :: Utilities
Programming Language :: C++ Programming Language :: C++
Programming Language :: Python :: 3 :: Only Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.10
Programming Language :: Python :: 3.11 Programming Language :: Python :: 3.11
Programming Language :: Python :: 3.12 Programming Language :: Python :: 3.12
Programming Language :: Python :: 3.13
License :: OSI Approved :: BSD License License :: OSI Approved :: BSD License
Programming Language :: Python :: Implementation :: PyPy Programming Language :: Python :: Implementation :: PyPy
Programming Language :: Python :: Implementation :: CPython Programming Language :: Python :: Implementation :: CPython
@ -39,5 +38,5 @@ project_urls =
Chat = https://gitter.im/pybind/Lobby Chat = https://gitter.im/pybind/Lobby
[options] [options]
python_requires = >=3.6 python_requires = >=3.8
zip_safe = False zip_safe = False

View File

@ -1,6 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# Setup script for PyPI; use CMakeFile.txt to build extension modules # Setup script for PyPI; use CMakeFile.txt to build extension modules
from __future__ import annotations
import contextlib import contextlib
import os import os
@ -9,9 +10,9 @@ import shutil
import string import string
import subprocess import subprocess
import sys import sys
from collections.abc import Generator
from pathlib import Path from pathlib import Path
from tempfile import TemporaryDirectory from tempfile import TemporaryDirectory
from typing import Dict, Iterator, List, Union
import setuptools.command.sdist import setuptools.command.sdist
@ -23,7 +24,7 @@ VERSION_FILE = Path("pybind11/_version.py")
COMMON_FILE = Path("include/pybind11/detail/common.h") COMMON_FILE = Path("include/pybind11/detail/common.h")
def build_expected_version_hex(matches: Dict[str, str]) -> str: def build_expected_version_hex(matches: dict[str, str]) -> str:
patch_level_serial = matches["PATCH"] patch_level_serial = matches["PATCH"]
serial = None serial = None
major = int(matches["MAJOR"]) major = int(matches["MAJOR"])
@ -64,7 +65,7 @@ to_src = (
# Read the listed version # Read the listed version
loc: Dict[str, str] = {} loc: dict[str, str] = {}
code = compile(VERSION_FILE.read_text(encoding="utf-8"), "pybind11/_version.py", "exec") code = compile(VERSION_FILE.read_text(encoding="utf-8"), "pybind11/_version.py", "exec")
exec(code, loc) exec(code, loc)
version = loc["__version__"] version = loc["__version__"]
@ -84,9 +85,7 @@ if version_hex != exp_version_hex:
# TODO: use literals & overload (typing extensions or Python 3.8) # TODO: use literals & overload (typing extensions or Python 3.8)
def get_and_replace( def get_and_replace(filename: Path, binary: bool = False, **opts: str) -> bytes | str:
filename: Path, binary: bool = False, **opts: str
) -> Union[bytes, str]:
if binary: if binary:
contents = filename.read_bytes() contents = filename.read_bytes()
return string.Template(contents.decode()).substitute(opts).encode() return string.Template(contents.decode()).substitute(opts).encode()
@ -97,7 +96,7 @@ def get_and_replace(
# Use our input files instead when making the SDist (and anything that depends # Use our input files instead when making the SDist (and anything that depends
# on it, like a wheel) # on it, like a wheel)
class SDist(setuptools.command.sdist.sdist): class SDist(setuptools.command.sdist.sdist):
def make_release_tree(self, base_dir: str, files: List[str]) -> None: def make_release_tree(self, base_dir: str, files: list[str]) -> None:
super().make_release_tree(base_dir, files) super().make_release_tree(base_dir, files)
for to, src in to_src: for to, src in to_src:
@ -112,7 +111,7 @@ class SDist(setuptools.command.sdist.sdist):
# Remove the CMake install directory when done # Remove the CMake install directory when done
@contextlib.contextmanager @contextlib.contextmanager
def remove_output(*sources: str) -> Iterator[None]: def remove_output(*sources: str) -> Generator[None, None, None]:
try: try:
yield yield
finally: finally:

View File

@ -5,16 +5,7 @@
# All rights reserved. Use of this source code is governed by a # All rights reserved. Use of this source code is governed by a
# BSD-style license that can be found in the LICENSE file. # BSD-style license that can be found in the LICENSE file.
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.15...3.30)
# The `cmake_minimum_required(VERSION 3.5...3.29)` syntax does not work with
# some versions of VS that have a patched CMake 3.11. This forces us to emulate
# the behavior using the following workaround:
if(${CMAKE_VERSION} VERSION_LESS 3.29)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else()
cmake_policy(VERSION 3.29)
endif()
# Filter out items; print an optional message if any items filtered. This ignores extensions. # Filter out items; print an optional message if any items filtered. This ignores extensions.
# #
@ -76,8 +67,8 @@ project(pybind11_tests CXX)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../tools") list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../tools")
option(PYBIND11_WERROR "Report all warnings as errors" OFF) option(PYBIND11_WERROR "Report all warnings as errors" OFF)
option(DOWNLOAD_EIGEN "Download EIGEN (requires CMake 3.11+)" OFF) option(DOWNLOAD_EIGEN "Download EIGEN" OFF)
option(PYBIND11_CUDA_TESTS "Enable building CUDA tests (requires CMake 3.12+)" OFF) option(PYBIND11_CUDA_TESTS "Enable building CUDA tests" OFF)
set(PYBIND11_TEST_OVERRIDE set(PYBIND11_TEST_OVERRIDE
"" ""
CACHE STRING "Tests from ;-separated list of *.cpp files will be built instead of all tests") CACHE STRING "Tests from ;-separated list of *.cpp files will be built instead of all tests")
@ -88,7 +79,12 @@ set(PYBIND11_TEST_FILTER
if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
# We're being loaded directly, i.e. not via add_subdirectory, so make this # We're being loaded directly, i.e. not via add_subdirectory, so make this
# work as its own project and load the pybind11Config to get the tools we need # work as its own project and load the pybind11Config to get the tools we need
find_package(pybind11 REQUIRED CONFIG)
if(SKBUILD)
add_subdirectory(.. pybind11_src)
else()
find_package(pybind11 REQUIRED CONFIG)
endif()
endif() endif()
if(NOT CMAKE_BUILD_TYPE AND NOT DEFINED CMAKE_CONFIGURATION_TYPES) if(NOT CMAKE_BUILD_TYPE AND NOT DEFINED CMAKE_CONFIGURATION_TYPES)
@ -122,6 +118,7 @@ set(PYBIND11_TEST_FILES
test_const_name test_const_name
test_constants_and_functions test_constants_and_functions
test_copy_move test_copy_move
test_cpp_conduit
test_custom_type_casters test_custom_type_casters
test_custom_type_setup test_custom_type_setup
test_docstring_options test_docstring_options
@ -153,11 +150,13 @@ set(PYBIND11_TEST_FILES
test_tagbased_polymorphic test_tagbased_polymorphic
test_thread test_thread
test_type_caster_pyobject_ptr test_type_caster_pyobject_ptr
test_type_caster_std_function_specializations
test_union test_union
test_unnamed_namespace_a test_unnamed_namespace_a
test_unnamed_namespace_b test_unnamed_namespace_b
test_vector_unique_ptr_member test_vector_unique_ptr_member
test_virtual_functions) test_virtual_functions
test_warnings)
# Invoking cmake with something like: # Invoking cmake with something like:
# cmake -DPYBIND11_TEST_OVERRIDE="test_callbacks.cpp;test_pickling.cpp" .. # cmake -DPYBIND11_TEST_OVERRIDE="test_callbacks.cpp;test_pickling.cpp" ..
@ -220,6 +219,8 @@ tests_extra_targets("test_exceptions.py;test_local_bindings.py;test_stl.py;test_
# And add additional targets for other tests. # And add additional targets for other tests.
tests_extra_targets("test_exceptions.py" "cross_module_interleaved_error_already_set") tests_extra_targets("test_exceptions.py" "cross_module_interleaved_error_already_set")
tests_extra_targets("test_gil_scoped.py" "cross_module_gil_utils") tests_extra_targets("test_gil_scoped.py" "cross_module_gil_utils")
tests_extra_targets("test_cpp_conduit.py"
"exo_planet_pybind11;exo_planet_c_api;home_planet_very_lonely_traveler")
set(PYBIND11_EIGEN_REPO set(PYBIND11_EIGEN_REPO
"https://gitlab.com/libeigen/eigen.git" "https://gitlab.com/libeigen/eigen.git"
@ -243,25 +244,21 @@ endif()
if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1) if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1)
# Try loading via newer Eigen's Eigen3Config first (bypassing tools/FindEigen3.cmake). # Try loading via newer Eigen's Eigen3Config first (bypassing tools/FindEigen3.cmake).
# Eigen 3.3.1+ exports a cmake 3.0+ target for handling dependency requirements, but also # Eigen 3.3.1+ exports a cmake 3.0+ target for handling dependency requirements, but also
# produces a fatal error if loaded from a pre-3.0 cmake.
if(DOWNLOAD_EIGEN) if(DOWNLOAD_EIGEN)
if(CMAKE_VERSION VERSION_LESS 3.11) if(CMAKE_VERSION VERSION_LESS 3.18)
message(FATAL_ERROR "CMake 3.11+ required when using DOWNLOAD_EIGEN") set(_opts)
else()
set(_opts SOURCE_SUBDIR no-cmake-build)
endif() endif()
include(FetchContent) include(FetchContent)
FetchContent_Declare( FetchContent_Declare(
eigen eigen
GIT_REPOSITORY "${PYBIND11_EIGEN_REPO}" GIT_REPOSITORY "${PYBIND11_EIGEN_REPO}"
GIT_TAG "${PYBIND11_EIGEN_VERSION_HASH}") GIT_TAG "${PYBIND11_EIGEN_VERSION_HASH}"
${_opts})
FetchContent_GetProperties(eigen) FetchContent_MakeAvailable(eigen)
if(NOT eigen_POPULATED) if(NOT CMAKE_VERSION VERSION_LESS 3.18)
message( set(EIGEN3_INCLUDE_DIR "${eigen_SOURCE_DIR}")
STATUS
"Downloading Eigen ${PYBIND11_EIGEN_VERSION_STRING} (${PYBIND11_EIGEN_VERSION_HASH}) from ${PYBIND11_EIGEN_REPO}"
)
FetchContent_Populate(eigen)
endif() endif()
set(EIGEN3_INCLUDE_DIR ${eigen_SOURCE_DIR}) set(EIGEN3_INCLUDE_DIR ${eigen_SOURCE_DIR})
@ -309,8 +306,7 @@ if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1)
if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1) if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1)
list(REMOVE_AT PYBIND11_TEST_FILES ${PYBIND11_TEST_FILES_EIGEN_I}) list(REMOVE_AT PYBIND11_TEST_FILES ${PYBIND11_TEST_FILES_EIGEN_I})
endif() endif()
message( message(STATUS "Building tests WITHOUT Eigen, use -DDOWNLOAD_EIGEN=ON to download")
STATUS "Building tests WITHOUT Eigen, use -DDOWNLOAD_EIGEN=ON on CMake 3.11+ to download")
endif() endif()
endif() endif()
@ -390,6 +386,9 @@ function(pybind11_enable_warnings target_name)
-Wdeprecated -Wdeprecated
-Wundef -Wundef
-Wnon-virtual-dtor) -Wnon-virtual-dtor)
if(DEFINED CMAKE_CXX_STANDARD AND NOT CMAKE_CXX_STANDARD VERSION_LESS 20)
target_compile_options(${target_name} PRIVATE -Wpedantic)
endif()
endif() endif()
if(PYBIND11_WERROR) if(PYBIND11_WERROR)
@ -489,15 +488,16 @@ foreach(target ${test_targets})
endforeach() endforeach()
endif() endif()
endif() endif()
if(SKBUILD)
install(TARGETS ${target} LIBRARY DESTINATION .)
endif()
endforeach() endforeach()
# Provide nice organisation in IDEs # Provide nice organisation in IDEs
if(NOT CMAKE_VERSION VERSION_LESS 3.8) source_group(
source_group( TREE "${CMAKE_CURRENT_SOURCE_DIR}/../include"
TREE "${CMAKE_CURRENT_SOURCE_DIR}/../include" PREFIX "Header Files"
PREFIX "Header Files" FILES ${PYBIND11_HEADERS})
FILES ${PYBIND11_HEADERS})
endif()
# Make sure pytest is found or produce a warning # Make sure pytest is found or produce a warning
pybind11_find_import(pytest VERSION 3.1) pybind11_find_import(pytest VERSION 3.1)

View File

@ -4,6 +4,8 @@ Extends output capture as needed by pybind11: ignore constructors, optional unor
Adds docstring and exceptions message sanitizers. Adds docstring and exceptions message sanitizers.
""" """
from __future__ import annotations
import contextlib import contextlib
import difflib import difflib
import gc import gc
@ -134,7 +136,7 @@ class Capture:
return Output(self.err) return Output(self.err)
@pytest.fixture() @pytest.fixture
def capture(capsys): def capture(capsys):
"""Extended `capsys` with context manager and custom equality operators""" """Extended `capsys` with context manager and custom equality operators"""
return Capture(capsys) return Capture(capsys)
@ -170,7 +172,7 @@ def _sanitize_docstring(thing):
return _sanitize_general(s) return _sanitize_general(s)
@pytest.fixture() @pytest.fixture
def doc(): def doc():
"""Sanitize docstrings and add custom failure explanation""" """Sanitize docstrings and add custom failure explanation"""
return SanitizedString(_sanitize_docstring) return SanitizedString(_sanitize_docstring)
@ -182,7 +184,7 @@ def _sanitize_message(thing):
return _hexadecimal.sub("0", s) return _hexadecimal.sub("0", s)
@pytest.fixture() @pytest.fixture
def msg(): def msg():
"""Sanitize messages and add custom failure explanation""" """Sanitize messages and add custom failure explanation"""
return SanitizedString(_sanitize_message) return SanitizedString(_sanitize_message)

View File

@ -190,7 +190,7 @@ public:
t1 = &p.first; t1 = &p.first;
} }
} }
} catch (const std::out_of_range &) { } catch (const std::out_of_range &) { // NOLINT(bugprone-empty-catch)
} }
if (!t1) { if (!t1) {
throw std::runtime_error("Unknown class passed to ConstructorStats::get()"); throw std::runtime_error("Unknown class passed to ConstructorStats::get()");

View File

@ -92,6 +92,9 @@ extern "C" PYBIND11_EXPORT PyObject *PyInit_cross_module_gil_utils() {
if (m != nullptr) { if (m != nullptr) {
static_assert(sizeof(&gil_acquire) == sizeof(void *), static_assert(sizeof(&gil_acquire) == sizeof(void *),
"Function pointer must have the same size as void*"); "Function pointer must have the same size as void*");
#ifdef Py_GIL_DISABLED
PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED);
#endif
ADD_FUNCTION("gil_acquire_funcaddr", gil_acquire) ADD_FUNCTION("gil_acquire_funcaddr", gil_acquire)
ADD_FUNCTION("gil_multi_acquire_release_funcaddr", gil_multi_acquire_release) ADD_FUNCTION("gil_multi_acquire_release_funcaddr", gil_multi_acquire_release)
ADD_FUNCTION("gil_acquire_inner_custom_funcaddr", ADD_FUNCTION("gil_acquire_inner_custom_funcaddr",

View File

@ -42,6 +42,9 @@ extern "C" PYBIND11_EXPORT PyObject *PyInit_cross_module_interleaved_error_alrea
if (m != nullptr) { if (m != nullptr) {
static_assert(sizeof(&interleaved_error_already_set) == sizeof(void *), static_assert(sizeof(&interleaved_error_already_set) == sizeof(void *),
"Function pointer must have the same size as void *"); "Function pointer must have the same size as void *");
#ifdef Py_GIL_DISABLED
PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED);
#endif
PyModule_AddObject( PyModule_AddObject(
m, m,
"funcaddr", "funcaddr",

View File

@ -0,0 +1,10 @@
from __future__ import annotations
class PythonMyException7(Exception):
def __init__(self, message):
self.message = message
super().__init__(message)
def __str__(self):
return "[PythonMyException7]: " + self.message.a

View File

@ -11,4 +11,6 @@
#include "test_eigen_tensor.inl" #include "test_eigen_tensor.inl"
PYBIND11_MODULE(eigen_tensor_avoid_stl_array, m) { eigen_tensor_test::test_module(m); } PYBIND11_MODULE(eigen_tensor_avoid_stl_array, m, pybind11::mod_gil_not_used()) {
eigen_tensor_test::test_module(m);
}

View File

@ -1,5 +1,8 @@
from __future__ import annotations
import platform import platform
import sys import sys
import sysconfig
import pytest import pytest
@ -9,6 +12,7 @@ WIN = sys.platform.startswith("win32") or sys.platform.startswith("cygwin")
CPYTHON = platform.python_implementation() == "CPython" CPYTHON = platform.python_implementation() == "CPython"
PYPY = platform.python_implementation() == "PyPy" PYPY = platform.python_implementation() == "PyPy"
PY_GIL_DISABLED = bool(sysconfig.get_config_var("Py_GIL_DISABLED"))
def deprecated_call(): def deprecated_call():

103
tests/exo_planet_c_api.cpp Normal file
View File

@ -0,0 +1,103 @@
// Copyright (c) 2024 The pybind Community.
// THIS MUST STAY AT THE TOP!
#include <pybind11/pybind11.h> // EXCLUSIVELY for PYBIND11_PLATFORM_ABI_ID
// Potential future direction to maximize reusability:
// (e.g. for use from SWIG, Cython, PyCLIF, nanobind):
// #include <pybind11/compat/platform_abi_id.h>
// This would only depend on:
// 1. A C++ compiler, WITHOUT requiring -fexceptions.
// 2. Python.h
#include "test_cpp_conduit_traveler_types.h"
#include <Python.h>
#include <typeinfo>
namespace {
void *get_cpp_conduit_void_ptr(PyObject *py_obj, const std::type_info *cpp_type_info) {
PyObject *cpp_type_info_capsule
= PyCapsule_New(const_cast<void *>(static_cast<const void *>(cpp_type_info)),
typeid(std::type_info).name(),
nullptr);
if (cpp_type_info_capsule == nullptr) {
return nullptr;
}
PyObject *cpp_conduit = PyObject_CallMethod(py_obj,
"_pybind11_conduit_v1_",
"yOy",
PYBIND11_PLATFORM_ABI_ID,
cpp_type_info_capsule,
"raw_pointer_ephemeral");
Py_DECREF(cpp_type_info_capsule);
if (cpp_conduit == nullptr) {
return nullptr;
}
void *void_ptr = PyCapsule_GetPointer(cpp_conduit, cpp_type_info->name());
Py_DECREF(cpp_conduit);
if (PyErr_Occurred()) {
return nullptr;
}
return void_ptr;
}
template <typename T>
T *get_cpp_conduit_type_ptr(PyObject *py_obj) {
void *void_ptr = get_cpp_conduit_void_ptr(py_obj, &typeid(T));
if (void_ptr == nullptr) {
return nullptr;
}
return static_cast<T *>(void_ptr);
}
extern "C" PyObject *wrapGetLuggage(PyObject * /*self*/, PyObject *traveler) {
const auto *cpp_traveler
= get_cpp_conduit_type_ptr<pybind11_tests::test_cpp_conduit::Traveler>(traveler);
if (cpp_traveler == nullptr) {
return nullptr;
}
return PyUnicode_FromString(cpp_traveler->luggage.c_str());
}
extern "C" PyObject *wrapGetPoints(PyObject * /*self*/, PyObject *premium_traveler) {
const auto *cpp_premium_traveler
= get_cpp_conduit_type_ptr<pybind11_tests::test_cpp_conduit::PremiumTraveler>(
premium_traveler);
if (cpp_premium_traveler == nullptr) {
return nullptr;
}
return PyLong_FromLong(static_cast<long>(cpp_premium_traveler->points));
}
PyMethodDef ThisMethodDef[] = {{"GetLuggage", wrapGetLuggage, METH_O, nullptr},
{"GetPoints", wrapGetPoints, METH_O, nullptr},
{nullptr, nullptr, 0, nullptr}};
struct PyModuleDef ThisModuleDef = {
PyModuleDef_HEAD_INIT, // m_base
"exo_planet_c_api", // m_name
nullptr, // m_doc
-1, // m_size
ThisMethodDef, // m_methods
nullptr, // m_slots
nullptr, // m_traverse
nullptr, // m_clear
nullptr // m_free
};
} // namespace
#if defined(WIN32) || defined(_WIN32)
# define EXO_PLANET_C_API_EXPORT __declspec(dllexport)
#else
# define EXO_PLANET_C_API_EXPORT __attribute__((visibility("default")))
#endif
extern "C" EXO_PLANET_C_API_EXPORT PyObject *PyInit_exo_planet_c_api() {
PyObject *m = PyModule_Create(&ThisModuleDef);
if (m == nullptr) {
return nullptr;
}
return m;
}

View File

@ -0,0 +1,19 @@
// Copyright (c) 2024 The pybind Community.
#if defined(PYBIND11_INTERNALS_VERSION)
# undef PYBIND11_INTERNALS_VERSION
#endif
#define PYBIND11_INTERNALS_VERSION 900000001
#include "test_cpp_conduit_traveler_bindings.h"
namespace pybind11_tests {
namespace test_cpp_conduit {
PYBIND11_MODULE(exo_planet_pybind11, m) {
wrap_traveler(m);
m.def("wrap_very_lonely_traveler", [m]() { wrap_very_lonely_traveler(m); });
}
} // namespace test_cpp_conduit
} // namespace pybind11_tests

View File

@ -1,3 +1,5 @@
from __future__ import annotations
import contextlib import contextlib
import os import os
import string import string
@ -7,7 +9,6 @@ import tarfile
import zipfile import zipfile
# These tests must be run explicitly # These tests must be run explicitly
# They require CMake 3.15+ (--install)
DIR = os.path.abspath(os.path.dirname(__file__)) DIR = os.path.abspath(os.path.dirname(__file__))
MAIN_DIR = os.path.dirname(os.path.dirname(DIR)) MAIN_DIR = os.path.dirname(os.path.dirname(DIR))
@ -46,16 +47,20 @@ main_headers = {
"include/pybind11/stl_bind.h", "include/pybind11/stl_bind.h",
"include/pybind11/type_caster_pyobject_ptr.h", "include/pybind11/type_caster_pyobject_ptr.h",
"include/pybind11/typing.h", "include/pybind11/typing.h",
"include/pybind11/warnings.h",
} }
detail_headers = { detail_headers = {
"include/pybind11/detail/class.h", "include/pybind11/detail/class.h",
"include/pybind11/detail/common.h", "include/pybind11/detail/common.h",
"include/pybind11/detail/cpp_conduit.h",
"include/pybind11/detail/descr.h", "include/pybind11/detail/descr.h",
"include/pybind11/detail/init.h", "include/pybind11/detail/init.h",
"include/pybind11/detail/internals.h", "include/pybind11/detail/internals.h",
"include/pybind11/detail/type_caster_base.h", "include/pybind11/detail/type_caster_base.h",
"include/pybind11/detail/typeid.h", "include/pybind11/detail/typeid.h",
"include/pybind11/detail/value_and_holder.h",
"include/pybind11/detail/exception_translation.h",
} }
eigen_headers = { eigen_headers = {

View File

@ -1,3 +1,5 @@
from __future__ import annotations
import os import os
import subprocess import subprocess
import sys import sys

View File

@ -0,0 +1,13 @@
// Copyright (c) 2024 The pybind Community.
#include "test_cpp_conduit_traveler_bindings.h"
namespace pybind11_tests {
namespace test_cpp_conduit {
PYBIND11_MODULE(home_planet_very_lonely_traveler, m) {
m.def("wrap_very_lonely_traveler", [m]() { wrap_very_lonely_traveler(m); });
}
} // namespace test_cpp_conduit
} // namespace pybind11_tests

View File

@ -56,13 +56,13 @@ private:
std::string message = ""; std::string message = "";
}; };
PYBIND11_MAKE_OPAQUE(LocalVec); PYBIND11_MAKE_OPAQUE(LocalVec)
PYBIND11_MAKE_OPAQUE(LocalVec2); PYBIND11_MAKE_OPAQUE(LocalVec2)
PYBIND11_MAKE_OPAQUE(LocalMap); PYBIND11_MAKE_OPAQUE(LocalMap)
PYBIND11_MAKE_OPAQUE(NonLocalVec); PYBIND11_MAKE_OPAQUE(NonLocalVec)
// PYBIND11_MAKE_OPAQUE(NonLocalVec2); // same type as LocalVec2 // PYBIND11_MAKE_OPAQUE(NonLocalVec2) // same type as LocalVec2
PYBIND11_MAKE_OPAQUE(NonLocalMap); PYBIND11_MAKE_OPAQUE(NonLocalMap)
PYBIND11_MAKE_OPAQUE(NonLocalMap2); PYBIND11_MAKE_OPAQUE(NonLocalMap2)
// Simple bindings (used with the above): // Simple bindings (used with the above):
template <typename T, int Adjust = 0, typename... Args> template <typename T, int Adjust = 0, typename... Args>
@ -70,7 +70,7 @@ py::class_<T> bind_local(Args &&...args) {
return py::class_<T>(std::forward<Args>(args)...).def(py::init<int>()).def("get", [](T &i) { return py::class_<T>(std::forward<Args>(args)...).def(py::init<int>()).def("get", [](T &i) {
return i.i + Adjust; return i.i + Adjust;
}); });
}; }
// Simulate a foreign library base class (to match the example in the docs): // Simulate a foreign library base class (to match the example in the docs):
namespace pets { namespace pets {

View File

@ -16,7 +16,7 @@
#include <numeric> #include <numeric>
#include <utility> #include <utility>
PYBIND11_MODULE(pybind11_cross_module_tests, m) { PYBIND11_MODULE(pybind11_cross_module_tests, m, py::mod_gil_not_used()) {
m.doc() = "pybind11 cross-module test module"; m.doc() = "pybind11 cross-module test module";
// test_local_bindings.py tests: // test_local_bindings.py tests:

View File

@ -58,7 +58,7 @@ void bind_ConstructorStats(py::module_ &m) {
// registered instances to allow instance cleanup checks (invokes a GC first) // registered instances to allow instance cleanup checks (invokes a GC first)
.def_static("detail_reg_inst", []() { .def_static("detail_reg_inst", []() {
ConstructorStats::gc(); ConstructorStats::gc();
return py::detail::get_internals().registered_instances.size(); return py::detail::num_registered_instances();
}); });
} }
@ -75,7 +75,7 @@ const char *cpp_std() {
#endif #endif
} }
PYBIND11_MODULE(pybind11_tests, m) { PYBIND11_MODULE(pybind11_tests, m, py::mod_gil_not_used()) {
m.doc() = "pybind11 test module"; m.doc() = "pybind11 test module";
// Intentionally kept minimal to not create a maintenance chore // Intentionally kept minimal to not create a maintenance chore

View File

@ -3,6 +3,8 @@
#include <pybind11/eval.h> #include <pybind11/eval.h>
#include <pybind11/pybind11.h> #include <pybind11/pybind11.h>
#include <memory>
namespace py = pybind11; namespace py = pybind11;
using namespace pybind11::literals; using namespace pybind11::literals;
@ -52,6 +54,17 @@ union IntFloat {
float f; float f;
}; };
class UnusualOpRef {
public:
using NonTrivialType = std::shared_ptr<int>; // Almost any non-trivial type will do.
// Overriding operator& should not break pybind11.
NonTrivialType operator&() { return non_trivial_member; }
NonTrivialType operator&() const { return non_trivial_member; }
private:
NonTrivialType non_trivial_member;
};
/// Custom cast-only type that casts to a string "rvalue" or "lvalue" depending on the cast /// Custom cast-only type that casts to a string "rvalue" or "lvalue" depending on the cast
/// context. Used to test recursive casters (e.g. std::tuple, stl containers). /// context. Used to test recursive casters (e.g. std::tuple, stl containers).
struct RValueCaster {}; struct RValueCaster {};

17
tests/pyproject.toml Normal file
View File

@ -0,0 +1,17 @@
# Warning: this is currently used for pyodide, and is not a general out-of-tree
# builder for the tests (yet). Specifically, wheels can't be built from SDists.
[build-system]
requires = ["scikit-build-core"]
build-backend = "scikit_build_core.build"
[project]
name = "pybind11_tests"
version = "0.0.1"
dependencies = ["pytest", "pytest-timeout", "numpy", "scipy"]
[tool.scikit-build.cmake.define]
PYBIND11_FINDPYTHON = true
[tool.cibuildwheel]
test-command = "pytest -o timeout=0 -p no:cacheprovider {project}/tests/test_*.py"

View File

@ -1,15 +1,12 @@
--only-binary=:all: --only-binary=:all:
build~=0.9; python_version=="3.6" build~=1.0; python_version>="3.8"
build~=1.0; python_version>="3.7"
numpy~=1.20.0; python_version=="3.7" and platform_python_implementation=="PyPy"
numpy~=1.23.0; python_version=="3.8" and platform_python_implementation=="PyPy" numpy~=1.23.0; python_version=="3.8" and platform_python_implementation=="PyPy"
numpy~=1.25.0; python_version=="3.9" and platform_python_implementation=='PyPy' numpy~=1.25.0; python_version=="3.9" and platform_python_implementation=='PyPy'
numpy~=1.19.3; platform_python_implementation!="PyPy" and python_version=="3.6" numpy~=1.21.5; platform_python_implementation!="PyPy" and python_version>="3.8" and python_version<"3.10"
numpy~=1.21.5; platform_python_implementation!="PyPy" and python_version>="3.7" and python_version<"3.10"
numpy~=1.22.2; platform_python_implementation!="PyPy" and python_version=="3.10" numpy~=1.22.2; platform_python_implementation!="PyPy" and python_version=="3.10"
numpy~=1.26.0; platform_python_implementation!="PyPy" and python_version>="3.11" and python_version<"3.13" numpy~=1.26.0; platform_python_implementation!="PyPy" and python_version>="3.11" and python_version<"3.13"
pytest~=7.0 pytest~=7.0
pytest-timeout pytest-timeout
scipy~=1.5.4; platform_python_implementation!="PyPy" and python_version<"3.10" scipy~=1.5.4; platform_python_implementation!="PyPy" and python_version<"3.10"
scipy~=1.8.0; platform_python_implementation!="PyPy" and python_version=="3.10" scipy~=1.8.0; platform_python_implementation!="PyPy" and python_version=="3.10" and sys_platform!='win32'
scipy~=1.11.1; platform_python_implementation!="PyPy" and python_version>="3.11" and python_version<"3.13" scipy~=1.11.1; platform_python_implementation!="PyPy" and python_version>="3.11" and python_version<"3.13" and sys_platform!='win32'

View File

@ -1,10 +1,17 @@
from __future__ import annotations
import sys
import pytest import pytest
asyncio = pytest.importorskip("asyncio") asyncio = pytest.importorskip("asyncio")
m = pytest.importorskip("pybind11_tests.async_module") m = pytest.importorskip("pybind11_tests.async_module")
if sys.platform.startswith("emscripten"):
pytest.skip("Can't run a new event_loop in pyodide", allow_module_level=True)
@pytest.fixture()
@pytest.fixture
def event_loop(): def event_loop():
loop = asyncio.new_event_loop() loop = asyncio.new_event_loop()
yield loop yield loop

View File

@ -167,6 +167,18 @@ TEST_SUBMODULE(buffers, m) {
sizeof(float)}); sizeof(float)});
}); });
class BrokenMatrix : public Matrix {
public:
BrokenMatrix(py::ssize_t rows, py::ssize_t cols) : Matrix(rows, cols) {}
void throw_runtime_error() { throw std::runtime_error("See PR #5324 for context."); }
};
py::class_<BrokenMatrix>(m, "BrokenMatrix", py::buffer_protocol())
.def(py::init<py::ssize_t, py::ssize_t>())
.def_buffer([](BrokenMatrix &m) {
m.throw_runtime_error();
return py::buffer_info();
});
// test_inherited_protocol // test_inherited_protocol
class SquareMatrix : public Matrix { class SquareMatrix : public Matrix {
public: public:

View File

@ -1,3 +1,5 @@
from __future__ import annotations
import ctypes import ctypes
import io import io
import struct import struct
@ -226,3 +228,10 @@ def test_buffer_docstring():
m.get_buffer_info.__doc__.strip() m.get_buffer_info.__doc__.strip()
== "get_buffer_info(arg0: Buffer) -> pybind11_tests.buffers.buffer_info" == "get_buffer_info(arg0: Buffer) -> pybind11_tests.buffers.buffer_info"
) )
def test_buffer_exception():
with pytest.raises(BufferError, match="Error getting buffer") as excinfo:
memoryview(m.BrokenMatrix(1, 1))
assert isinstance(excinfo.value.__cause__, RuntimeError)
assert "for context" in str(excinfo.value.__cause__)

View File

@ -1,3 +1,5 @@
from __future__ import annotations
import sys import sys
import pytest import pytest
@ -295,7 +297,7 @@ def test_int_convert():
cant_convert(3.14159) cant_convert(3.14159)
# TODO: Avoid DeprecationWarning in `PyLong_AsLong` (and similar) # TODO: Avoid DeprecationWarning in `PyLong_AsLong` (and similar)
# TODO: PyPy 3.8 does not behave like CPython 3.8 here yet (7.3.7) # TODO: PyPy 3.8 does not behave like CPython 3.8 here yet (7.3.7)
if (3, 8) <= sys.version_info < (3, 10) and env.CPYTHON: if sys.version_info < (3, 10) and env.CPYTHON:
with env.deprecated_call(): with env.deprecated_call():
assert convert(Int()) == 42 assert convert(Int()) == 42
else: else:
@ -366,6 +368,8 @@ def test_tuple(doc):
""" """
) )
assert doc(m.empty_tuple) == """empty_tuple() -> tuple[()]"""
assert m.rvalue_pair() == ("rvalue", "rvalue") assert m.rvalue_pair() == ("rvalue", "rvalue")
assert m.lvalue_pair() == ("lvalue", "lvalue") assert m.lvalue_pair() == ("lvalue", "lvalue")
assert m.rvalue_tuple() == ("rvalue", "rvalue", "rvalue") assert m.rvalue_tuple() == ("rvalue", "rvalue", "rvalue")

View File

@ -95,7 +95,7 @@ TEST_SUBMODULE(call_policies, m) {
}, },
py::call_guard<DependentGuard, CustomGuard>()); py::call_guard<DependentGuard, CustomGuard>());
#if defined(WITH_THREAD) && !defined(PYPY_VERSION) #if !defined(PYPY_VERSION)
// `py::call_guard<py::gil_scoped_release>()` should work in PyPy as well, // `py::call_guard<py::gil_scoped_release>()` should work in PyPy as well,
// but it's unclear how to test it without `PyGILState_GetThisThreadState`. // but it's unclear how to test it without `PyGILState_GetThisThreadState`.
auto report_gil_status = []() { auto report_gil_status = []() {
@ -109,5 +109,31 @@ TEST_SUBMODULE(call_policies, m) {
m.def("with_gil", report_gil_status); m.def("with_gil", report_gil_status);
m.def("without_gil", report_gil_status, py::call_guard<py::gil_scoped_release>()); m.def("without_gil", report_gil_status, py::call_guard<py::gil_scoped_release>());
class MyInt {
public:
MyInt(int i)
: i_(i)
{}
int i_;
};
py::class_<MyInt>(m, "MyInt")
.def(py::init<int>());
py::implicitly_convertible<int, MyInt>();
// This function races
m.def("guard_without_gil_implicit_conversion",
[](MyInt my_int) {
return my_int.i_;
},
py::call_guard<py::gil_scoped_release>());
// This function works fine
m.def("without_gil_implicit_conversion",
[](MyInt my_int) {
py::gil_scoped_release gil_release;
return my_int.i_;
});
#endif #endif
} }

View File

@ -1,4 +1,7 @@
from __future__ import annotations
import pytest import pytest
import threading
import env # noqa: F401 import env # noqa: F401
from pybind11_tests import ConstructorStats from pybind11_tests import ConstructorStats
@ -245,3 +248,33 @@ def test_call_guard():
if hasattr(m, "with_gil"): if hasattr(m, "with_gil"):
assert m.with_gil() == "GIL held" assert m.with_gil() == "GIL held"
assert m.without_gil() == "GIL released" assert m.without_gil() == "GIL released"
@pytest.mark.parametrize(
"test_func",
["guard_without_gil_implicit_conversion", "without_gil_implicit_conversion"],
)
def test_without_gil_implicit_conversion(test_func):
try:
func = getattr(m, test_func)
except AttributeError:
return
num_threads = 16
def check(results, i):
results[i] = func(42) == 42
results = [None] * num_threads
for _ in range(int(1e4)):
threads = []
for i in range(num_threads):
thread = threading.Thread(target=check, args=(results, i))
thread.start()
threads.append(thread)
for thread in threads:
thread.join()
ok = all(results)
if not ok:
raise AssertionError(results)

View File

@ -148,7 +148,7 @@ TEST_SUBMODULE(callbacks, m) {
m.def("dummy_function2", [](int i, int j) { return i + j; }); m.def("dummy_function2", [](int i, int j) { return i + j; });
m.def( m.def(
"roundtrip", "roundtrip",
[](std::function<int(int)> f, bool expect_none = false) { [](std::function<int(int)> f, bool expect_none) {
if (expect_none && f) { if (expect_none && f) {
throw std::runtime_error("Expected None to be converted to empty std::function"); throw std::runtime_error("Expected None to be converted to empty std::function");
} }

View File

@ -1,3 +1,6 @@
from __future__ import annotations
import sys
import time import time
from threading import Thread from threading import Thread
@ -151,6 +154,7 @@ def test_python_builtins():
assert m.test_sum_builtin(sum, []) == 0 assert m.test_sum_builtin(sum, []) == 0
@pytest.mark.skipif(sys.platform.startswith("emscripten"), reason="Requires threads")
def test_async_callbacks(): def test_async_callbacks():
# serves as state for async callback # serves as state for async callback
class Item: class Item:
@ -174,6 +178,7 @@ def test_async_callbacks():
assert sum(res) == sum(x + 3 for x in work) assert sum(res) == sum(x + 3 for x in work)
@pytest.mark.skipif(sys.platform.startswith("emscripten"), reason="Requires threads")
def test_async_async_callbacks(): def test_async_async_callbacks():
t = Thread(target=test_async_callbacks) t = Thread(target=test_async_callbacks)
t.start() t.start()

View File

@ -1,3 +1,5 @@
from __future__ import annotations
import datetime import datetime
import pytest import pytest

View File

@ -1,3 +1,5 @@
from __future__ import annotations
from unittest import mock from unittest import mock
import pytest import pytest

View File

@ -1,13 +1,4 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.15...3.30)
# The `cmake_minimum_required(VERSION 3.5...3.29)` syntax does not work with
# some versions of VS that have a patched CMake 3.11. This forces us to emulate
# the behavior using the following workaround:
if(${CMAKE_VERSION} VERSION_LESS 3.29)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else()
cmake_policy(VERSION 3.29)
endif()
project(test_installed_embed CXX) project(test_installed_embed CXX)

View File

@ -1,14 +1,4 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.15...3.30)
project(test_installed_module CXX)
# The `cmake_minimum_required(VERSION 3.5...3.29)` syntax does not work with
# some versions of VS that have a patched CMake 3.11. This forces us to emulate
# the behavior using the following workaround:
if(${CMAKE_VERSION} VERSION_LESS 3.29)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else()
cmake_policy(VERSION 3.29)
endif()
project(test_installed_function CXX) project(test_installed_function CXX)

View File

@ -1,13 +1,4 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.15...3.30)
# The `cmake_minimum_required(VERSION 3.5...3.29)` syntax does not work with
# some versions of VS that have a patched CMake 3.11. This forces us to emulate
# the behavior using the following workaround:
if(${CMAKE_VERSION} VERSION_LESS 3.29)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else()
cmake_policy(VERSION 3.29)
endif()
project(test_installed_target CXX) project(test_installed_target CXX)

View File

@ -1,6 +1,6 @@
#include <pybind11/pybind11.h> #include <pybind11/pybind11.h>
namespace py = pybind11; namespace py = pybind11;
PYBIND11_MODULE(test_cmake_build, m) { PYBIND11_MODULE(test_cmake_build, m, py::mod_gil_not_used()) {
m.def("add", [](int i, int j) { return i + j; }); m.def("add", [](int i, int j) { return i + j; });
} }

View File

@ -1,13 +1,4 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.15...3.30)
# The `cmake_minimum_required(VERSION 3.5...3.29)` syntax does not work with
# some versions of VS that have a patched CMake 3.11. This forces us to emulate
# the behavior using the following workaround:
if(${CMAKE_VERSION} VERSION_LESS 3.29)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else()
cmake_policy(VERSION 3.29)
endif()
project(test_subdirectory_embed CXX) project(test_subdirectory_embed CXX)

View File

@ -1,13 +1,4 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.15...3.30)
# The `cmake_minimum_required(VERSION 3.5...3.29)` syntax does not work with
# some versions of VS that have a patched CMake 3.11. This forces us to emulate
# the behavior using the following workaround:
if(${CMAKE_VERSION} VERSION_LESS 3.29)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else()
cmake_policy(VERSION 3.29)
endif()
project(test_subdirectory_function CXX) project(test_subdirectory_function CXX)

View File

@ -1,13 +1,4 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.15...3.30)
# The `cmake_minimum_required(VERSION 3.5...3.29)` syntax does not work with
# some versions of VS that have a patched CMake 3.11. This forces us to emulate
# the behavior using the following workaround:
if(${CMAKE_VERSION} VERSION_LESS 3.29)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else()
cmake_policy(VERSION 3.29)
endif()
project(test_subdirectory_target CXX) project(test_subdirectory_target CXX)

View File

@ -1,3 +1,5 @@
from __future__ import annotations
import sys import sys
import test_cmake_build import test_cmake_build

View File

@ -1,3 +1,5 @@
from __future__ import annotations
import pytest import pytest
from pybind11_tests import const_name as m from pybind11_tests import const_name as m

View File

@ -1,3 +1,5 @@
from __future__ import annotations
import pytest import pytest
m = pytest.importorskip("pybind11_tests.constants_and_functions") m = pytest.importorskip("pybind11_tests.constants_and_functions")

View File

@ -157,6 +157,13 @@ public:
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
PYBIND11_NAMESPACE_END(pybind11) PYBIND11_NAMESPACE_END(pybind11)
namespace {
py::object CastUnusualOpRefConstRef(const UnusualOpRef &cref) { return py::cast(cref); }
py::object CastUnusualOpRefMovable(UnusualOpRef &&mvbl) { return py::cast(std::move(mvbl)); }
} // namespace
TEST_SUBMODULE(copy_move_policies, m) { TEST_SUBMODULE(copy_move_policies, m) {
// test_lacking_copy_ctor // test_lacking_copy_ctor
py::class_<lacking_copy_ctor>(m, "lacking_copy_ctor") py::class_<lacking_copy_ctor>(m, "lacking_copy_ctor")
@ -293,6 +300,11 @@ TEST_SUBMODULE(copy_move_policies, m) {
// Make sure that cast from pytype rvalue to other pytype works // Make sure that cast from pytype rvalue to other pytype works
m.def("get_pytype_rvalue_castissue", [](double i) { return py::float_(i).cast<py::int_>(); }); m.def("get_pytype_rvalue_castissue", [](double i) { return py::float_(i).cast<py::int_>(); });
py::class_<UnusualOpRef>(m, "UnusualOpRef");
m.def("CallCastUnusualOpRefConstRef",
[]() { return CastUnusualOpRefConstRef(UnusualOpRef()); });
m.def("CallCastUnusualOpRefMovable", []() { return CastUnusualOpRefMovable(UnusualOpRef()); });
} }
/* /*

View File

@ -1,3 +1,5 @@
from __future__ import annotations
import pytest import pytest
from pybind11_tests import copy_move_policies as m from pybind11_tests import copy_move_policies as m
@ -130,3 +132,9 @@ def test_pytype_rvalue_cast():
value = m.get_pytype_rvalue_castissue(1.0) value = m.get_pytype_rvalue_castissue(1.0)
assert value == 1 assert value == 1
def test_unusual_op_ref():
# Merely to test that this still exists and built successfully.
assert m.CallCastUnusualOpRefConstRef().__class__.__name__ == "UnusualOpRef"
assert m.CallCastUnusualOpRefMovable().__class__.__name__ == "UnusualOpRef"

View File

@ -0,0 +1,22 @@
// Copyright (c) 2024 The pybind Community.
#include "pybind11_tests.h"
#include "test_cpp_conduit_traveler_bindings.h"
#include <typeinfo>
namespace pybind11_tests {
namespace test_cpp_conduit {
TEST_SUBMODULE(cpp_conduit, m) {
m.attr("PYBIND11_PLATFORM_ABI_ID") = py::bytes(PYBIND11_PLATFORM_ABI_ID);
m.attr("cpp_type_info_capsule_Traveler")
= py::capsule(&typeid(Traveler), typeid(std::type_info).name());
m.attr("cpp_type_info_capsule_int") = py::capsule(&typeid(int), typeid(std::type_info).name());
wrap_traveler(m);
wrap_lonely_traveler(m);
}
} // namespace test_cpp_conduit
} // namespace pybind11_tests

Some files were not shown because too many files have changed in this diff Show More