Merge branch 'v2.13' into stable

This commit is contained in:
Henry Schreiner 2024-08-15 00:17:13 -04:00
commit 5211a1715a
50 changed files with 571 additions and 173 deletions

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

@ -340,6 +340,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 }}"
@ -501,7 +507,9 @@ jobs:
- { gcc: 7, std: 17 } - { gcc: 7, std: 17 }
- { gcc: 8, std: 14 } - { gcc: 8, std: 14 }
- { gcc: 8, std: 17 } - { gcc: 8, std: 17 }
- { gcc: 9, std: 20 }
- { 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 }

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.20
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

@ -102,7 +102,7 @@ jobs:
- uses: actions/download-artifact@v4 - uses: actions/download-artifact@v4
- name: Generate artifact attestation for sdist and wheel - name: Generate artifact attestation for sdist and wheel
uses: actions/attest-build-provenance@173725a1209d09b31f9d30a3890cf2757ebbff0d # v1.1.2 uses: actions/attest-build-provenance@310b0a4a3b0b78ef57ecda988ee04b132db73ef8 # v1.4.1
with: with:
subject-path: "*/pybind11*" subject-path: "*/pybind11*"

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.5.6
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.1"
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:
@ -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.6"
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.1
hooks: hooks:
- id: check-readthedocs - id: check-readthedocs
- id: check-github-workflows - id: check-github-workflows

View File

