Merge branch 'master' into sh_merge_master

This commit is contained in:
Ralf W. Grosse-Kunstleve 2022-06-01 14:39:51 -07:00
commit 485dacef6a
22 changed files with 451 additions and 72 deletions

View File

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

View File

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

View File

@ -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>();
}
};

View File

@ -100,7 +100,7 @@ private:
if (size > remainder) {
str line(pbase(), size - remainder);
pywrite(line);
pywrite(std::move(line));
pyflush();
}

View File

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

View File

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

View File

@ -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();
}

View File

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

View 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;
}

View File

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

View File

@ -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();
});
}

View File

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

View File

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

View File

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

View File

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

View File

@ -301,7 +301,7 @@ struct B {};
TEST_SUBMODULE(numpy_dtypes, m) {
try {
py::module_::import("numpy");
} catch (...) {
} catch (const py::error_already_set &) {
return;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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