mirror of
https://github.com/pybind/pybind11.git
synced 2025-01-31 15:20:34 +00:00
Merge branch 'master' into sh_merge_master
This commit is contained in:
commit
485dacef6a
@ -426,6 +426,7 @@ PYBIND11_NOINLINE internals &get_internals() {
|
||||
~gil_scoped_acquire_local() { PyGILState_Release(state); }
|
||||
const PyGILState_STATE state;
|
||||
} gil;
|
||||
error_scope err_scope;
|
||||
|
||||
PYBIND11_STR_TYPE id(PYBIND11_INTERNALS_ID);
|
||||
auto builtins = handle(PyEval_GetBuiltins());
|
||||
|
@ -470,13 +470,20 @@ PYBIND11_NOINLINE bool isinstance_generic(handle obj, const std::type_info &tp)
|
||||
return isinstance(obj, type);
|
||||
}
|
||||
|
||||
PYBIND11_NOINLINE std::string error_string() {
|
||||
if (!PyErr_Occurred()) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Unknown internal error occurred");
|
||||
return "Unknown internal error occurred";
|
||||
PYBIND11_NOINLINE std::string error_string(const char *called) {
|
||||
error_scope scope; // Fetch error state (will be restored when this function returns).
|
||||
if (scope.type == nullptr) {
|
||||
if (called == nullptr) {
|
||||
called = "pybind11::detail::error_string()";
|
||||
}
|
||||
pybind11_fail("Internal error: " + std::string(called)
|
||||
+ " called while Python error indicator not set.");
|
||||
}
|
||||
|
||||
error_scope scope; // Preserve error state
|
||||
PyErr_NormalizeException(&scope.type, &scope.value, &scope.trace);
|
||||
if (scope.trace != nullptr) {
|
||||
PyException_SetTraceback(scope.value, scope.trace);
|
||||
}
|
||||
|
||||
std::string errorString;
|
||||
if (scope.type) {
|
||||
@ -487,12 +494,6 @@ PYBIND11_NOINLINE std::string error_string() {
|
||||
errorString += (std::string) str(scope.value);
|
||||
}
|
||||
|
||||
PyErr_NormalizeException(&scope.type, &scope.value, &scope.trace);
|
||||
|
||||
if (scope.trace != nullptr) {
|
||||
PyException_SetTraceback(scope.value, scope.trace);
|
||||
}
|
||||
|
||||
#if !defined(PYPY_VERSION)
|
||||
if (scope.trace) {
|
||||
auto *trace = (PyTracebackObject *) scope.trace;
|
||||
|
@ -98,8 +98,8 @@ public:
|
||||
explicit func_wrapper(func_handle &&hf) noexcept : hfunc(std::move(hf)) {}
|
||||
Return operator()(Args... args) const {
|
||||
gil_scoped_acquire acq;
|
||||
object retval(hfunc.f(std::forward<Args>(args)...));
|
||||
return retval.template cast<Return>();
|
||||
// casts the returned object as a rvalue to the return type
|
||||
return object(hfunc.f(std::forward<Args>(args)...)).template cast<Return>();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -100,7 +100,7 @@ private:
|
||||
|
||||
if (size > remainder) {
|
||||
str line(pbase(), size - remainder);
|
||||
pywrite(line);
|
||||
pywrite(std::move(line));
|
||||
pyflush();
|
||||
}
|
||||
|
||||
|
@ -1174,9 +1174,16 @@ public:
|
||||
py::module_ m3 = m2.def_submodule("subsub", "A submodule of 'example.sub'");
|
||||
\endrst */
|
||||
module_ def_submodule(const char *name, const char *doc = nullptr) {
|
||||
std::string full_name
|
||||
= std::string(PyModule_GetName(m_ptr)) + std::string(".") + std::string(name);
|
||||
auto result = reinterpret_borrow<module_>(PyImport_AddModule(full_name.c_str()));
|
||||
const char *this_name = PyModule_GetName(m_ptr);
|
||||
if (this_name == nullptr) {
|
||||
throw error_already_set();
|
||||
}
|
||||
std::string full_name = std::string(this_name) + '.' + name;
|
||||
handle submodule = PyImport_AddModule(full_name.c_str());
|
||||
if (!submodule) {
|
||||
throw error_already_set();
|
||||
}
|
||||
auto result = reinterpret_borrow<module_>(submodule);
|
||||
if (doc && options::show_user_defined_docstrings()) {
|
||||
result.attr("__doc__") = pybind11::str(doc);
|
||||
}
|
||||
@ -1796,7 +1803,8 @@ public:
|
||||
scope(*this),
|
||||
sibling(getattr(*this, name_, none())),
|
||||
extra...);
|
||||
attr(cf.name()) = staticmethod(cf);
|
||||
auto cf_name = cf.name();
|
||||
attr(std::move(cf_name)) = staticmethod(std::move(cf));
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -1850,7 +1858,7 @@ public:
|
||||
if (!caster.load(obj, false)) {
|
||||
return nullptr;
|
||||
}
|
||||
return new buffer_info(((capture *) ptr)->func(caster));
|
||||
return new buffer_info(((capture *) ptr)->func(std::move(caster)));
|
||||
},
|
||||
ptr);
|
||||
weakref(m_ptr, cpp_function([ptr](handle wr) {
|
||||
@ -2316,12 +2324,12 @@ struct enum_base {
|
||||
str name(name_);
|
||||
if (entries.contains(name)) {
|
||||
std::string type_name = (std::string) str(m_base.attr("__name__"));
|
||||
throw value_error(type_name + ": element \"" + std::string(name_)
|
||||
throw value_error(std::move(type_name) + ": element \"" + std::string(name_)
|
||||
+ "\" already exists!");
|
||||
}
|
||||
|
||||
entries[name] = std::make_pair(value, doc);
|
||||
m_base.attr(name) = value;
|
||||
m_base.attr(std::move(name)) = std::move(value);
|
||||
}
|
||||
|
||||
PYBIND11_NOINLINE void export_values() {
|
||||
@ -2580,7 +2588,7 @@ template <typename Access,
|
||||
typename Sentinel,
|
||||
typename ValueType,
|
||||
typename... Extra>
|
||||
iterator make_iterator_impl(Iterator first, Sentinel last, Extra &&...extra) {
|
||||
iterator make_iterator_impl(Iterator &&first, Sentinel &&last, Extra &&...extra) {
|
||||
using state = detail::iterator_state<Access, Policy, Iterator, Sentinel, ValueType, Extra...>;
|
||||
// TODO: state captures only the types of Extra, not the values
|
||||
|
||||
@ -2606,7 +2614,7 @@ iterator make_iterator_impl(Iterator first, Sentinel last, Extra &&...extra) {
|
||||
Policy);
|
||||
}
|
||||
|
||||
return cast(state{first, last, true});
|
||||
return cast(state{std::forward<Iterator>(first), std::forward<Sentinel>(last), true});
|
||||
}
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
@ -2617,13 +2625,15 @@ template <return_value_policy Policy = return_value_policy::reference_internal,
|
||||
typename Sentinel,
|
||||
typename ValueType = typename detail::iterator_access<Iterator>::result_type,
|
||||
typename... Extra>
|
||||
iterator make_iterator(Iterator first, Sentinel last, Extra &&...extra) {
|
||||
iterator make_iterator(Iterator &&first, Sentinel &&last, Extra &&...extra) {
|
||||
return detail::make_iterator_impl<detail::iterator_access<Iterator>,
|
||||
Policy,
|
||||
Iterator,
|
||||
Sentinel,
|
||||
ValueType,
|
||||
Extra...>(first, last, std::forward<Extra>(extra)...);
|
||||
Extra...>(std::forward<Iterator>(first),
|
||||
std::forward<Sentinel>(last),
|
||||
std::forward<Extra>(extra)...);
|
||||
}
|
||||
|
||||
/// Makes a python iterator over the keys (`.first`) of a iterator over pairs from a
|
||||
@ -2633,13 +2643,15 @@ template <return_value_policy Policy = return_value_policy::reference_internal,
|
||||
typename Sentinel,
|
||||
typename KeyType = typename detail::iterator_key_access<Iterator>::result_type,
|
||||
typename... Extra>
|
||||
iterator make_key_iterator(Iterator first, Sentinel last, Extra &&...extra) {
|
||||
iterator make_key_iterator(Iterator &&first, Sentinel &&last, Extra &&...extra) {
|
||||
return detail::make_iterator_impl<detail::iterator_key_access<Iterator>,
|
||||
Policy,
|
||||
Iterator,
|
||||
Sentinel,
|
||||
KeyType,
|
||||
Extra...>(first, last, std::forward<Extra>(extra)...);
|
||||
Extra...>(std::forward<Iterator>(first),
|
||||
std::forward<Sentinel>(last),
|
||||
std::forward<Extra>(extra)...);
|
||||
}
|
||||
|
||||
/// Makes a python iterator over the values (`.second`) of a iterator over pairs from a
|
||||
@ -2649,13 +2661,15 @@ template <return_value_policy Policy = return_value_policy::reference_internal,
|
||||
typename Sentinel,
|
||||
typename ValueType = typename detail::iterator_value_access<Iterator>::result_type,
|
||||
typename... Extra>
|
||||
iterator make_value_iterator(Iterator first, Sentinel last, Extra &&...extra) {
|
||||
iterator make_value_iterator(Iterator &&first, Sentinel &&last, Extra &&...extra) {
|
||||
return detail::make_iterator_impl<detail::iterator_value_access<Iterator>,
|
||||
Policy,
|
||||
Iterator,
|
||||
Sentinel,
|
||||
ValueType,
|
||||
Extra...>(first, last, std::forward<Extra>(extra)...);
|
||||
Extra...>(std::forward<Iterator>(first),
|
||||
std::forward<Sentinel>(last),
|
||||
std::forward<Extra>(extra)...);
|
||||
}
|
||||
|
||||
/// Makes an iterator over values of an stl container or other container supporting
|
||||
@ -2714,7 +2728,7 @@ void implicitly_convertible() {
|
||||
};
|
||||
|
||||
if (auto *tinfo = detail::get_type_info(typeid(OutputType))) {
|
||||
tinfo->implicit_conversions.push_back(implicit_caster);
|
||||
tinfo->implicit_conversions.emplace_back(std::move(implicit_caster));
|
||||
} else {
|
||||
pybind11_fail("implicitly_convertible: Unable to find type " + type_id<OutputType>());
|
||||
}
|
||||
@ -2851,7 +2865,7 @@ PYBIND11_NOINLINE void print(const tuple &args, const dict &kwargs) {
|
||||
}
|
||||
|
||||
auto write = file.attr("write");
|
||||
write(line);
|
||||
write(std::move(line));
|
||||
write(kwargs.contains("end") ? kwargs["end"] : str("\n"));
|
||||
|
||||
if (kwargs.contains("flush") && kwargs["flush"].cast<bool>()) {
|
||||
|
@ -85,7 +85,9 @@ public:
|
||||
or `object` subclass causes a call to ``__setitem__``.
|
||||
\endrst */
|
||||
item_accessor operator[](handle key) const;
|
||||
/// See above (the only difference is that they key is provided as a string literal)
|
||||
/// See above (the only difference is that the key's reference is stolen)
|
||||
item_accessor operator[](object &&key) const;
|
||||
/// See above (the only difference is that the key is provided as a string literal)
|
||||
item_accessor operator[](const char *key) const;
|
||||
|
||||
/** \rst
|
||||
@ -95,7 +97,9 @@ public:
|
||||
or `object` subclass causes a call to ``setattr``.
|
||||
\endrst */
|
||||
obj_attr_accessor attr(handle key) const;
|
||||
/// See above (the only difference is that they key is provided as a string literal)
|
||||
/// See above (the only difference is that the key's reference is stolen)
|
||||
obj_attr_accessor attr(object &&key) const;
|
||||
/// See above (the only difference is that the key is provided as a string literal)
|
||||
str_attr_accessor attr(const char *key) const;
|
||||
|
||||
/** \rst
|
||||
@ -180,6 +184,10 @@ private:
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
#if !defined(PYBIND11_HANDLE_REF_DEBUG) && !defined(NDEBUG)
|
||||
# define PYBIND11_HANDLE_REF_DEBUG
|
||||
#endif
|
||||
|
||||
/** \rst
|
||||
Holds a reference to a Python object (no reference counting)
|
||||
|
||||
@ -209,6 +217,9 @@ public:
|
||||
this function automatically. Returns a reference to itself.
|
||||
\endrst */
|
||||
const handle &inc_ref() const & {
|
||||
#ifdef PYBIND11_HANDLE_REF_DEBUG
|
||||
inc_ref_counter(1);
|
||||
#endif
|
||||
Py_XINCREF(m_ptr);
|
||||
return *this;
|
||||
}
|
||||
@ -244,6 +255,18 @@ public:
|
||||
|
||||
protected:
|
||||
PyObject *m_ptr = nullptr;
|
||||
|
||||
#ifdef PYBIND11_HANDLE_REF_DEBUG
|
||||
private:
|
||||
static std::size_t inc_ref_counter(std::size_t add) {
|
||||
thread_local std::size_t counter = 0;
|
||||
counter += add;
|
||||
return counter;
|
||||
}
|
||||
|
||||
public:
|
||||
static std::size_t inc_ref_counter() { return inc_ref_counter(0); }
|
||||
#endif
|
||||
};
|
||||
|
||||
/** \rst
|
||||
@ -360,7 +383,7 @@ T reinterpret_steal(handle h) {
|
||||
}
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
std::string error_string();
|
||||
std::string error_string(const char *called = nullptr);
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
@ -375,20 +398,27 @@ PYBIND11_NAMESPACE_END(detail)
|
||||
/// python).
|
||||
class PYBIND11_EXPORT_EXCEPTION error_already_set : public std::runtime_error {
|
||||
public:
|
||||
/// Constructs a new exception from the current Python error indicator, if any. The current
|
||||
/// Constructs a new exception from the current Python error indicator. The current
|
||||
/// Python error indicator will be cleared.
|
||||
error_already_set() : std::runtime_error(detail::error_string()) {
|
||||
error_already_set() : std::runtime_error(detail::error_string("pybind11::error_already_set")) {
|
||||
PyErr_Fetch(&m_type.ptr(), &m_value.ptr(), &m_trace.ptr());
|
||||
}
|
||||
|
||||
/// WARNING: The GIL must be held when this copy constructor is invoked!
|
||||
error_already_set(const error_already_set &) = default;
|
||||
error_already_set(error_already_set &&) = default;
|
||||
|
||||
/// WARNING: This destructor needs to acquire the Python GIL. This can lead to
|
||||
/// crashes (undefined behavior) if the Python interpreter is finalizing.
|
||||
inline ~error_already_set() override;
|
||||
|
||||
/// Give the currently-held error back to Python, if any. If there is currently a Python error
|
||||
/// already set it is cleared first. After this call, the current object no longer stores the
|
||||
/// error variables (but the `.what()` string is still available).
|
||||
/// Restores the currently-held Python error (which will clear the Python error indicator first
|
||||
/// if already set). After this call, the current object no longer stores the error variables.
|
||||
/// NOTE: Any copies of this object may still store the error variables. Currently there is no
|
||||
// protection against calling restore() from multiple copies.
|
||||
/// NOTE: This member function will always restore the normalized exception, which may or may
|
||||
/// not be the original Python exception.
|
||||
/// WARNING: The GIL must be held when this member function is called!
|
||||
void restore() {
|
||||
PyErr_Restore(m_type.release().ptr(), m_value.release().ptr(), m_trace.release().ptr());
|
||||
}
|
||||
@ -405,6 +435,7 @@ public:
|
||||
}
|
||||
/// An alternate version of `discard_as_unraisable()`, where a string provides information on
|
||||
/// the location of the error. For example, `__func__` could be helpful.
|
||||
/// WARNING: The GIL must be held when this member function is called!
|
||||
void discard_as_unraisable(const char *err_context) {
|
||||
discard_as_unraisable(reinterpret_steal<object>(PYBIND11_FROM_STRING(err_context)));
|
||||
}
|
||||
@ -657,7 +688,7 @@ public:
|
||||
}
|
||||
template <typename T>
|
||||
void operator=(T &&value) & {
|
||||
get_cache() = reinterpret_borrow<object>(object_or_cast(std::forward<T>(value)));
|
||||
get_cache() = ensure_object(object_or_cast(std::forward<T>(value)));
|
||||
}
|
||||
|
||||
template <typename T = Policy>
|
||||
@ -685,6 +716,9 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
static object ensure_object(object &&o) { return std::move(o); }
|
||||
static object ensure_object(handle h) { return reinterpret_borrow<object>(h); }
|
||||
|
||||
object &get_cache() const {
|
||||
if (!cache) {
|
||||
cache = Policy::get(obj, key);
|
||||
@ -1581,6 +1615,8 @@ public:
|
||||
|
||||
capsule(const void *value, void (*destructor)(void *)) {
|
||||
m_ptr = PyCapsule_New(const_cast<void *>(value), nullptr, [](PyObject *o) {
|
||||
// guard if destructor called while err indicator is set
|
||||
error_scope error_guard;
|
||||
auto destructor = reinterpret_cast<void (*)(void *)>(PyCapsule_GetContext(o));
|
||||
if (destructor == nullptr) {
|
||||
if (PyErr_Occurred()) {
|
||||
@ -1682,7 +1718,10 @@ public:
|
||||
size_t size() const { return (size_t) PyTuple_Size(m_ptr); }
|
||||
bool empty() const { return size() == 0; }
|
||||
detail::tuple_accessor operator[](size_t index) const { return {*this, index}; }
|
||||
detail::item_accessor operator[](handle h) const { return object::operator[](h); }
|
||||
template <typename T, detail::enable_if_t<detail::is_pyobject<T>::value, int> = 0>
|
||||
detail::item_accessor operator[](T &&o) const {
|
||||
return object::operator[](std::forward<T>(o));
|
||||
}
|
||||
detail::tuple_iterator begin() const { return {*this, 0}; }
|
||||
detail::tuple_iterator end() const { return {*this, PyTuple_GET_SIZE(m_ptr)}; }
|
||||
};
|
||||
@ -1742,7 +1781,10 @@ public:
|
||||
}
|
||||
bool empty() const { return size() == 0; }
|
||||
detail::sequence_accessor operator[](size_t index) const { return {*this, index}; }
|
||||
detail::item_accessor operator[](handle h) const { return object::operator[](h); }
|
||||
template <typename T, detail::enable_if_t<detail::is_pyobject<T>::value, int> = 0>
|
||||
detail::item_accessor operator[](T &&o) const {
|
||||
return object::operator[](std::forward<T>(o));
|
||||
}
|
||||
detail::sequence_iterator begin() const { return {*this, 0}; }
|
||||
detail::sequence_iterator end() const { return {*this, PySequence_Size(m_ptr)}; }
|
||||
};
|
||||
@ -1761,7 +1803,10 @@ public:
|
||||
size_t size() const { return (size_t) PyList_Size(m_ptr); }
|
||||
bool empty() const { return size() == 0; }
|
||||
detail::list_accessor operator[](size_t index) const { return {*this, index}; }
|
||||
detail::item_accessor operator[](handle h) const { return object::operator[](h); }
|
||||
template <typename T, detail::enable_if_t<detail::is_pyobject<T>::value, int> = 0>
|
||||
detail::item_accessor operator[](T &&o) const {
|
||||
return object::operator[](std::forward<T>(o));
|
||||
}
|
||||
detail::list_iterator begin() const { return {*this, 0}; }
|
||||
detail::list_iterator end() const { return {*this, PyList_GET_SIZE(m_ptr)}; }
|
||||
template <typename T>
|
||||
@ -2061,6 +2106,10 @@ item_accessor object_api<D>::operator[](handle key) const {
|
||||
return {derived(), reinterpret_borrow<object>(key)};
|
||||
}
|
||||
template <typename D>
|
||||
item_accessor object_api<D>::operator[](object &&key) const {
|
||||
return {derived(), std::move(key)};
|
||||
}
|
||||
template <typename D>
|
||||
item_accessor object_api<D>::operator[](const char *key) const {
|
||||
return {derived(), pybind11::str(key)};
|
||||
}
|
||||
@ -2069,6 +2118,10 @@ obj_attr_accessor object_api<D>::attr(handle key) const {
|
||||
return {derived(), reinterpret_borrow<object>(key)};
|
||||
}
|
||||
template <typename D>
|
||||
obj_attr_accessor object_api<D>::attr(object &&key) const {
|
||||
return {derived(), std::move(key)};
|
||||
}
|
||||
template <typename D>
|
||||
str_attr_accessor object_api<D>::attr(const char *key) const {
|
||||
return {derived(), key};
|
||||
}
|
||||
|
@ -128,7 +128,7 @@ struct map_caster {
|
||||
if (!key || !value) {
|
||||
return handle();
|
||||
}
|
||||
d[key] = value;
|
||||
d[std::move(key)] = std::move(value);
|
||||
}
|
||||
return d.release();
|
||||
}
|
||||
|
@ -232,6 +232,7 @@ tests_extra_targets("test_exceptions.py;test_local_bindings.py;test_stl.py;test_
|
||||
"pybind11_cross_module_tests")
|
||||
|
||||
# And add additional targets for other tests.
|
||||
tests_extra_targets("test_exceptions.py" "cross_module_interleaved_error_already_set")
|
||||
tests_extra_targets("test_gil_scoped.py" "cross_module_gil_utils")
|
||||
tests_extra_targets("test_class_sh_module_local.py"
|
||||
"class_sh_module_local_0;class_sh_module_local_1;class_sh_module_local_2")
|
||||
|
51
tests/cross_module_interleaved_error_already_set.cpp
Normal file
51
tests/cross_module_interleaved_error_already_set.cpp
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
Copyright (c) 2022 Google LLC
|
||||
|
||||
All rights reserved. Use of this source code is governed by a
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include <pybind11/pybind11.h>
|
||||
|
||||
// This file mimics a DSO that makes pybind11 calls but does not define a PYBIND11_MODULE,
|
||||
// so that the first call of cross_module_error_already_set() triggers the first call of
|
||||
// pybind11::detail::get_internals().
|
||||
|
||||
namespace {
|
||||
|
||||
namespace py = pybind11;
|
||||
|
||||
void interleaved_error_already_set() {
|
||||
PyErr_SetString(PyExc_RuntimeError, "1st error.");
|
||||
try {
|
||||
throw py::error_already_set();
|
||||
} catch (const py::error_already_set &) {
|
||||
// The 2nd error could be conditional in a real application.
|
||||
PyErr_SetString(PyExc_RuntimeError, "2nd error.");
|
||||
} // Here the 1st error is destroyed before the 2nd error is fetched.
|
||||
// The error_already_set dtor triggers a pybind11::detail::get_internals()
|
||||
// call via pybind11::gil_scoped_acquire.
|
||||
if (PyErr_Occurred()) {
|
||||
throw py::error_already_set();
|
||||
}
|
||||
}
|
||||
|
||||
constexpr char kModuleName[] = "cross_module_interleaved_error_already_set";
|
||||
|
||||
struct PyModuleDef moduledef = {
|
||||
PyModuleDef_HEAD_INIT, kModuleName, nullptr, 0, nullptr, nullptr, nullptr, nullptr, nullptr};
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" PYBIND11_EXPORT PyObject *PyInit_cross_module_interleaved_error_already_set() {
|
||||
PyObject *m = PyModule_Create(&moduledef);
|
||||
if (m != nullptr) {
|
||||
static_assert(sizeof(&interleaved_error_already_set) == sizeof(void *),
|
||||
"Function pointer must have the same size as void *");
|
||||
PyModule_AddObject(
|
||||
m,
|
||||
"funcaddr",
|
||||
PyLong_FromVoidPtr(reinterpret_cast<void *>(&interleaved_error_already_set)));
|
||||
}
|
||||
return m;
|
||||
}
|
@ -274,7 +274,8 @@ TEST_SUBMODULE(builtin_casters, m) {
|
||||
m.def("lvalue_nested", []() -> const decltype(lvnested) & { return lvnested; });
|
||||
|
||||
static std::pair<int, std::string> int_string_pair{2, "items"};
|
||||
m.def("int_string_pair", []() { return &int_string_pair; });
|
||||
m.def(
|
||||
"int_string_pair", []() { return &int_string_pair; }, py::return_value_policy::reference);
|
||||
|
||||
// test_builtins_cast_return_none
|
||||
m.def("return_none_string", []() -> std::string * { return nullptr; });
|
||||
|
@ -228,7 +228,10 @@ TEST_SUBMODULE(exceptions, m) {
|
||||
throw py::error_already_set();
|
||||
} catch (const std::runtime_error &e) {
|
||||
if ((err && e.what() != std::string("ValueError: foo"))
|
||||
|| (!err && e.what() != std::string("Unknown internal error occurred"))) {
|
||||
|| (!err
|
||||
&& e.what()
|
||||
!= std::string("Internal error: pybind11::error_already_set called "
|
||||
"while Python error indicator not set."))) {
|
||||
PyErr_Clear();
|
||||
throw std::runtime_error("error message mismatch");
|
||||
}
|
||||
@ -299,4 +302,19 @@ TEST_SUBMODULE(exceptions, m) {
|
||||
std::throw_with_nested(std::runtime_error("Outer Exception"));
|
||||
}
|
||||
});
|
||||
|
||||
m.def("error_already_set_what", [](const py::object &exc_type, const py::object &exc_value) {
|
||||
PyErr_SetObject(exc_type.ptr(), exc_value.ptr());
|
||||
std::string what = py::error_already_set().what();
|
||||
bool py_err_set_after_what = (PyErr_Occurred() != nullptr);
|
||||
PyErr_Clear();
|
||||
return py::make_tuple(std::move(what), py_err_set_after_what);
|
||||
});
|
||||
|
||||
m.def("test_cross_module_interleaved_error_already_set", []() {
|
||||
auto cm = py::module_::import("cross_module_interleaved_error_already_set");
|
||||
auto interleaved_error_already_set
|
||||
= reinterpret_cast<void (*)()>(PyLong_AsVoidPtr(cm.attr("funcaddr").ptr()));
|
||||
interleaved_error_already_set();
|
||||
});
|
||||
}
|
||||
|
@ -16,7 +16,10 @@ def test_std_exception(msg):
|
||||
def test_error_already_set(msg):
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
m.throw_already_set(False)
|
||||
assert msg(excinfo.value) == "Unknown internal error occurred"
|
||||
assert (
|
||||
msg(excinfo.value)
|
||||
== "Internal error: pybind11::error_already_set called while Python error indicator not set."
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
m.throw_already_set(True)
|
||||
@ -270,3 +273,62 @@ def test_local_translator(msg):
|
||||
m.throws_local_simple_error()
|
||||
assert not isinstance(excinfo.value, cm.LocalSimpleException)
|
||||
assert msg(excinfo.value) == "this mod"
|
||||
|
||||
|
||||
class FlakyException(Exception):
|
||||
def __init__(self, failure_point):
|
||||
if failure_point == "failure_point_init":
|
||||
raise ValueError("triggered_failure_point_init")
|
||||
self.failure_point = failure_point
|
||||
|
||||
def __str__(self):
|
||||
if self.failure_point == "failure_point_str":
|
||||
raise ValueError("triggered_failure_point_str")
|
||||
return "FlakyException.__str__"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"exc_type, exc_value, expected_what",
|
||||
(
|
||||
(ValueError, "plain_str", "ValueError: plain_str"),
|
||||
(ValueError, ("tuple_elem",), "ValueError: tuple_elem"),
|
||||
(FlakyException, ("happy",), "FlakyException: FlakyException.__str__"),
|
||||
),
|
||||
)
|
||||
def test_error_already_set_what_with_happy_exceptions(
|
||||
exc_type, exc_value, expected_what
|
||||
):
|
||||
what, py_err_set_after_what = m.error_already_set_what(exc_type, exc_value)
|
||||
assert not py_err_set_after_what
|
||||
assert what == expected_what
|
||||
|
||||
|
||||
@pytest.mark.skipif("env.PYPY", reason="PyErr_NormalizeException Segmentation fault")
|
||||
def test_flaky_exception_failure_point_init():
|
||||
what, py_err_set_after_what = m.error_already_set_what(
|
||||
FlakyException, ("failure_point_init",)
|
||||
)
|
||||
assert not py_err_set_after_what
|
||||
lines = what.splitlines()
|
||||
# PyErr_NormalizeException replaces the original FlakyException with ValueError:
|
||||
assert lines[:3] == ["ValueError: triggered_failure_point_init", "", "At:"]
|
||||
# Checking the first two lines of the traceback as formatted in error_string():
|
||||
assert "test_exceptions.py(" in lines[3]
|
||||
assert lines[3].endswith("): __init__")
|
||||
assert lines[4].endswith("): test_flaky_exception_failure_point_init")
|
||||
|
||||
|
||||
def test_flaky_exception_failure_point_str():
|
||||
# The error_already_set ctor fails due to a ValueError in error_string():
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
m.error_already_set_what(FlakyException, ("failure_point_str",))
|
||||
assert str(excinfo.value) == "triggered_failure_point_str"
|
||||
|
||||
|
||||
def test_cross_module_interleaved_error_already_set():
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
m.test_cross_module_interleaved_error_already_set()
|
||||
assert str(excinfo.value) in (
|
||||
"2nd error.", # Almost all platforms.
|
||||
"RuntimeError: 2nd error.", # Some PyPy builds (seen under macOS).
|
||||
)
|
||||
|
@ -120,4 +120,6 @@ TEST_SUBMODULE(modules, m) {
|
||||
|
||||
return failures;
|
||||
});
|
||||
|
||||
m.def("def_submodule", [](py::module_ m, const char *name) { return m.def_submodule(name); });
|
||||
}
|
||||
|
@ -1,3 +1,6 @@
|
||||
import pytest
|
||||
|
||||
import env
|
||||
from pybind11_tests import ConstructorStats
|
||||
from pybind11_tests import modules as m
|
||||
from pybind11_tests.modules import subsubmodule as ms
|
||||
@ -89,3 +92,30 @@ def test_builtin_key_type():
|
||||
keys = __builtins__.__dict__.keys()
|
||||
|
||||
assert {type(k) for k in keys} == {str}
|
||||
|
||||
|
||||
@pytest.mark.xfail("env.PYPY", reason="PyModule_GetName()")
|
||||
def test_def_submodule_failures():
|
||||
sm = m.def_submodule(m, b"ScratchSubModuleName") # Using bytes to show it works.
|
||||
assert sm.__name__ == m.__name__ + "." + "ScratchSubModuleName"
|
||||
malformed_utf8 = b"\x80"
|
||||
if env.PYPY:
|
||||
# It is not worth the effort finding a trigger for a failure when running with PyPy.
|
||||
pytest.skip("Sufficiently exercised on platforms other than PyPy.")
|
||||
else:
|
||||
# Meant to trigger PyModule_GetName() failure:
|
||||
sm_name_orig = sm.__name__
|
||||
sm.__name__ = malformed_utf8
|
||||
try:
|
||||
with pytest.raises(Exception):
|
||||
# Seen with Python 3.9: SystemError: nameless module
|
||||
# But we do not want to exercise the internals of PyModule_GetName(), which could
|
||||
# change in future versions of Python, but a bad __name__ is very likely to cause
|
||||
# some kind of failure indefinitely.
|
||||
m.def_submodule(sm, b"SubSubModuleName")
|
||||
finally:
|
||||
# Clean up to ensure nothing gets upset by a module with an invalid __name__.
|
||||
sm.__name__ = sm_name_orig # Purely precautionary.
|
||||
# Meant to trigger PyImport_AddModule() failure:
|
||||
with pytest.raises(UnicodeDecodeError):
|
||||
m.def_submodule(sm, malformed_utf8)
|
||||
|
@ -162,7 +162,7 @@ static int data_i = 42;
|
||||
TEST_SUBMODULE(numpy_array, sm) {
|
||||
try {
|
||||
py::module_::import("numpy");
|
||||
} catch (...) {
|
||||
} catch (const py::error_already_set &) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -301,7 +301,7 @@ struct B {};
|
||||
TEST_SUBMODULE(numpy_dtypes, m) {
|
||||
try {
|
||||
py::module_::import("numpy");
|
||||
} catch (...) {
|
||||
} catch (const py::error_already_set &) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@ double my_func(int x, float y, double z) {
|
||||
TEST_SUBMODULE(numpy_vectorize, m) {
|
||||
try {
|
||||
py::module_::import("numpy");
|
||||
} catch (...) {
|
||||
} catch (const py::error_already_set &) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -297,6 +297,55 @@ TEST_SUBMODULE(pytypes, m) {
|
||||
return d;
|
||||
});
|
||||
|
||||
m.def("accessor_moves", []() { // See PR #3970
|
||||
py::list return_list;
|
||||
#ifdef PYBIND11_HANDLE_REF_DEBUG
|
||||
py::int_ py_int_0(0);
|
||||
py::int_ py_int_42(42);
|
||||
py::str py_str_count("count");
|
||||
|
||||
auto tup = py::make_tuple(0);
|
||||
|
||||
py::sequence seq(tup);
|
||||
|
||||
py::list lst;
|
||||
lst.append(0);
|
||||
|
||||
# define PYBIND11_LOCAL_DEF(...) \
|
||||
{ \
|
||||
std::size_t inc_refs = py::handle::inc_ref_counter(); \
|
||||
__VA_ARGS__; \
|
||||
inc_refs = py::handle::inc_ref_counter() - inc_refs; \
|
||||
return_list.append(inc_refs); \
|
||||
}
|
||||
|
||||
PYBIND11_LOCAL_DEF(tup[py_int_0]) // l-value (to have a control)
|
||||
PYBIND11_LOCAL_DEF(tup[py::int_(0)]) // r-value
|
||||
|
||||
PYBIND11_LOCAL_DEF(tup.attr(py_str_count)) // l-value
|
||||
PYBIND11_LOCAL_DEF(tup.attr(py::str("count"))) // r-value
|
||||
|
||||
PYBIND11_LOCAL_DEF(seq[py_int_0]) // l-value
|
||||
PYBIND11_LOCAL_DEF(seq[py::int_(0)]) // r-value
|
||||
|
||||
PYBIND11_LOCAL_DEF(seq.attr(py_str_count)) // l-value
|
||||
PYBIND11_LOCAL_DEF(seq.attr(py::str("count"))) // r-value
|
||||
|
||||
PYBIND11_LOCAL_DEF(lst[py_int_0]) // l-value
|
||||
PYBIND11_LOCAL_DEF(lst[py::int_(0)]) // r-value
|
||||
|
||||
PYBIND11_LOCAL_DEF(lst.attr(py_str_count)) // l-value
|
||||
PYBIND11_LOCAL_DEF(lst.attr(py::str("count"))) // r-value
|
||||
|
||||
auto lst_acc = lst[py::int_(0)];
|
||||
lst_acc = py::int_(42); // Detaches lst_acc from lst.
|
||||
PYBIND11_LOCAL_DEF(lst_acc = py_int_42) // l-value
|
||||
PYBIND11_LOCAL_DEF(lst_acc = py::int_(42)) // r-value
|
||||
# undef PYBIND11_LOCAL_DEF
|
||||
#endif
|
||||
return return_list;
|
||||
});
|
||||
|
||||
// test_constructors
|
||||
m.def("default_constructors", []() {
|
||||
return py::dict("bytes"_a = py::bytes(),
|
||||
@ -612,4 +661,38 @@ TEST_SUBMODULE(pytypes, m) {
|
||||
double v = x.get_value();
|
||||
return v * v;
|
||||
});
|
||||
|
||||
m.def("tuple_rvalue_getter", [](const py::tuple &tup) {
|
||||
// tests accessing tuple object with rvalue int
|
||||
for (size_t i = 0; i < tup.size(); i++) {
|
||||
auto o = py::handle(tup[py::int_(i)]);
|
||||
if (!o) {
|
||||
throw py::value_error("tuple is malformed");
|
||||
}
|
||||
}
|
||||
return tup;
|
||||
});
|
||||
m.def("list_rvalue_getter", [](const py::list &l) {
|
||||
// tests accessing list with rvalue int
|
||||
for (size_t i = 0; i < l.size(); i++) {
|
||||
auto o = py::handle(l[py::int_(i)]);
|
||||
if (!o) {
|
||||
throw py::value_error("list is malformed");
|
||||
}
|
||||
}
|
||||
return l;
|
||||
});
|
||||
m.def("populate_dict_rvalue", [](int population) {
|
||||
auto d = py::dict();
|
||||
for (int i = 0; i < population; i++) {
|
||||
d[py::int_(i)] = py::int_(i);
|
||||
}
|
||||
return d;
|
||||
});
|
||||
m.def("populate_obj_str_attrs", [](py::object &o, int population) {
|
||||
for (int i = 0; i < population; i++) {
|
||||
o.attr(py::str(py::int_(i))) = py::str(py::int_(i));
|
||||
}
|
||||
return o;
|
||||
});
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import contextlib
|
||||
import sys
|
||||
import types
|
||||
|
||||
import pytest
|
||||
|
||||
@ -317,6 +318,14 @@ def test_accessors():
|
||||
assert d["var"] == 99
|
||||
|
||||
|
||||
def test_accessor_moves():
|
||||
inc_refs = m.accessor_moves()
|
||||
if inc_refs:
|
||||
assert inc_refs == [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]
|
||||
else:
|
||||
pytest.skip("Not defined: PYBIND11_HANDLE_REF_DEBUG")
|
||||
|
||||
|
||||
def test_constructors():
|
||||
"""C++ default and converting constructors are equivalent to type calls in Python"""
|
||||
types = [bytes, bytearray, str, bool, int, float, tuple, list, dict, set]
|
||||
@ -698,3 +707,30 @@ def test_implementation_details():
|
||||
def test_external_float_():
|
||||
r1 = m.square_float_(2.0)
|
||||
assert r1 == 4.0
|
||||
|
||||
|
||||
def test_tuple_rvalue_getter():
|
||||
pop = 1000
|
||||
tup = tuple(range(pop))
|
||||
m.tuple_rvalue_getter(tup)
|
||||
|
||||
|
||||
def test_list_rvalue_getter():
|
||||
pop = 1000
|
||||
my_list = list(range(pop))
|
||||
m.list_rvalue_getter(my_list)
|
||||
|
||||
|
||||
def test_populate_dict_rvalue():
|
||||
pop = 1000
|
||||
my_dict = {i: i for i in range(pop)}
|
||||
assert m.populate_dict_rvalue(pop) == my_dict
|
||||
|
||||
|
||||
def test_populate_obj_str_attrs():
|
||||
pop = 1000
|
||||
o = types.SimpleNamespace(**{str(i): i for i in range(pop)})
|
||||
new_o = m.populate_obj_str_attrs(o, pop)
|
||||
new_attrs = {k: v for k, v in new_o.__dict__.items() if not k.startswith("_")}
|
||||
assert all(isinstance(v, str) for v in new_attrs.values())
|
||||
assert len(new_attrs) == pop
|
||||
|
@ -177,7 +177,8 @@ TEST_SUBMODULE(stl, m) {
|
||||
[](const std::vector<bool> &v) { return v.at(0) == true && v.at(1) == false; });
|
||||
// Unnumbered regression (caused by #936): pointers to stl containers aren't castable
|
||||
static std::vector<RValueCaster> lvv{2};
|
||||
m.def("cast_ptr_vector", []() { return &lvv; });
|
||||
m.def(
|
||||
"cast_ptr_vector", []() { return &lvv; }, py::return_value_policy::reference);
|
||||
|
||||
// test_deque
|
||||
m.def("cast_deque", []() { return std::deque<int>{1}; });
|
||||
|
@ -151,26 +151,36 @@ if(NOT _PYTHON_SUCCESS MATCHES 0)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Can manually set values when cross-compiling
|
||||
macro(_PYBIND11_GET_IF_UNDEF lst index name)
|
||||
if(NOT DEFINED "${name}")
|
||||
list(GET "${lst}" "${index}" "${name}")
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
# Convert the process output into a list
|
||||
if(WIN32)
|
||||
string(REGEX REPLACE "\\\\" "/" _PYTHON_VALUES ${_PYTHON_VALUES})
|
||||
endif()
|
||||
string(REGEX REPLACE ";" "\\\\;" _PYTHON_VALUES ${_PYTHON_VALUES})
|
||||
string(REGEX REPLACE "\n" ";" _PYTHON_VALUES ${_PYTHON_VALUES})
|
||||
list(GET _PYTHON_VALUES 0 _PYTHON_VERSION_LIST)
|
||||
list(GET _PYTHON_VALUES 1 PYTHON_PREFIX)
|
||||
list(GET _PYTHON_VALUES 2 PYTHON_INCLUDE_DIR)
|
||||
list(GET _PYTHON_VALUES 3 PYTHON_SITE_PACKAGES)
|
||||
list(GET _PYTHON_VALUES 4 PYTHON_MODULE_EXTENSION)
|
||||
list(GET _PYTHON_VALUES 5 PYTHON_IS_DEBUG)
|
||||
list(GET _PYTHON_VALUES 6 PYTHON_SIZEOF_VOID_P)
|
||||
list(GET _PYTHON_VALUES 7 PYTHON_LIBRARY_SUFFIX)
|
||||
list(GET _PYTHON_VALUES 8 PYTHON_LIBDIR)
|
||||
list(GET _PYTHON_VALUES 9 PYTHON_MULTIARCH)
|
||||
_pybind11_get_if_undef(_PYTHON_VALUES 0 _PYTHON_VERSION_LIST)
|
||||
_pybind11_get_if_undef(_PYTHON_VALUES 1 PYTHON_PREFIX)
|
||||
_pybind11_get_if_undef(_PYTHON_VALUES 2 PYTHON_INCLUDE_DIR)
|
||||
_pybind11_get_if_undef(_PYTHON_VALUES 3 PYTHON_SITE_PACKAGES)
|
||||
_pybind11_get_if_undef(_PYTHON_VALUES 4 PYTHON_MODULE_EXTENSION)
|
||||
_pybind11_get_if_undef(_PYTHON_VALUES 5 PYTHON_IS_DEBUG)
|
||||
_pybind11_get_if_undef(_PYTHON_VALUES 6 PYTHON_SIZEOF_VOID_P)
|
||||
_pybind11_get_if_undef(_PYTHON_VALUES 7 PYTHON_LIBRARY_SUFFIX)
|
||||
_pybind11_get_if_undef(_PYTHON_VALUES 8 PYTHON_LIBDIR)
|
||||
_pybind11_get_if_undef(_PYTHON_VALUES 9 PYTHON_MULTIARCH)
|
||||
|
||||
# Make sure the Python has the same pointer-size as the chosen compiler
|
||||
# Skip if CMAKE_SIZEOF_VOID_P is not defined
|
||||
if(CMAKE_SIZEOF_VOID_P AND (NOT "${PYTHON_SIZEOF_VOID_P}" STREQUAL "${CMAKE_SIZEOF_VOID_P}"))
|
||||
# This should be skipped for (non-Apple) cross-compiles (like EMSCRIPTEN)
|
||||
if(NOT CMAKE_CROSSCOMPILING
|
||||
AND CMAKE_SIZEOF_VOID_P
|
||||
AND (NOT "${PYTHON_SIZEOF_VOID_P}" STREQUAL "${CMAKE_SIZEOF_VOID_P}"))
|
||||
if(PythonLibsNew_FIND_REQUIRED)
|
||||
math(EXPR _PYTHON_BITS "${PYTHON_SIZEOF_VOID_P} * 8")
|
||||
math(EXPR _CMAKE_BITS "${CMAKE_SIZEOF_VOID_P} * 8")
|
||||
|
@ -115,17 +115,32 @@ if(PYTHON_IS_DEBUG)
|
||||
PROPERTY INTERFACE_COMPILE_DEFINITIONS Py_DEBUG)
|
||||
endif()
|
||||
|
||||
set_property(
|
||||
TARGET pybind11::module
|
||||
APPEND
|
||||
PROPERTY
|
||||
INTERFACE_LINK_LIBRARIES pybind11::python_link_helper
|
||||
"$<$<OR:$<PLATFORM_ID:Windows>,$<PLATFORM_ID:Cygwin>>:$<BUILD_INTERFACE:${PYTHON_LIBRARIES}>>")
|
||||
if(CMAKE_VERSION VERSION_LESS 3.11)
|
||||
set_property(
|
||||
TARGET pybind11::module
|
||||
APPEND
|
||||
PROPERTY
|
||||
INTERFACE_LINK_LIBRARIES
|
||||
pybind11::python_link_helper
|
||||
"$<$<OR:$<PLATFORM_ID:Windows>,$<PLATFORM_ID:Cygwin>>:$<BUILD_INTERFACE:${PYTHON_LIBRARIES}>>"
|
||||
)
|
||||
|
||||
set_property(
|
||||
TARGET pybind11::embed
|
||||
APPEND
|
||||
PROPERTY INTERFACE_LINK_LIBRARIES pybind11::pybind11 $<BUILD_INTERFACE:${PYTHON_LIBRARIES}>)
|
||||
set_property(
|
||||
TARGET pybind11::embed
|
||||
APPEND
|
||||
PROPERTY INTERFACE_LINK_LIBRARIES pybind11::pybind11 $<BUILD_INTERFACE:${PYTHON_LIBRARIES}>)
|
||||
else()
|
||||
target_link_libraries(
|
||||
pybind11::module
|
||||
INTERFACE
|
||||
pybind11::python_link_helper
|
||||
"$<$<OR:$<PLATFORM_ID:Windows>,$<PLATFORM_ID:Cygwin>>:$<BUILD_INTERFACE:${PYTHON_LIBRARIES}>>"
|
||||
)
|
||||
|
||||
target_link_libraries(pybind11::embed INTERFACE pybind11::pybind11
|
||||
$<BUILD_INTERFACE:${PYTHON_LIBRARIES}>)
|
||||
|
||||
endif()
|
||||
|
||||
function(pybind11_extension name)
|
||||
# The prefix and extension are provided by FindPythonLibsNew.cmake
|
||||
|
Loading…
Reference in New Issue
Block a user