Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ jobs:
python-version: '3.10'
cmake-args: -DPYBIND11_TEST_SMART_HOLDER=ON -DCMAKE_CXX_FLAGS="/GR /EHsc"
- runs-on: windows-2022
python-version: '3.13'
python-version: '3.13.3'
cmake-args: -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDebugDLL
- runs-on: windows-latest
python-version: '3.13t'
Expand Down Expand Up @@ -850,7 +850,7 @@ jobs:
args: -DCMAKE_CXX_STANDARD=17
- python: '3.10'
args: -DCMAKE_CXX_STANDARD=20
- python: '3.13'
- python: '3.13.3'


name: "🐍 ${{ matrix.python }} • MSVC 2022 • x86 ${{ matrix.args }}"
Expand Down
22 changes: 22 additions & 0 deletions docs/advanced/exceptions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,28 @@ Alternately, to ignore the error, call `PyErr_Clear
Any Python error must be thrown or cleared, or Python/pybind11 will be left in
an invalid state.

Handling warnings from the Python C API
=======================================

Wrappers for handling Python warnings are provided in ``pybind11/warnings.h``.
This header must be included explicitly; it is not transitively included via
``pybind11/pybind11.h``.

Warnings can be raised with the ``warn`` function:

.. code-block:: cpp

py::warnings::warn("This is a warning!", PyExc_Warning);

// Optionally, a `stack_level` can be specified.
py::warnings::warn("Another one!", PyExc_DeprecationWarning, 3);

New warning types can be registered at the module level using ``new_warning_type``:

.. code-block:: cpp

py::warnings::new_warning_type(m, "CustomWarning", PyExc_RuntimeWarning);

Chaining exceptions ('raise from')
==================================

Expand Down
40 changes: 40 additions & 0 deletions docs/advanced/pycpp/numpy.rst
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,46 @@ prevent many types of unsupported structures, it is still the user's
responsibility to use only "plain" structures that can be safely manipulated as
raw memory without violating invariants.

Scalar types
============

In some cases we may want to accept or return NumPy scalar values such as
``np.float32`` or ``np.float64``. We hope to be able to handle single-precision
and double-precision on the C-side. However, both are bound to Python's
double-precision builtin float by default, so they cannot be processed separately.
We used the ``py::buffer`` trick to implement the previous approach, which
will cause the readability of the code to drop significantly.

Luckily, there's a helper type for this occasion - ``py::numpy_scalar``:

.. code-block:: cpp

m.def("add", [](py::numpy_scalar<float> a, py::numpy_scalar<float> b) {
return py::make_scalar(a + b);
});
m.def("add", [](py::numpy_scalar<double> a, py::numpy_scalar<double> b) {
return py::make_scalar(a + b);
});

This type is trivially convertible to and from the type it wraps; currently
supported scalar types are NumPy arithmetic types: ``bool_``, ``int8``,
``int16``, ``int32``, ``int64``, ``uint8``, ``uint16``, ``uint32``,
``uint64``, ``float32``, ``float64``, ``complex64``, ``complex128``, all of
them mapping to respective C++ counterparts.

.. note::

``py::numpy_scalar<T>`` strictly matches NumPy scalar types. For example,
``py::numpy_scalar<int64_t>`` will accept ``np.int64(123)``,
but **not** a regular Python ``int`` like ``123``.

.. note::

Native C types are mapped to NumPy types in a platform specific way: for
instance, ``char`` may be mapped to either ``np.int8`` or ``np.uint8``
and ``long`` may use 4 or 8 bytes depending on the platform. Unless you
clearly understand the difference and your needs, please use ``<cstdint>``.

Vectorizing functions
=====================

Expand Down
7 changes: 7 additions & 0 deletions include/pybind11/attr.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include "detail/common.h"
#include "cast.h"
#include "trampoline_self_life_support.h"

#include <functional>

Expand Down Expand Up @@ -312,6 +313,12 @@ struct type_record {
/// Function pointer to class_<..>::dealloc
void (*dealloc)(detail::value_and_holder &) = nullptr;

/// Function pointer for casting alias class (aka trampoline) pointer to
/// trampoline_self_life_support pointer. Sidesteps cross-DSO RTTI issues
/// on platforms like macOS (see PR #5728 for details).
get_trampoline_self_life_support_fn get_trampoline_self_life_support
= [](void *) -> trampoline_self_life_support * { return nullptr; };

/// List of base classes of the newly created type
list bases;

Expand Down
14 changes: 8 additions & 6 deletions include/pybind11/cast.h
Original file line number Diff line number Diff line change
Expand Up @@ -980,7 +980,7 @@ struct copyable_holder_caster<

explicit operator std::shared_ptr<type> &() {
if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) {
shared_ptr_storage = sh_load_helper.load_as_shared_ptr(value);
shared_ptr_storage = sh_load_helper.load_as_shared_ptr(typeinfo, value);
}
return shared_ptr_storage;
}
Expand All @@ -989,7 +989,8 @@ struct copyable_holder_caster<
if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) {
// Reusing shared_ptr code to minimize code complexity.
shared_ptr_storage
= sh_load_helper.load_as_shared_ptr(value,
= sh_load_helper.load_as_shared_ptr(typeinfo,
value,
/*responsible_parent=*/nullptr,
/*force_potentially_slicing_shared_ptr=*/true);
}
Expand Down Expand Up @@ -1019,7 +1020,8 @@ struct copyable_holder_caster<
copyable_holder_caster loader;
loader.load(responsible_parent, /*convert=*/false);
assert(loader.typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder);
return loader.sh_load_helper.load_as_shared_ptr(loader.value, responsible_parent);
return loader.sh_load_helper.load_as_shared_ptr(
loader.typeinfo, loader.value, responsible_parent);
}

protected:
Expand Down Expand Up @@ -1240,20 +1242,20 @@ struct move_only_holder_caster<

explicit operator std::unique_ptr<type, deleter>() {
if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) {
return sh_load_helper.template load_as_unique_ptr<deleter>(value);
return sh_load_helper.template load_as_unique_ptr<deleter>(typeinfo, value);
}
pybind11_fail("Expected to be UNREACHABLE: " __FILE__ ":" PYBIND11_TOSTRING(__LINE__));
}

explicit operator const std::unique_ptr<type, deleter> &() {
if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) {
// Get shared_ptr to ensure that the Python object is not disowned elsewhere.
shared_ptr_storage = sh_load_helper.load_as_shared_ptr(value);
shared_ptr_storage = sh_load_helper.load_as_shared_ptr(typeinfo, value);
// Build a temporary unique_ptr that is meant to never expire.
unique_ptr_storage = std::shared_ptr<std::unique_ptr<type, deleter>>(
new std::unique_ptr<type, deleter>{
sh_load_helper.template load_as_const_unique_ptr<deleter>(
shared_ptr_storage.get())},
typeinfo, shared_ptr_storage.get())},
[](std::unique_ptr<type, deleter> *ptr) {
if (!ptr) {
pybind11_fail("FATAL: `const std::unique_ptr<T, D> &` was disowned "
Expand Down
28 changes: 23 additions & 5 deletions include/pybind11/detail/init.h
Original file line number Diff line number Diff line change
Expand Up @@ -246,20 +246,38 @@ void construct(value_and_holder &v_h,
v_h.type->init_instance(v_h.inst, &smhldr);
}

template <typename Class, detail::enable_if_t<is_smart_holder<Holder<Class>>::value, int> = 0>
void construct(value_and_holder &v_h, std::shared_ptr<Cpp<Class>> &&shd_ptr, bool need_alias) {
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias);
template <typename PtrType, typename Class>
void construct_from_shared_ptr(value_and_holder &v_h,
std::shared_ptr<PtrType> &&shd_ptr,
bool need_alias) {
static_assert(std::is_same<PtrType, Cpp<Class>>::value
|| std::is_same<PtrType, const Cpp<Class>>::value,
"Expected (const) Cpp<Class> as shared_ptr pointee");
auto *ptr = shd_ptr.get();
no_nullptr(ptr);
if (Class::has_alias && need_alias && !is_alias<Class>(ptr)) {
throw type_error("pybind11::init(): construction failed: returned std::shared_ptr pointee "
"is not an alias instance");
}
auto smhldr = smart_holder::from_shared_ptr(shd_ptr);
v_h.value_ptr() = ptr;
// Cast to non-const if needed, consistent with internal design
auto smhldr
= smart_holder::from_shared_ptr(std::const_pointer_cast<Cpp<Class>>(std::move(shd_ptr)));
v_h.value_ptr() = const_cast<Cpp<Class> *>(ptr);
v_h.type->init_instance(v_h.inst, &smhldr);
}

template <typename Class, detail::enable_if_t<is_smart_holder<Holder<Class>>::value, int> = 0>
void construct(value_and_holder &v_h, std::shared_ptr<Cpp<Class>> &&shd_ptr, bool need_alias) {
construct_from_shared_ptr<Cpp<Class>, Class>(v_h, std::move(shd_ptr), need_alias);
}

template <typename Class, detail::enable_if_t<is_smart_holder<Holder<Class>>::value, int> = 0>
void construct(value_and_holder &v_h,
std::shared_ptr<const Cpp<Class>> &&shd_ptr,
bool need_alias) {
construct_from_shared_ptr<const Cpp<Class>, Class>(v_h, std::move(shd_ptr), need_alias);
}

template <typename Class, detail::enable_if_t<is_smart_holder<Holder<Class>>::value, int> = 0>
void construct(value_and_holder &v_h,
std::shared_ptr<Alias<Class>> &&shd_ptr,
Expand Down
14 changes: 11 additions & 3 deletions include/pybind11/detail/internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
#include <pybind11/conduit/pybind11_platform_abi_id.h>
#include <pybind11/gil_simple.h>
#include <pybind11/pytypes.h>
#include <pybind11/trampoline_self_life_support.h>

#include "common.h"
#include "struct_smart_holder.h"

#include <atomic>
#include <exception>
Expand All @@ -35,11 +37,11 @@
/// further ABI-incompatible changes may be made before the ABI is officially
/// changed to the new version.
#ifndef PYBIND11_INTERNALS_VERSION
# define PYBIND11_INTERNALS_VERSION 10
# define PYBIND11_INTERNALS_VERSION 11
#endif

#if PYBIND11_INTERNALS_VERSION < 10
# error "PYBIND11_INTERNALS_VERSION 10 is the minimum for all platforms for pybind11v3."
#if PYBIND11_INTERNALS_VERSION < 11
# error "PYBIND11_INTERNALS_VERSION 11 is the minimum for all platforms for pybind11v3."
#endif

PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
Expand Down Expand Up @@ -308,6 +310,12 @@ struct type_info {
void *(*operator_new)(size_t);
void (*init_instance)(instance *, const void *);
void (*dealloc)(value_and_holder &v_h);

// Cross-DSO-safe function pointers, to sidestep cross-DSO RTTI issues
// on platforms like macOS (see PR #5728 for details):
memory::get_guarded_delete_fn get_memory_guarded_delete = memory::get_guarded_delete;
get_trampoline_self_life_support_fn get_trampoline_self_life_support = nullptr;

std::vector<PyObject *(*) (PyObject *, PyTypeObject *)> implicit_conversions;
std::vector<std::pair<const std::type_info *, void *(*) (void *)>> implicit_casts;
std::vector<bool (*)(PyObject *, void *&)> *direct_conversions;
Expand Down
Loading
Loading