mirror of https://github.com/pybind/pybind11.git
Compare commits
12 Commits
7da2bd2b7f
...
46d861b6f6
Author | SHA1 | Date |
---|---|---|
Francesco Rizzi | 46d861b6f6 | |
Hintay | 1f8b4a7f1a | |
dependabot[bot] | ad9fd39e14 | |
vfdev | 1d9483ff73 | |
Francesco Rizzi | 8917a1e9b3 | |
Francesco Rizzi | d2ea386ef7 | |
Francesco Rizzi | 136c664b5a | |
Francesco Rizzi | 17912ba667 | |
Francesco Rizzi | f52a31a004 | |
Francesco Rizzi | 87fd2c5121 | |
Francesco Rizzi | cba2b32ead | |
Francesco Rizzi | 4d785be984 |
|
@ -22,7 +22,7 @@ jobs:
|
|||
submodules: true
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: pypa/cibuildwheel@v2.20
|
||||
- uses: pypa/cibuildwheel@v2.21
|
||||
env:
|
||||
PYODIDE_BUILD_EXPORTS: whole_archive
|
||||
with:
|
||||
|
|
|
@ -50,12 +50,12 @@ inline void try_translate_exceptions() {
|
|||
- delegate translation to the next translator by throwing a new type of exception.
|
||||
*/
|
||||
|
||||
bool handled = with_internals([&](internals &internals) {
|
||||
auto &local_exception_translators = get_local_internals().registered_exception_translators;
|
||||
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;
|
||||
}
|
||||
auto &exception_translators = internals.registered_exception_translators;
|
||||
if (detail::apply_exception_translators(exception_translators)) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -39,7 +39,11 @@
|
|||
# if PY_VERSION_HEX >= 0x030C0000 || defined(_MSC_VER)
|
||||
// Version bump for Python 3.12+, before first 3.12 beta release.
|
||||
// Version bump for MSVC piggy-backed on PR #4779. See comments there.
|
||||
# ifdef Py_GIL_DISABLED
|
||||
# define PYBIND11_INTERNALS_VERSION 6
|
||||
# else
|
||||
# define PYBIND11_INTERNALS_VERSION 5
|
||||
# endif
|
||||
# else
|
||||
# define PYBIND11_INTERNALS_VERSION 4
|
||||
# endif
|
||||
|
@ -177,6 +181,7 @@ static_assert(sizeof(instance_map_shard) % 64 == 0,
|
|||
struct internals {
|
||||
#ifdef Py_GIL_DISABLED
|
||||
pymutex mutex;
|
||||
pymutex exception_translator_mutex;
|
||||
#endif
|
||||
// std::type_index -> pybind11's type information
|
||||
type_map<type_info *> registered_types_cpp;
|
||||
|
@ -643,6 +648,19 @@ inline auto with_internals(const F &cb) -> decltype(cb(get_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.
|
||||
|
|
|
@ -1232,9 +1232,7 @@ public:
|
|||
// Reference to element at a given index
|
||||
template <typename... Ix>
|
||||
const T &at(Ix... index) const {
|
||||
if ((ssize_t) sizeof...(index) != ndim()) {
|
||||
fail_dim_check(sizeof...(index), "index dimension mismatch");
|
||||
}
|
||||
check_rank_precondition(sizeof...(index));
|
||||
return *(static_cast<const T *>(array::data())
|
||||
+ byte_offset(ssize_t(index)...) / itemsize());
|
||||
}
|
||||
|
@ -1242,13 +1240,33 @@ public:
|
|||
// Mutable reference to element at a given index
|
||||
template <typename... Ix>
|
||||
T &mutable_at(Ix... index) {
|
||||
if ((ssize_t) sizeof...(index) != ndim()) {
|
||||
fail_dim_check(sizeof...(index), "index dimension mismatch");
|
||||
}
|
||||
check_rank_precondition(sizeof...(index));
|
||||
return *(static_cast<T *>(array::mutable_data())
|
||||
+ byte_offset(ssize_t(index)...) / itemsize());
|
||||
}
|
||||
|
||||
// const-reference to element at a given index without bounds checking
|
||||
template <typename... Ix>
|
||||
const T &operator()(Ix... index) const {
|
||||
#if !defined(NDEBUG)
|
||||
check_rank_precondition(sizeof...(index));
|
||||
check_dimensions(index...);
|
||||
#endif
|
||||
return *(static_cast<const T *>(array::data())
|
||||
+ detail::byte_offset_unsafe(strides(), ssize_t(index)...) / itemsize());
|
||||
}
|
||||
|
||||
// mutable reference to element at a given index without bounds checking
|
||||
template <typename... Ix>
|
||||
T &operator()(Ix... index) {
|
||||
#if !defined(NDEBUG)
|
||||
check_rank_precondition(sizeof...(index));
|
||||
check_dimensions(index...);
|
||||
#endif
|
||||
return *(static_cast<T *>(array::mutable_data())
|
||||
+ detail::byte_offset_unsafe(strides(), ssize_t(index)...) / itemsize());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a proxy object that provides access to the array's data without bounds or
|
||||
* dimensionality checking. Will throw if the array is missing the `writeable` flag. Use with
|
||||
|
@ -1305,6 +1323,13 @@ protected:
|
|||
| ExtraFlags,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
private:
|
||||
void check_rank_precondition(ssize_t dim) const {
|
||||
if (dim != ndim()) {
|
||||
fail_dim_check(dim, "index dimension mismatch");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
|
|
|
@ -2586,9 +2586,11 @@ void implicitly_convertible() {
|
|||
}
|
||||
|
||||
inline void register_exception_translator(ExceptionTranslator &&translator) {
|
||||
detail::with_internals([&](detail::internals &internals) {
|
||||
internals.registered_exception_translators.push_front(
|
||||
std::forward<ExceptionTranslator>(translator));
|
||||
detail::with_exception_translators(
|
||||
[&](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));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -2599,10 +2601,11 @@ inline void register_exception_translator(ExceptionTranslator &&translator) {
|
|||
* the exception.
|
||||
*/
|
||||
inline void register_local_exception_translator(ExceptionTranslator &&translator) {
|
||||
detail::with_internals([&](detail::internals &internals) {
|
||||
(void) internals;
|
||||
detail::get_local_internals().registered_exception_translators.push_front(
|
||||
std::forward<ExceptionTranslator>(translator));
|
||||
detail::with_exception_translators(
|
||||
[&](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));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
|
@ -111,6 +111,16 @@ struct PythonAlreadySetInDestructor {
|
|||
py::str s;
|
||||
};
|
||||
|
||||
struct CustomData {
|
||||
explicit CustomData(const std::string &a) : a(a) {}
|
||||
std::string a;
|
||||
};
|
||||
|
||||
struct MyException7 {
|
||||
explicit MyException7(const CustomData &message) : message(message) {}
|
||||
CustomData message;
|
||||
};
|
||||
|
||||
TEST_SUBMODULE(exceptions, m) {
|
||||
m.def("throw_std_exception",
|
||||
[]() { throw std::runtime_error("This exception was intentionally thrown."); });
|
||||
|
@ -385,4 +395,33 @@ TEST_SUBMODULE(exceptions, m) {
|
|||
|
||||
// m.def("pass_exception_void", [](const py::exception<void>&) {}); // Does not compile.
|
||||
m.def("return_exception_void", []() { return py::exception<void>(); });
|
||||
|
||||
m.def("throws7", []() {
|
||||
auto data = CustomData("abc");
|
||||
throw MyException7(data);
|
||||
});
|
||||
|
||||
py::class_<CustomData>(m, "CustomData", py::module_local())
|
||||
.def(py::init<const std::string &>())
|
||||
.def_readwrite("a", &CustomData::a);
|
||||
|
||||
PYBIND11_CONSTINIT static py::gil_safe_call_once_and_store<py::object>
|
||||
PythonMyException7_storage;
|
||||
PythonMyException7_storage.call_once_and_store_result([&]() {
|
||||
auto mod = py::module_::import("custom_exceptions");
|
||||
py::object obj = mod.attr("PythonMyException7");
|
||||
return obj;
|
||||
});
|
||||
|
||||
py::register_local_exception_translator([](std::exception_ptr p) {
|
||||
try {
|
||||
if (p) {
|
||||
std::rethrow_exception(p);
|
||||
}
|
||||
} catch (const MyException7 &e) {
|
||||
auto exc_type = PythonMyException7_storage.get_stored();
|
||||
py::object exc_inst = exc_type(e.message);
|
||||
PyErr_SetObject(PyExc_Exception, exc_inst.ptr());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ from __future__ import annotations
|
|||
import sys
|
||||
|
||||
import pytest
|
||||
from custom_exceptions import PythonMyException7
|
||||
|
||||
import env
|
||||
import pybind11_cross_module_tests as cm
|
||||
|
@ -195,6 +196,10 @@ def test_custom(msg):
|
|||
raise RuntimeError("Exception error: caught child from parent") from err
|
||||
assert msg(excinfo.value) == "this is a helper-defined translated exception"
|
||||
|
||||
with pytest.raises(PythonMyException7) as excinfo:
|
||||
m.throws7()
|
||||
assert msg(excinfo.value) == "[PythonMyException7]: abc"
|
||||
|
||||
|
||||
def test_nested_throws(capture):
|
||||
"""Tests nested (e.g. C++ -> Python -> C++) exception handling"""
|
||||
|
|
|
@ -131,6 +131,15 @@ arr_t &mutate_at_t(arr_t &a, Ix... idx) {
|
|||
a.mutable_at(idx...)++;
|
||||
return a;
|
||||
}
|
||||
template <typename... Ix>
|
||||
arr_t &subscript_via_call_operator_t(arr_t &a, Ix... idx) {
|
||||
a(idx...)++;
|
||||
return a;
|
||||
}
|
||||
template <typename... Ix>
|
||||
py::ssize_t const_subscript_via_call_operator_t(const arr_t &a, Ix... idx) {
|
||||
return a(idx...);
|
||||
}
|
||||
|
||||
#define def_index_fn(name, type) \
|
||||
sm.def(#name, [](type a) { return name(a); }); \
|
||||
|
@ -197,6 +206,13 @@ TEST_SUBMODULE(numpy_array, sm) {
|
|||
sm.def("nbytes", [](const arr &a) { return a.nbytes(); });
|
||||
sm.def("owndata", [](const arr &a) { return a.owndata(); });
|
||||
|
||||
sm.attr("defined_NDEBUG") =
|
||||
#ifdef NDEBUG
|
||||
true;
|
||||
#else
|
||||
false;
|
||||
#endif
|
||||
|
||||
// test_index_offset
|
||||
def_index_fn(index_at, const arr &);
|
||||
def_index_fn(index_at_t, const arr_t &);
|
||||
|
@ -210,6 +226,8 @@ TEST_SUBMODULE(numpy_array, sm) {
|
|||
def_index_fn(mutate_data_t, arr_t &);
|
||||
def_index_fn(at_t, const arr_t &);
|
||||
def_index_fn(mutate_at_t, arr_t &);
|
||||
def_index_fn(subscript_via_call_operator_t, arr_t &);
|
||||
def_index_fn(const_subscript_via_call_operator_t, const arr_t &);
|
||||
|
||||
// test_make_c_f_array
|
||||
sm.def("make_f_array", [] { return py::array_t<float>({2, 2}, {4, 8}); });
|
||||
|
|
|
@ -111,20 +111,32 @@ def test_data(arr, args, ret):
|
|||
assert all(m.data(arr, *args)[(1 if byteorder == "little" else 0) :: 2] == 0)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"func",
|
||||
[
|
||||
m.at_t,
|
||||
m.mutate_at_t,
|
||||
m.const_subscript_via_call_operator_t,
|
||||
m.subscript_via_call_operator_t,
|
||||
][: 2 if m.defined_NDEBUG else 99],
|
||||
)
|
||||
@pytest.mark.parametrize("dim", [0, 1, 3])
|
||||
def test_at_fail(arr, dim):
|
||||
for func in m.at_t, m.mutate_at_t:
|
||||
def test_elem_reference(arr, func, dim):
|
||||
with pytest.raises(IndexError) as excinfo:
|
||||
func(arr, *([0] * dim))
|
||||
assert str(excinfo.value) == f"index dimension mismatch: {dim} (ndim = 2)"
|
||||
|
||||
|
||||
def test_at(arr):
|
||||
assert m.at_t(arr, 0, 2) == 3
|
||||
assert m.at_t(arr, 1, 0) == 4
|
||||
@pytest.mark.parametrize("func", [m.at_t, m.const_subscript_via_call_operator_t])
|
||||
def test_const_elem_reference(arr, func):
|
||||
assert func(arr, 0, 2) == 3
|
||||
assert func(arr, 1, 0) == 4
|
||||
|
||||
assert all(m.mutate_at_t(arr, 0, 2).ravel() == [1, 2, 4, 4, 5, 6])
|
||||
assert all(m.mutate_at_t(arr, 1, 0).ravel() == [1, 2, 4, 5, 5, 6])
|
||||
|
||||
@pytest.mark.parametrize("func", [m.mutate_at_t, m.subscript_via_call_operator_t])
|
||||
def test_mutable_elem_reference(arr, func):
|
||||
assert all(func(arr, 0, 2).ravel() == [1, 2, 4, 4, 5, 6])
|
||||
assert all(func(arr, 1, 0).ravel() == [1, 2, 4, 5, 5, 6])
|
||||
|
||||
|
||||
def test_mutate_readonly(arr):
|
||||
|
@ -153,8 +165,9 @@ def test_mutate_data(arr):
|
|||
assert all(m.mutate_data_t(arr, 1, 2).ravel() == [6, 19, 27, 68, 84, 197])
|
||||
|
||||
|
||||
def test_bounds_check(arr):
|
||||
for func in (
|
||||
@pytest.mark.parametrize(
|
||||
"func",
|
||||
[
|
||||
m.index_at,
|
||||
m.index_at_t,
|
||||
m.data,
|
||||
|
@ -163,7 +176,11 @@ def test_bounds_check(arr):
|
|||
m.mutate_data_t,
|
||||
m.at_t,
|
||||
m.mutate_at_t,
|
||||
):
|
||||
m.const_subscript_via_call_operator_t,
|
||||
m.subscript_via_call_operator_t,
|
||||
][: 8 if m.defined_NDEBUG else 99],
|
||||
)
|
||||
def test_bounds_check(arr, func):
|
||||
with pytest.raises(IndexError) as excinfo:
|
||||
func(arr, 2, 0)
|
||||
assert str(excinfo.value) == "index 2 is out of bounds for axis 0 with size 2"
|
||||
|
|
|
@ -274,10 +274,6 @@ function(pybind11_add_module target_name)
|
|||
target_link_libraries(${target_name} PRIVATE pybind11::embed)
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
target_link_libraries(${target_name} PRIVATE pybind11::windows_extras)
|
||||
endif()
|
||||
|
||||
# -fvisibility=hidden is required to allow multiple modules compiled against
|
||||
# different pybind versions to work properly, and for some features (e.g.
|
||||
# py::module_local). We force it on everything inside the `pybind11`
|
||||
|
|
Loading…
Reference in New Issue