@ -154,6 +154,7 @@ set(PYBIND11_HEADERS
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/attr.h include/pybind11/attr.h
include/pybind11/buffer_info.h include/pybind11/buffer_info.h
include/pybind11/cast.h include/pybind11/cast.h

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

@ -15,6 +15,98 @@ 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>`_
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) Version 2.13.1 (June 26, 2024)
------------------------------ ------------------------------

View File

@ -25,7 +25,7 @@ A Python extension module can be created with just a few lines of code:
find_package(pybind11 CONFIG REQUIRED) find_package(pybind11 CONFIG REQUIRED)
pybind11_add_module(example example.cpp) pybind11_add_module(example example.cpp)
install(TARGET example DESTINATION .) install(TARGETS example DESTINATION .)
(You use the ``add_subdirectory`` instead, see the example in :ref:`cmake`.) In (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 this example, the code is located in a file named :file:`example.cpp`. Either
@ -388,7 +388,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),
@ -719,7 +719,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

@ -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 \

View File

@ -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)

View File

@ -11,11 +11,11 @@
#define PYBIND11_VERSION_MAJOR 2 #define PYBIND11_VERSION_MAJOR 2
#define PYBIND11_VERSION_MINOR 13 #define PYBIND11_VERSION_MINOR 13
#define PYBIND11_VERSION_PATCH 1 #define PYBIND11_VERSION_PATCH 4
// 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 0x020D0100 #define PYBIND11_VERSION_HEX 0x020D0400
// Define some generic pybind11 helper macros for warning management. // Define some generic pybind11 helper macros for warning management.
// //
@ -462,6 +462,22 @@ 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, ...) \ #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) \
@ -538,7 +554,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

@ -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

@ -148,20 +148,35 @@ struct override_hash {
using instance_map = std::unordered_multimap<const void *, instance *>; 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. // Instance map shards are used to reduce mutex contention in free-threaded Python.
struct instance_map_shard { struct instance_map_shard {
std::mutex mutex;
instance_map registered_instances; instance_map registered_instances;
pymutex mutex;
// alignas(64) would be better, but causes compile errors in macOS before 10.14 (see #5200) // alignas(64) would be better, but causes compile errors in macOS before 10.14 (see #5200)
char padding[64 - (sizeof(std::mutex) + sizeof(instance_map)) % 64]; 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 #ifdef Py_GIL_DISABLED
std::mutex mutex; pymutex mutex;
#endif #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;
@ -538,7 +553,7 @@ PYBIND11_NOINLINE internals &get_internals() {
} }
#endif #endif
internals_ptr->istate = tstate->interp; internals_ptr->istate = tstate->interp;
state_dict[PYBIND11_INTERNALS_ID] = capsule(internals_pp); 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();
@ -614,7 +629,7 @@ inline local_internals &get_local_internals() {
} }
#ifdef Py_GIL_DISABLED #ifdef Py_GIL_DISABLED
# define PYBIND11_LOCK_INTERNALS(internals) std::unique_lock<std::mutex> lock((internals).mutex) # define PYBIND11_LOCK_INTERNALS(internals) std::unique_lock<pymutex> lock((internals).mutex)
#else #else
# define PYBIND11_LOCK_INTERNALS(internals) # define PYBIND11_LOCK_INTERNALS(internals)
#endif #endif
@ -651,7 +666,7 @@ inline auto with_instance_map(const void *ptr,
auto idx = static_cast<size_t>(hash & internals.instance_shards_mask); auto idx = static_cast<size_t>(hash & internals.instance_shards_mask);
auto &shard = internals.instance_shards[idx]; auto &shard = internals.instance_shards[idx];
std::unique_lock<std::mutex> lock(shard.mutex); std::unique_lock<pymutex> lock(shard.mutex);
return cb(shard.registered_instances); return cb(shard.registered_instances);
#else #else
(void) ptr; (void) ptr;
@ -667,7 +682,7 @@ inline size_t num_registered_instances() {
size_t count = 0; size_t count = 0;
for (size_t i = 0; i <= internals.instance_shards_mask; ++i) { for (size_t i = 0; i <= internals.instance_shards_mask; ++i) {
auto &shard = internals.instance_shards[i]; auto &shard = internals.instance_shards[i];
std::unique_lock<std::mutex> lock(shard.mutex); std::unique_lock<pymutex> lock(shard.mutex);
count += shard.registered_instances.size(); count += shard.registered_instances.size();
} }
return count; return count;

View File

@ -14,6 +14,7 @@
#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 <iterator> #include <iterator>
@ -259,67 +260,6 @@ PYBIND11_NOINLINE handle find_registered_python_instance(void *src,
}); });
} }
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:
@ -488,7 +428,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));
} }
} }

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

@ -469,9 +469,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

@ -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

@ -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())
@ -1986,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

@ -33,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 {
@ -72,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.
@ -80,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)
@ -98,7 +100,10 @@ class Never : public none {
using none::none; using none::none;
}; };
#if defined(__cpp_nontype_template_parameter_class) #if defined(__cpp_nontype_template_parameter_class) \
&& (/* See #5201 */ !defined(__GNUC__) \
|| (__GNUC__ > 10 || (__GNUC__ == 10 && __GNUC_MINOR__ >= 3)))
# define PYBIND11_TYPING_H_HAS_STRING_LITERAL
template <size_t N> template <size_t N>
struct StringLiteral { struct StringLiteral {
constexpr StringLiteral(const char (&str)[N]) { std::copy_n(str, N, name); } constexpr StringLiteral(const char (&str)[N]) { std::copy_n(str, N, name); }
@ -222,7 +227,7 @@ struct handle_type_name<typing::Never> {
static constexpr auto name = const_name("Never"); static constexpr auto name = const_name("Never");
}; };
#if defined(__cpp_nontype_template_parameter_class) #if defined(PYBIND11_TYPING_H_HAS_STRING_LITERAL)
template <typing::StringLiteral... Literals> template <typing::StringLiteral... Literals>
struct handle_type_name<typing::Literal<Literals...>> { struct handle_type_name<typing::Literal<Literals...>> {
static constexpr auto name = const_name("Literal[") static constexpr auto name = const_name("Literal[")

View File

@ -2,12 +2,35 @@
from __future__ import annotations 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 = [
@ -22,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:
@ -54,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

@ -8,5 +8,5 @@ def _to_int(s: str) -> int | str:
return s return s
__version__ = "2.13.1" __version__ = "2.13.4"
version_info = tuple(_to_int(s) for s in __version__.split(".")) version_info = tuple(_to_int(s) for s in __version__.split("."))

View File

@ -88,7 +88,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)
@ -153,6 +158,7 @@ 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
@ -489,6 +495,9 @@ 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

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

@ -58,6 +58,7 @@ detail_headers = {
"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",
} }
eigen_headers = { eigen_headers = {

21
tests/pyproject.toml Normal file
View File

@ -0,0 +1,21 @@
# 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]
# Hide a warning while we also support CMake < 3.15
cmake.version = ">=3.15"
[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,10 +1,15 @@
from __future__ import annotations 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():

View File

@ -368,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

@ -1,5 +1,6 @@
from __future__ import annotations from __future__ import annotations
import sys
import time import time
from threading import Thread from threading import Thread
@ -153,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:
@ -176,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

@ -147,33 +147,39 @@ void init_tensor_module(pybind11::module &m) {
m.def( m.def(
"take_fixed_tensor", "take_fixed_tensor",
[]() { []() {
Eigen::aligned_allocator< Eigen::aligned_allocator<
Eigen::TensorFixedSize<double, Eigen::Sizes<3, 5, 2>, Options>> Eigen::TensorFixedSize<double, Eigen::Sizes<3, 5, 2>, Options>>
allocator; allocator;
return new (allocator.allocate(1)) static auto *obj = new (allocator.allocate(1))
Eigen::TensorFixedSize<double, Eigen::Sizes<3, 5, 2>, Options>( Eigen::TensorFixedSize<double, Eigen::Sizes<3, 5, 2>, Options>(
get_fixed_tensor<Options>()); get_fixed_tensor<Options>());
return obj; // take_ownership will fail.
}, },
py::return_value_policy::take_ownership); py::return_value_policy::take_ownership);
m.def( m.def(
"take_tensor", "take_tensor",
[]() { return new Eigen::Tensor<double, 3, Options>(get_tensor<Options>()); }, []() {
static auto *obj = new Eigen::Tensor<double, 3, Options>(get_tensor<Options>());
return obj; // take_ownership will fail.
},
py::return_value_policy::take_ownership); py::return_value_policy::take_ownership);
m.def( m.def(
"take_const_tensor", "take_const_tensor",
[]() -> const Eigen::Tensor<double, 3, Options> * { []() -> const Eigen::Tensor<double, 3, Options> * {
return new Eigen::Tensor<double, 3, Options>(get_tensor<Options>()); static auto *obj = new Eigen::Tensor<double, 3, Options>(get_tensor<Options>());
return obj; // take_ownership will fail.
}, },
py::return_value_policy::take_ownership); py::return_value_policy::take_ownership);
m.def( m.def(
"take_view_tensor", "take_view_tensor",
[]() -> const Eigen::TensorMap<Eigen::Tensor<double, 3, Options>> * { []() -> const Eigen::TensorMap<Eigen::Tensor<double, 3, Options>> * {
return new Eigen::TensorMap<Eigen::Tensor<double, 3, Options>>(get_tensor<Options>()); static auto *obj
= new Eigen::TensorMap<Eigen::Tensor<double, 3, Options>>(get_tensor<Options>());
return obj; // take_ownership will fail.
}, },
py::return_value_policy::take_ownership); py::return_value_policy::take_ownership);

View File

@ -75,7 +75,7 @@ def test_cross_module_exceptions(msg):
# TODO: FIXME # TODO: FIXME
@pytest.mark.xfail( @pytest.mark.xfail(
"env.MACOS and (env.PYPY or pybind11_tests.compiler_info.startswith('Homebrew Clang'))", "env.MACOS and (env.PYPY or pybind11_tests.compiler_info.startswith('Homebrew Clang')) or sys.platform.startswith('emscripten')",
raises=RuntimeError, raises=RuntimeError,
reason="See Issue #2847, PR #2999, PR #4324", reason="See Issue #2847, PR #2999, PR #4324",
) )

View File

@ -71,24 +71,28 @@ def test_cross_module_gil_inner_pybind11_acquired():
m.test_cross_module_gil_inner_pybind11_acquired() m.test_cross_module_gil_inner_pybind11_acquired()
@pytest.mark.skipif(sys.platform.startswith("emscripten"), reason="Requires threads")
def test_cross_module_gil_nested_custom_released(): def test_cross_module_gil_nested_custom_released():
"""Makes sure that the GIL can be nested acquired/released by another module """Makes sure that the GIL can be nested acquired/released by another module
from a GIL-released state using custom locking logic.""" from a GIL-released state using custom locking logic."""
m.test_cross_module_gil_nested_custom_released() m.test_cross_module_gil_nested_custom_released()
@pytest.mark.skipif(sys.platform.startswith("emscripten"), reason="Requires threads")
def test_cross_module_gil_nested_custom_acquired(): def test_cross_module_gil_nested_custom_acquired():
"""Makes sure that the GIL can be nested acquired/acquired by another module """Makes sure that the GIL can be nested acquired/acquired by another module
from a GIL-acquired state using custom locking logic.""" from a GIL-acquired state using custom locking logic."""
m.test_cross_module_gil_nested_custom_acquired() m.test_cross_module_gil_nested_custom_acquired()
@pytest.mark.skipif(sys.platform.startswith("emscripten"), reason="Requires threads")
def test_cross_module_gil_nested_pybind11_released(): def test_cross_module_gil_nested_pybind11_released():
"""Makes sure that the GIL can be nested acquired/released by another module """Makes sure that the GIL can be nested acquired/released by another module
from a GIL-released state using pybind11 locking logic.""" from a GIL-released state using pybind11 locking logic."""
m.test_cross_module_gil_nested_pybind11_released() m.test_cross_module_gil_nested_pybind11_released()
@pytest.mark.skipif(sys.platform.startswith("emscripten"), reason="Requires threads")
def test_cross_module_gil_nested_pybind11_acquired(): def test_cross_module_gil_nested_pybind11_acquired():
"""Makes sure that the GIL can be nested acquired/acquired by another module """Makes sure that the GIL can be nested acquired/acquired by another module
from a GIL-acquired state using pybind11 locking logic.""" from a GIL-acquired state using pybind11 locking logic."""
@ -103,6 +107,7 @@ def test_nested_acquire():
assert m.test_nested_acquire(0xAB) == "171" assert m.test_nested_acquire(0xAB) == "171"
@pytest.mark.skipif(sys.platform.startswith("emscripten"), reason="Requires threads")
def test_multi_acquire_release_cross_module(): def test_multi_acquire_release_cross_module():
for bits in range(16 * 8): for bits in range(16 * 8):
internals_ids = m.test_multi_acquire_release_cross_module(bits) internals_ids = m.test_multi_acquire_release_cross_module(bits)
@ -204,7 +209,7 @@ def _run_in_threads(test_fn, num_threads, parallel):
thread.join() thread.join()
# TODO: FIXME, sometimes returns -11 (segfault) instead of 0 on macOS Python 3.9 @pytest.mark.skipif(sys.platform.startswith("emscripten"), reason="Requires threads")
@pytest.mark.parametrize("test_fn", ALL_BASIC_TESTS_PLUS_INTENTIONAL_DEADLOCK) @pytest.mark.parametrize("test_fn", ALL_BASIC_TESTS_PLUS_INTENTIONAL_DEADLOCK)
def test_run_in_process_one_thread(test_fn): def test_run_in_process_one_thread(test_fn):
"""Makes sure there is no GIL deadlock when running in a thread. """Makes sure there is no GIL deadlock when running in a thread.
@ -214,7 +219,7 @@ def test_run_in_process_one_thread(test_fn):
assert _run_in_process(_run_in_threads, test_fn, num_threads=1, parallel=False) == 0 assert _run_in_process(_run_in_threads, test_fn, num_threads=1, parallel=False) == 0
# TODO: FIXME on macOS Python 3.9 @pytest.mark.skipif(sys.platform.startswith("emscripten"), reason="Requires threads")
@pytest.mark.parametrize("test_fn", ALL_BASIC_TESTS_PLUS_INTENTIONAL_DEADLOCK) @pytest.mark.parametrize("test_fn", ALL_BASIC_TESTS_PLUS_INTENTIONAL_DEADLOCK)
def test_run_in_process_multiple_threads_parallel(test_fn): def test_run_in_process_multiple_threads_parallel(test_fn):
"""Makes sure there is no GIL deadlock when running in a thread multiple times in parallel. """Makes sure there is no GIL deadlock when running in a thread multiple times in parallel.
@ -224,7 +229,7 @@ def test_run_in_process_multiple_threads_parallel(test_fn):
assert _run_in_process(_run_in_threads, test_fn, num_threads=8, parallel=True) == 0 assert _run_in_process(_run_in_threads, test_fn, num_threads=8, parallel=True) == 0
# TODO: FIXME on macOS Python 3.9 @pytest.mark.skipif(sys.platform.startswith("emscripten"), reason="Requires threads")
@pytest.mark.parametrize("test_fn", ALL_BASIC_TESTS_PLUS_INTENTIONAL_DEADLOCK) @pytest.mark.parametrize("test_fn", ALL_BASIC_TESTS_PLUS_INTENTIONAL_DEADLOCK)
def test_run_in_process_multiple_threads_sequential(test_fn): def test_run_in_process_multiple_threads_sequential(test_fn):
"""Makes sure there is no GIL deadlock when running in a thread multiple times sequentially. """Makes sure there is no GIL deadlock when running in a thread multiple times sequentially.
@ -234,7 +239,7 @@ def test_run_in_process_multiple_threads_sequential(test_fn):
assert _run_in_process(_run_in_threads, test_fn, num_threads=8, parallel=False) == 0 assert _run_in_process(_run_in_threads, test_fn, num_threads=8, parallel=False) == 0
# TODO: FIXME on macOS Python 3.9 @pytest.mark.skipif(sys.platform.startswith("emscripten"), reason="Requires threads")
@pytest.mark.parametrize("test_fn", ALL_BASIC_TESTS_PLUS_INTENTIONAL_DEADLOCK) @pytest.mark.parametrize("test_fn", ALL_BASIC_TESTS_PLUS_INTENTIONAL_DEADLOCK)
def test_run_in_process_direct(test_fn): def test_run_in_process_direct(test_fn):
"""Makes sure there is no GIL deadlock when using processes. """Makes sure there is no GIL deadlock when using processes.

View File

@ -1,8 +1,11 @@
from __future__ import annotations from __future__ import annotations
import sys
from contextlib import redirect_stderr, redirect_stdout from contextlib import redirect_stderr, redirect_stdout
from io import StringIO from io import StringIO
import pytest
from pybind11_tests import iostream as m from pybind11_tests import iostream as m
@ -270,6 +273,7 @@ def test_redirect_both(capfd):
assert stream2.getvalue() == msg2 assert stream2.getvalue() == msg2
@pytest.mark.skipif(sys.platform.startswith("emscripten"), reason="Requires threads")
def test_threading(): def test_threading():
with m.ostream_redirect(stdout=True, stderr=False): with m.ostream_redirect(stdout=True, stderr=False):
# start some threads # start some threads

View File

@ -90,32 +90,32 @@ TEST_SUBMODULE(modules, m) {
try { try {
py::class_<Dupe1>(dm, "Dupe1"); py::class_<Dupe1>(dm, "Dupe1");
failures.append("Dupe1 class"); failures.append("Dupe1 class");
} catch (std::runtime_error &) { } catch (std::runtime_error &) { // NOLINT(bugprone-empty-catch)
} }
try { try {
dm.def("Dupe1", []() { return Dupe1(); }); dm.def("Dupe1", []() { return Dupe1(); });
failures.append("Dupe1 function"); failures.append("Dupe1 function");
} catch (std::runtime_error &) { } catch (std::runtime_error &) { // NOLINT(bugprone-empty-catch)
} }
try { try {
py::class_<Dupe3>(dm, "dupe1_factory"); py::class_<Dupe3>(dm, "dupe1_factory");
failures.append("dupe1_factory"); failures.append("dupe1_factory");
} catch (std::runtime_error &) { } catch (std::runtime_error &) { // NOLINT(bugprone-empty-catch)
} }
try { try {
py::exception<Dupe3>(dm, "Dupe2"); py::exception<Dupe3>(dm, "Dupe2");
failures.append("Dupe2"); failures.append("Dupe2");
} catch (std::runtime_error &) { } catch (std::runtime_error &) { // NOLINT(bugprone-empty-catch)
} }
try { try {
dm.def("DupeException", []() { return 30; }); dm.def("DupeException", []() { return 30; });
failures.append("DupeException1"); failures.append("DupeException1");
} catch (std::runtime_error &) { } catch (std::runtime_error &) { // NOLINT(bugprone-empty-catch)
} }
try { try {
py::class_<DupeException>(dm, "DupeException"); py::class_<DupeException>(dm, "DupeException");
failures.append("DupeException2"); failures.append("DupeException2");
} catch (std::runtime_error &) { } catch (std::runtime_error &) { // NOLINT(bugprone-empty-catch)
} }
return failures; return failures;

View File

@ -266,6 +266,8 @@ py::array_t<int32_t, 0> test_array_ctors(int i) {
return fill(arr_t(buf_ndim1_null)); return fill(arr_t(buf_ndim1_null));
case 44: case 44:
return fill(py::array(buf_ndim1_null)); return fill(py::array(buf_ndim1_null));
default:
break;
} }
return arr_t(); return arr_t();
} }

View File

@ -65,7 +65,7 @@ TEST_SUBMODULE(opaque_types, m) {
m.def("return_unique_ptr", []() -> std::unique_ptr<StringList> { m.def("return_unique_ptr", []() -> std::unique_ptr<StringList> {
auto *result = new StringList(); auto *result = new StringList();
result->push_back("some value"); result->emplace_back("some value");
return std::unique_ptr<StringList>(result); return std::unique_ptr<StringList>(result);
}); });

View File

@ -109,7 +109,7 @@ void m_defs(py::module_ &m) {
} // namespace handle_from_move_only_type_with_operator_PyObject } // namespace handle_from_move_only_type_with_operator_PyObject
#if defined(__cpp_nontype_template_parameter_class) #if defined(PYBIND11_TYPING_H_HAS_STRING_LITERAL)
namespace literals { namespace literals {
enum Color { RED = 0, BLUE = 1 }; enum Color { RED = 0, BLUE = 1 };
@ -905,7 +905,7 @@ TEST_SUBMODULE(pytypes, m) {
m.def("annotate_optional_to_object", m.def("annotate_optional_to_object",
[](py::typing::Optional<int> &o) -> py::object { return o; }); [](py::typing::Optional<int> &o) -> py::object { return o; });
#if defined(__cpp_nontype_template_parameter_class) #if defined(PYBIND11_TYPING_H_HAS_STRING_LITERAL)
py::enum_<literals::Color>(m, "Color") py::enum_<literals::Color>(m, "Color")
.value("RED", literals::Color::RED) .value("RED", literals::Color::RED)
.value("BLUE", literals::Color::BLUE); .value("BLUE", literals::Color::BLUE);
@ -919,8 +919,8 @@ TEST_SUBMODULE(pytypes, m) {
m.def("annotate_listT_to_T", m.def("annotate_listT_to_T",
[](const py::typing::List<typevar::TypeVarT> &l) -> typevar::TypeVarT { return l[0]; }); [](const py::typing::List<typevar::TypeVarT> &l) -> typevar::TypeVarT { return l[0]; });
m.def("annotate_object_to_T", [](const py::object &o) -> typevar::TypeVarT { return o; }); m.def("annotate_object_to_T", [](const py::object &o) -> typevar::TypeVarT { return o; });
m.attr("if_defined__cpp_nontype_template_parameter_class") = true; m.attr("defined_PYBIND11_TYPING_H_HAS_STRING_LITERAL") = true;
#else #else
m.attr("if_defined__cpp_nontype_template_parameter_class") = false; m.attr("defined_PYBIND11_TYPING_H_HAS_STRING_LITERAL") = false;
#endif #endif
} }

View File

@ -1025,7 +1025,7 @@ def test_optional_object_annotations(doc):
@pytest.mark.skipif( @pytest.mark.skipif(
not m.if_defined__cpp_nontype_template_parameter_class, not m.defined_PYBIND11_TYPING_H_HAS_STRING_LITERAL,
reason="C++20 feature not available.", reason="C++20 feature not available.",
) )
def test_literal(doc): def test_literal(doc):
@ -1036,7 +1036,7 @@ def test_literal(doc):
@pytest.mark.skipif( @pytest.mark.skipif(
not m.if_defined__cpp_nontype_template_parameter_class, not m.defined_PYBIND11_TYPING_H_HAS_STRING_LITERAL,
reason="C++20 feature not available.", reason="C++20 feature not available.",
) )
def test_typevar(doc): def test_typevar(doc):

View File

@ -74,6 +74,7 @@ std::vector<std::unique_ptr<Animal>> create_zoo() {
// simulate some new type of Dog that the Python bindings // simulate some new type of Dog that the Python bindings
// haven't been updated for; it should still be considered // haven't been updated for; it should still be considered
// a Dog, not just an Animal. // a Dog, not just an Animal.
// NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange)
ret.emplace_back(new Dog("Ginger", Dog::Kind(150))); ret.emplace_back(new Dog("Ginger", Dog::Kind(150)));
ret.emplace_back(new Chihuahua("Hertzl")); ret.emplace_back(new Chihuahua("Hertzl"));

View File

@ -1,7 +1,10 @@
from __future__ import annotations from __future__ import annotations
import sys
import threading import threading
import pytest
from pybind11_tests import thread as m from pybind11_tests import thread as m
@ -24,6 +27,7 @@ class Thread(threading.Thread):
raise self.e raise self.e
@pytest.mark.skipif(sys.platform.startswith("emscripten"), reason="Requires threads")
def test_implicit_conversion(): def test_implicit_conversion():
a = Thread(m.test) a = Thread(m.test)
b = Thread(m.test) b = Thread(m.test)
@ -34,6 +38,7 @@ def test_implicit_conversion():
x.join() x.join()
@pytest.mark.skipif(sys.platform.startswith("emscripten"), reason="Requires threads")
def test_implicit_conversion_no_gil(): def test_implicit_conversion_no_gil():
a = Thread(m.test_no_gil) a = Thread(m.test_no_gil)
b = Thread(m.test_no_gil) b = Thread(m.test_no_gil)

View File

@ -0,0 +1,46 @@
#include <pybind11/functional.h>
#include <pybind11/pybind11.h>
#include "pybind11_tests.h"
namespace py = pybind11;
namespace {
struct SpecialReturn {
int value = 99;
};
} // namespace
namespace pybind11 {
namespace detail {
namespace type_caster_std_function_specializations {
template <typename... Args>
struct func_wrapper<SpecialReturn, Args...> : func_wrapper_base {
using func_wrapper_base::func_wrapper_base;
SpecialReturn operator()(Args... args) const {
gil_scoped_acquire acq;
SpecialReturn result;
try {
result = hfunc.f(std::forward<Args>(args)...).template cast<SpecialReturn>();
} catch (error_already_set &) {
result.value += 1;
}
result.value += 100;
return result;
}
};
} // namespace type_caster_std_function_specializations
} // namespace detail
} // namespace pybind11
TEST_SUBMODULE(type_caster_std_function_specializations, m) {
py::class_<SpecialReturn>(m, "SpecialReturn")
.def(py::init<>())
.def_readwrite("value", &SpecialReturn::value);
m.def("call_callback_with_special_return",
[](const std::function<SpecialReturn()> &func) { return func(); });
}

View File

@ -0,0 +1,15 @@
from __future__ import annotations
from pybind11_tests import type_caster_std_function_specializations as m
def test_callback_with_special_return():
def return_special():
return m.SpecialReturn()
def raise_exception():
raise ValueError("called raise_exception.")
assert return_special().value == 99
assert m.call_callback_with_special_return(return_special).value == 199
assert m.call_callback_with_special_return(raise_exception).value == 200

View File

@ -1,5 +1,7 @@
from __future__ import annotations from __future__ import annotations
import sys
import pytest import pytest
import env # noqa: F401 import env # noqa: F401
@ -435,6 +437,7 @@ def test_inherited_virtuals():
assert obj.say_everything() == "BT -7" assert obj.say_everything() == "BT -7"
@pytest.mark.skipif(sys.platform.startswith("emscripten"), reason="Requires threads")
def test_issue_1454(): def test_issue_1454():
# Fix issue #1454 (crash when acquiring/releasing GIL on another thread in Python 2.7) # Fix issue #1454 (crash when acquiring/releasing GIL on another thread in Python 2.7)
m.test_gil() m.test_gil()

View File

@ -2,7 +2,7 @@
Adds the following targets:: Adds the following targets::
pybind11::pybind11 - link to headers and pybind11 pybind11::pybind11 - link to Python headers and pybind11::headers
pybind11::module - Adds module links pybind11::module - Adds module links
pybind11::embed - Adds embed links pybind11::embed - Adds embed links
pybind11::lto - Link time optimizations (only if CMAKE_INTERPROCEDURAL_OPTIMIZATION is not set) pybind11::lto - Link time optimizations (only if CMAKE_INTERPROCEDURAL_OPTIMIZATION is not set)
@ -75,6 +75,32 @@ set_property(
APPEND APPEND
PROPERTY INTERFACE_LINK_LIBRARIES pybind11::pybind11) PROPERTY INTERFACE_LINK_LIBRARIES pybind11::pybind11)
# -------------- emscripten requires exceptions enabled -------------
# _pybind11_no_exceptions is a private mechanism to disable this addition.
# Please open an issue if you need to use it; it will be removed if no one
# needs it.
if(CMAKE_SYSTEM_NAME MATCHES Emscripten AND NOT _pybind11_no_exceptions)
if(CMAKE_VERSION VERSION_LESS 3.13)
message(WARNING "CMake 3.13+ is required to build for Emscripten. Some flags will be missing")
else()
if(is_config)
set(_tmp_config_target pybind11::pybind11_headers)
else()
set(_tmp_config_target pybind11_headers)
endif()
set_property(
TARGET ${_tmp_config_target}
APPEND
PROPERTY INTERFACE_LINK_OPTIONS -fexceptions)
set_property(
TARGET ${_tmp_config_target}
APPEND
PROPERTY INTERFACE_COMPILE_OPTIONS -fexceptions)
unset(_tmp_config_target)
endif()
endif()
# --------------------------- link helper --------------------------- # --------------------------- link helper ---------------------------
add_library(pybind11::python_link_helper IMPORTED INTERFACE ${optional_global}) add_library(pybind11::python_link_helper IMPORTED INTERFACE ${optional_global})
@ -329,13 +355,13 @@ function(_pybind11_generate_lto target prefer_thin_lto)
if(CMAKE_SYSTEM_PROCESSOR MATCHES "ppc64le" OR CMAKE_SYSTEM_PROCESSOR MATCHES "mips64") if(CMAKE_SYSTEM_PROCESSOR MATCHES "ppc64le" OR CMAKE_SYSTEM_PROCESSOR MATCHES "mips64")
# Do nothing # Do nothing
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES emscripten) elseif(CMAKE_SYSTEM_NAME MATCHES Emscripten)
# This compile is very costly when cross-compiling, so set this without checking # This compile is very costly when cross-compiling, so set this without checking
set(PYBIND11_LTO_CXX_FLAGS "-flto${thin}${cxx_append}") set(PYBIND11_LTO_CXX_FLAGS "-flto${thin}${cxx_append}")
set(PYBIND11_LTO_LINKER_FLAGS "-flto${thin}${linker_append}") set(PYBIND11_LTO_LINKER_FLAGS "-flto${thin}${linker_append}")
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
_pybind11_return_if_cxx_and_linker_flags_work( _pybind11_return_if_cxx_and_linker_flags_work(
HAS_FLTO_THIN "-flto${thin}${cxx_append}" "-flto=${thin}${linker_append}" HAS_FLTO_THIN "-flto${thin}${cxx_append}" "-flto${thin}${linker_append}"
PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS) PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS)
endif() endif()
if(NOT HAS_FLTO_THIN) if(NOT HAS_FLTO_THIN)

View File

@ -84,7 +84,7 @@ you can either use the basic targets, or use the FindPython tools:
# Python method: # Python method:
Python_add_library(MyModule2 src2.cpp) Python_add_library(MyModule2 src2.cpp)
target_link_libraries(MyModule2 pybind11::headers) target_link_libraries(MyModule2 PUBLIC pybind11::headers)
set_target_properties(MyModule2 PROPERTIES set_target_properties(MyModule2 PROPERTIES
INTERPROCEDURAL_OPTIMIZATION ON INTERPROCEDURAL_OPTIMIZATION ON
CXX_VISIBILITY_PRESET ON CXX_VISIBILITY_PRESET ON