aboutsummaryrefslogtreecommitdiff
path: root/include/pybind11/pytypes.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/pybind11/pytypes.h')
-rw-r--r--include/pybind11/pytypes.h1902
1 files changed, 1393 insertions, 509 deletions
diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h
index 78db7947..c93e3d3b 100644
--- a/include/pybind11/pytypes.h
+++ b/include/pybind11/pytypes.h
@@ -11,30 +11,53 @@
#include "detail/common.h"
#include "buffer_info.h"
-#include <utility>
+
+#include <assert.h>
+#include <cstddef>
+#include <exception>
+#include <frameobject.h>
+#include <iterator>
+#include <memory>
+#include <string>
#include <type_traits>
+#include <typeinfo>
+#include <utility>
+
+#if defined(PYBIND11_HAS_OPTIONAL)
+# include <optional>
+#endif
+
+#ifdef PYBIND11_HAS_STRING_VIEW
+# include <string_view>
+#endif
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
+PYBIND11_WARNING_DISABLE_MSVC(4127)
+
/* A few forward declarations */
-class handle; class object;
-class str; class iterator;
+class handle;
+class object;
+class str;
+class iterator;
class type;
-struct arg; struct arg_v;
+struct arg;
+struct arg_v;
PYBIND11_NAMESPACE_BEGIN(detail)
class args_proxy;
-inline bool isinstance_generic(handle obj, const std::type_info &tp);
+bool isinstance_generic(handle obj, const std::type_info &tp);
// Accessor forward declarations
-template <typename Policy> class accessor;
+template <typename Policy>
+class accessor;
namespace accessor_policies {
- struct obj_attr;
- struct str_attr;
- struct generic_item;
- struct sequence_item;
- struct list_item;
- struct tuple_item;
+struct obj_attr;
+struct str_attr;
+struct generic_item;
+struct sequence_item;
+struct list_item;
+struct tuple_item;
} // namespace accessor_policies
using obj_attr_accessor = accessor<accessor_policies::obj_attr>;
using str_attr_accessor = accessor<accessor_policies::str_attr>;
@@ -44,8 +67,9 @@ using list_accessor = accessor<accessor_policies::list_item>;
using tuple_accessor = accessor<accessor_policies::tuple_item>;
/// Tag and check to identify a class which implements the Python object API
-class pyobject_tag { };
-template <typename T> using is_pyobject = std::is_base_of<pyobject_tag, remove_reference_t<T>>;
+class pyobject_tag {};
+template <typename T>
+using is_pyobject = std::is_base_of<pyobject_tag, remove_reference_t<T>>;
/** \rst
A mixin class which adds common functions to `handle`, `object` and various accessors.
@@ -71,7 +95,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
@@ -81,7 +107,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
@@ -93,7 +121,8 @@ public:
args_proxy operator*() const;
/// Check if the given item is contained within this object, i.e. ``item in obj``.
- template <typename T> bool contains(T &&item) const;
+ template <typename T>
+ bool contains(T &&item) const;
/** \rst
Assuming the Python object is a function or implements the ``__call__``
@@ -105,44 +134,46 @@ public:
function will throw a `cast_error` exception. When the Python function
call fails, a `error_already_set` exception is thrown.
\endrst */
- template <return_value_policy policy = return_value_policy::automatic_reference, typename... Args>
+ template <return_value_policy policy = return_value_policy::automatic_reference,
+ typename... Args>
object operator()(Args &&...args) const;
- template <return_value_policy policy = return_value_policy::automatic_reference, typename... Args>
+ template <return_value_policy policy = return_value_policy::automatic_reference,
+ typename... Args>
PYBIND11_DEPRECATED("call(...) was deprecated in favor of operator()(...)")
- object call(Args&&... args) const;
+ object call(Args &&...args) const;
/// Equivalent to ``obj is other`` in Python.
- bool is(object_api const& other) const { return derived().ptr() == other.derived().ptr(); }
+ bool is(object_api const &other) const { return derived().ptr() == other.derived().ptr(); }
/// Equivalent to ``obj is None`` in Python.
bool is_none() const { return derived().ptr() == Py_None; }
/// Equivalent to obj == other in Python
- bool equal(object_api const &other) const { return rich_compare(other, Py_EQ); }
- bool not_equal(object_api const &other) const { return rich_compare(other, Py_NE); }
- bool operator<(object_api const &other) const { return rich_compare(other, Py_LT); }
+ bool equal(object_api const &other) const { return rich_compare(other, Py_EQ); }
+ bool not_equal(object_api const &other) const { return rich_compare(other, Py_NE); }
+ bool operator<(object_api const &other) const { return rich_compare(other, Py_LT); }
bool operator<=(object_api const &other) const { return rich_compare(other, Py_LE); }
- bool operator>(object_api const &other) const { return rich_compare(other, Py_GT); }
+ bool operator>(object_api const &other) const { return rich_compare(other, Py_GT); }
bool operator>=(object_api const &other) const { return rich_compare(other, Py_GE); }
object operator-() const;
object operator~() const;
object operator+(object_api const &other) const;
- object operator+=(object_api const &other) const;
+ object operator+=(object_api const &other);
object operator-(object_api const &other) const;
- object operator-=(object_api const &other) const;
+ object operator-=(object_api const &other);
object operator*(object_api const &other) const;
- object operator*=(object_api const &other) const;
+ object operator*=(object_api const &other);
object operator/(object_api const &other) const;
- object operator/=(object_api const &other) const;
+ object operator/=(object_api const &other);
object operator|(object_api const &other) const;
- object operator|=(object_api const &other) const;
+ object operator|=(object_api const &other);
object operator&(object_api const &other) const;
- object operator&=(object_api const &other) const;
+ object operator&=(object_api const &other);
object operator^(object_api const &other) const;
- object operator^=(object_api const &other) const;
+ object operator^=(object_api const &other);
object operator<<(object_api const &other) const;
- object operator<<=(object_api const &other) const;
+ object operator<<=(object_api const &other);
object operator>>(object_api const &other) const;
- object operator>>=(object_api const &other) const;
+ object operator>>=(object_api const &other);
PYBIND11_DEPRECATED("Use py::str(obj) instead")
pybind11::str str() const;
@@ -153,15 +184,25 @@ public:
/// Return the object's current reference count
int ref_count() const { return static_cast<int>(Py_REFCNT(derived().ptr())); }
- // TODO PYBIND11_DEPRECATED("Call py::type::handle_of(h) or py::type::of(h) instead of h.get_type()")
+ // TODO PYBIND11_DEPRECATED(
+ // "Call py::type::handle_of(h) or py::type::of(h) instead of h.get_type()")
handle get_type() const;
private:
bool rich_compare(object_api const &other, int value) const;
};
+template <typename T>
+using is_pyobj_ptr_or_nullptr_t = detail::any_of<std::is_same<T, PyObject *>,
+ std::is_same<T, PyObject *const>,
+ std::is_same<T, std::nullptr_t>>;
+
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)
@@ -177,8 +218,24 @@ class handle : public detail::object_api<handle> {
public:
/// The default constructor creates a handle with a ``nullptr``-valued pointer
handle() = default;
- /// Creates a ``handle`` from the given raw Python object pointer
- handle(PyObject *ptr) : m_ptr(ptr) { } // Allow implicit conversion from PyObject*
+
+ /// Enable implicit conversion from ``PyObject *`` and ``nullptr``.
+ /// Not using ``handle(PyObject *ptr)`` to avoid implicit conversion from ``0``.
+ template <typename T,
+ detail::enable_if_t<detail::is_pyobj_ptr_or_nullptr_t<T>::value, int> = 0>
+ // NOLINTNEXTLINE(google-explicit-constructor)
+ handle(T ptr) : m_ptr(ptr) {}
+
+ /// Enable implicit conversion through ``T::operator PyObject *()``.
+ template <
+ typename T,
+ detail::enable_if_t<detail::all_of<detail::none_of<std::is_base_of<handle, T>,
+ detail::is_pyobj_ptr_or_nullptr_t<T>>,
+ std::is_convertible<T, PyObject *>>::value,
+ int>
+ = 0>
+ // NOLINTNEXTLINE(google-explicit-constructor)
+ handle(T &obj) : m_ptr(obj) {}
/// Return the underlying ``PyObject *`` pointer
PyObject *ptr() const { return m_ptr; }
@@ -189,20 +246,40 @@ public:
preferable to use the `object` class which derives from `handle` and calls
this function automatically. Returns a reference to itself.
\endrst */
- const handle& inc_ref() const & { Py_XINCREF(m_ptr); return *this; }
+ const handle &inc_ref() const & {
+#ifdef PYBIND11_HANDLE_REF_DEBUG
+ inc_ref_counter(1);
+#endif
+#ifdef PYBIND11_ASSERT_GIL_HELD_INCREF_DECREF
+ if (m_ptr != nullptr && !PyGILState_Check()) {
+ throw_gilstate_error("pybind11::handle::inc_ref()");
+ }
+#endif
+ Py_XINCREF(m_ptr);
+ return *this;
+ }
/** \rst
Manually decrease the reference count of the Python object. Usually, it is
preferable to use the `object` class which derives from `handle` and calls
this function automatically. Returns a reference to itself.
\endrst */
- const handle& dec_ref() const & { Py_XDECREF(m_ptr); return *this; }
+ const handle &dec_ref() const & {
+#ifdef PYBIND11_ASSERT_GIL_HELD_INCREF_DECREF
+ if (m_ptr != nullptr && !PyGILState_Check()) {
+ throw_gilstate_error("pybind11::handle::dec_ref()");
+ }
+#endif
+ Py_XDECREF(m_ptr);
+ return *this;
+ }
/** \rst
Attempt to cast the Python object into the given C++ type. A `cast_error`
will be throw upon failure.
\endrst */
- template <typename T> T cast() const;
+ template <typename T>
+ T cast() const;
/// Return ``true`` when the `handle` wraps a valid Python object
explicit operator bool() const { return m_ptr != nullptr; }
/** \rst
@@ -215,8 +292,41 @@ public:
bool operator!=(const handle &h) const { return m_ptr != h.m_ptr; }
PYBIND11_DEPRECATED("Use handle::operator bool() instead")
bool check() const { return m_ptr != nullptr; }
+
protected:
PyObject *m_ptr = nullptr;
+
+private:
+#ifdef PYBIND11_ASSERT_GIL_HELD_INCREF_DECREF
+ void throw_gilstate_error(const std::string &function_name) const {
+ fprintf(
+ stderr,
+ "%s is being called while the GIL is either not held or invalid. Please see "
+ "https://pybind11.readthedocs.io/en/stable/advanced/"
+ "misc.html#common-sources-of-global-interpreter-lock-errors for debugging advice.\n",
+ function_name.c_str());
+ fflush(stderr);
+ if (Py_TYPE(m_ptr)->tp_name != nullptr) {
+ fprintf(stderr,
+ "The failing %s call was triggered on a %s object.\n",
+ function_name.c_str(),
+ Py_TYPE(m_ptr)->tp_name);
+ fflush(stderr);
+ }
+ throw std::runtime_error(function_name + " PyGILState_Check() failure.");
+ }
+#endif
+
+#ifdef PYBIND11_HANDLE_REF_DEBUG
+ 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
@@ -233,11 +343,15 @@ class object : public handle {
public:
object() = default;
PYBIND11_DEPRECATED("Use reinterpret_borrow<object>() or reinterpret_steal<object>()")
- object(handle h, bool is_borrowed) : handle(h) { if (is_borrowed) inc_ref(); }
+ object(handle h, bool is_borrowed) : handle(h) {
+ if (is_borrowed) {
+ inc_ref();
+ }
+ }
/// Copy constructor; always increases the reference count
object(const object &o) : handle(o) { inc_ref(); }
/// Move constructor; steals the object from ``other`` and preserves its reference count
- object(object &&other) noexcept { m_ptr = other.m_ptr; other.m_ptr = nullptr; }
+ object(object &&other) noexcept : handle(other) { other.m_ptr = nullptr; }
/// Destructor; automatically calls `handle::dec_ref()`
~object() { dec_ref(); }
@@ -247,19 +361,25 @@ public:
Python object.
\endrst */
handle release() {
- PyObject *tmp = m_ptr;
- m_ptr = nullptr;
- return handle(tmp);
+ PyObject *tmp = m_ptr;
+ m_ptr = nullptr;
+ return handle(tmp);
}
- object& operator=(const object &other) {
- other.inc_ref();
- dec_ref();
- m_ptr = other.m_ptr;
+ object &operator=(const object &other) {
+ // Skip inc_ref and dec_ref if both objects are the same
+ if (!this->is(other)) {
+ other.inc_ref();
+ // Use temporary variable to ensure `*this` remains valid while
+ // `Py_XDECREF` executes, in case `*this` is accessible from Python.
+ handle temp(m_ptr);
+ m_ptr = other.m_ptr;
+ temp.dec_ref();
+ }
return *this;
}
- object& operator=(object &&other) noexcept {
+ object &operator=(object &&other) noexcept {
if (this != &other) {
handle temp(m_ptr);
m_ptr = other.m_ptr;
@@ -269,23 +389,43 @@ public:
return *this;
}
+#define PYBIND11_INPLACE_OP(iop) \
+ object iop(object_api const &other) { return operator=(handle::iop(other)); }
+
+ PYBIND11_INPLACE_OP(operator+=)
+ PYBIND11_INPLACE_OP(operator-=)
+ PYBIND11_INPLACE_OP(operator*=)
+ PYBIND11_INPLACE_OP(operator/=)
+ PYBIND11_INPLACE_OP(operator|=)
+ PYBIND11_INPLACE_OP(operator&=)
+ PYBIND11_INPLACE_OP(operator^=)
+ PYBIND11_INPLACE_OP(operator<<=)
+ PYBIND11_INPLACE_OP(operator>>=)
+#undef PYBIND11_INPLACE_OP
+
// Calling cast() on an object lvalue just copies (via handle::cast)
- template <typename T> T cast() const &;
+ template <typename T>
+ T cast() const &;
// Calling on an object rvalue does a move, if needed and/or possible
- template <typename T> T cast() &&;
+ template <typename T>
+ T cast() &&;
protected:
// Tags for choosing constructors from raw PyObject *
- struct borrowed_t { };
- struct stolen_t { };
+ struct borrowed_t {};
+ struct stolen_t {};
- template <typename T> friend T reinterpret_borrow(handle);
- template <typename T> friend T reinterpret_steal(handle);
+ /// @cond BROKEN
+ template <typename T>
+ friend T reinterpret_borrow(handle);
+ template <typename T>
+ friend T reinterpret_steal(handle);
+ /// @endcond
public:
// Only accessible from derived classes and the reinterpret_* functions
object(handle h, borrowed_t) : handle(h) { inc_ref(); }
- object(handle h, stolen_t) : handle(h) { }
+ object(handle h, stolen_t) : handle(h) {}
};
/** \rst
@@ -301,7 +441,10 @@ public:
// or
py::tuple t = reinterpret_borrow<py::tuple>(p); // <-- `p` must be already be a `tuple`
\endrst */
-template <typename T> T reinterpret_borrow(handle h) { return {h, object::borrowed_t{}}; }
+template <typename T>
+T reinterpret_borrow(handle h) {
+ return {h, object::borrowed_t{}};
+}
/** \rst
Like `reinterpret_borrow`, but steals the reference.
@@ -311,44 +454,297 @@ template <typename T> T reinterpret_borrow(handle h) { return {h, object::borrow
PyObject *p = PyObject_Str(obj);
py::str s = reinterpret_steal<py::str>(p); // <-- `p` must be already be a `str`
\endrst */
-template <typename T> T reinterpret_steal(handle h) { return {h, object::stolen_t{}}; }
+template <typename T>
+T reinterpret_steal(handle h) {
+ return {h, object::stolen_t{}};
+}
PYBIND11_NAMESPACE_BEGIN(detail)
-inline std::string error_string();
-PYBIND11_NAMESPACE_END(detail)
-/// Fetch and hold an error which was already set in Python. An instance of this is typically
-/// thrown to propagate python-side errors back through C++ which can either be caught manually or
-/// else falls back to the function dispatcher (which then raises the captured error back to
-/// python).
-class error_already_set : public std::runtime_error {
-public:
- /// Constructs a new exception from the current Python error indicator, if any. The current
- /// Python error indicator will be cleared.
- error_already_set() : std::runtime_error(detail::error_string()) {
+// Equivalent to obj.__class__.__name__ (or obj.__name__ if obj is a class).
+inline const char *obj_class_name(PyObject *obj) {
+ if (PyType_Check(obj)) {
+ return reinterpret_cast<PyTypeObject *>(obj)->tp_name;
+ }
+ return Py_TYPE(obj)->tp_name;
+}
+
+std::string error_string();
+
+// The code in this struct is very unusual, to minimize the chances of
+// masking bugs (elsewhere) by errors during the error handling (here).
+// This is meant to be a lifeline for troubleshooting long-running processes
+// that crash under conditions that are virtually impossible to reproduce.
+// Low-level implementation alternatives are preferred to higher-level ones
+// that might raise cascading exceptions. Last-ditch-kind-of attempts are made
+// to report as much of the original error as possible, even if there are
+// secondary issues obtaining some of the details.
+struct error_fetch_and_normalize {
+ // This comment only applies to Python <= 3.11:
+ // Immediate normalization is long-established behavior (starting with
+ // https://github.com/pybind/pybind11/commit/135ba8deafb8bf64a15b24d1513899eb600e2011
+ // from Sep 2016) and safest. Normalization could be deferred, but this could mask
+ // errors elsewhere, the performance gain is very minor in typical situations
+ // (usually the dominant bottleneck is EH unwinding), and the implementation here
+ // would be more complex.
+ // Starting with Python 3.12, PyErr_Fetch() normalizes exceptions immediately.
+ // Any errors during normalization are tracked under __notes__.
+ explicit error_fetch_and_normalize(const char *called) {
PyErr_Fetch(&m_type.ptr(), &m_value.ptr(), &m_trace.ptr());
+ if (!m_type) {
+ pybind11_fail("Internal error: " + std::string(called)
+ + " called while "
+ "Python error indicator not set.");
+ }
+ const char *exc_type_name_orig = detail::obj_class_name(m_type.ptr());
+ if (exc_type_name_orig == nullptr) {
+ pybind11_fail("Internal error: " + std::string(called)
+ + " failed to obtain the name "
+ "of the original active exception type.");
+ }
+ m_lazy_error_string = exc_type_name_orig;
+#if PY_VERSION_HEX >= 0x030C0000
+ // The presence of __notes__ is likely due to exception normalization
+ // errors, although that is not necessarily true, therefore insert a
+ // hint only:
+ if (PyObject_HasAttrString(m_value.ptr(), "__notes__")) {
+ m_lazy_error_string += "[WITH __notes__]";
+ }
+#else
+ // PyErr_NormalizeException() may change the exception type if there are cascading
+ // failures. This can potentially be extremely confusing.
+ PyErr_NormalizeException(&m_type.ptr(), &m_value.ptr(), &m_trace.ptr());
+ if (m_type.ptr() == nullptr) {
+ pybind11_fail("Internal error: " + std::string(called)
+ + " failed to normalize the "
+ "active exception.");
+ }
+ const char *exc_type_name_norm = detail::obj_class_name(m_type.ptr());
+ if (exc_type_name_norm == nullptr) {
+ pybind11_fail("Internal error: " + std::string(called)
+ + " failed to obtain the name "
+ "of the normalized active exception type.");
+ }
+# if defined(PYPY_VERSION_NUM) && PYPY_VERSION_NUM < 0x07030a00
+ // This behavior runs the risk of masking errors in the error handling, but avoids a
+ // conflict with PyPy, which relies on the normalization here to change OSError to
+ // FileNotFoundError (https://github.com/pybind/pybind11/issues/4075).
+ m_lazy_error_string = exc_type_name_norm;
+# else
+ if (exc_type_name_norm != m_lazy_error_string) {
+ std::string msg = std::string(called)
+ + ": MISMATCH of original and normalized "
+ "active exception types: ";
+ msg += "ORIGINAL ";
+ msg += m_lazy_error_string;
+ msg += " REPLACED BY ";
+ msg += exc_type_name_norm;
+ msg += ": " + format_value_and_trace();
+ pybind11_fail(msg);
+ }
+# endif
+#endif
+ }
+
+ error_fetch_and_normalize(const error_fetch_and_normalize &) = delete;
+ error_fetch_and_normalize(error_fetch_and_normalize &&) = delete;
+
+ std::string format_value_and_trace() const {
+ std::string result;
+ std::string message_error_string;
+ if (m_value) {
+ auto value_str = reinterpret_steal<object>(PyObject_Str(m_value.ptr()));
+ constexpr const char *message_unavailable_exc
+ = "<MESSAGE UNAVAILABLE DUE TO ANOTHER EXCEPTION>";
+ if (!value_str) {
+ message_error_string = detail::error_string();
+ result = message_unavailable_exc;
+ } else {
+ // Not using `value_str.cast<std::string>()`, to not potentially throw a secondary
+ // error_already_set that will then result in process termination (#4288).
+ auto value_bytes = reinterpret_steal<object>(
+ PyUnicode_AsEncodedString(value_str.ptr(), "utf-8", "backslashreplace"));
+ if (!value_bytes) {
+ message_error_string = detail::error_string();
+ result = message_unavailable_exc;
+ } else {
+ char *buffer = nullptr;
+ Py_ssize_t length = 0;
+ if (PyBytes_AsStringAndSize(value_bytes.ptr(), &buffer, &length) == -1) {
+ message_error_string = detail::error_string();
+ result = message_unavailable_exc;
+ } else {
+ result = std::string(buffer, static_cast<std::size_t>(length));
+ }
+ }
+ }
+#if PY_VERSION_HEX >= 0x030B0000
+ auto notes
+ = reinterpret_steal<object>(PyObject_GetAttrString(m_value.ptr(), "__notes__"));
+ if (!notes) {
+ PyErr_Clear(); // No notes is good news.
+ } else {
+ auto len_notes = PyList_Size(notes.ptr());
+ if (len_notes < 0) {
+ result += "\nFAILURE obtaining len(__notes__): " + detail::error_string();
+ } else {
+ result += "\n__notes__ (len=" + std::to_string(len_notes) + "):";
+ for (ssize_t i = 0; i < len_notes; i++) {
+ PyObject *note = PyList_GET_ITEM(notes.ptr(), i);
+ auto note_bytes = reinterpret_steal<object>(
+ PyUnicode_AsEncodedString(note, "utf-8", "backslashreplace"));
+ if (!note_bytes) {
+ result += "\nFAILURE obtaining __notes__[" + std::to_string(i)
+ + "]: " + detail::error_string();
+ } else {
+ char *buffer = nullptr;
+ Py_ssize_t length = 0;
+ if (PyBytes_AsStringAndSize(note_bytes.ptr(), &buffer, &length)
+ == -1) {
+ result += "\nFAILURE formatting __notes__[" + std::to_string(i)
+ + "]: " + detail::error_string();
+ } else {
+ result += '\n';
+ result += std::string(buffer, static_cast<std::size_t>(length));
+ }
+ }
+ }
+ }
+ }
+#endif
+ } else {
+ result = "<MESSAGE UNAVAILABLE>";
+ }
+ if (result.empty()) {
+ result = "<EMPTY MESSAGE>";
+ }
+
+ bool have_trace = false;
+ if (m_trace) {
+#if !defined(PYPY_VERSION)
+ auto *tb = reinterpret_cast<PyTracebackObject *>(m_trace.ptr());
+
+ // Get the deepest trace possible.
+ while (tb->tb_next) {
+ tb = tb->tb_next;
+ }
+
+ PyFrameObject *frame = tb->tb_frame;
+ Py_XINCREF(frame);
+ result += "\n\nAt:\n";
+ while (frame) {
+# if PY_VERSION_HEX >= 0x030900B1
+ PyCodeObject *f_code = PyFrame_GetCode(frame);
+# else
+ PyCodeObject *f_code = frame->f_code;
+ Py_INCREF(f_code);
+# endif
+ int lineno = PyFrame_GetLineNumber(frame);
+ result += " ";
+ result += handle(f_code->co_filename).cast<std::string>();
+ result += '(';
+ result += std::to_string(lineno);
+ result += "): ";
+ result += handle(f_code->co_name).cast<std::string>();
+ result += '\n';
+ Py_DECREF(f_code);
+# if PY_VERSION_HEX >= 0x030900B1
+ auto *b_frame = PyFrame_GetBack(frame);
+# else
+ auto *b_frame = frame->f_back;
+ Py_XINCREF(b_frame);
+# endif
+ Py_DECREF(frame);
+ frame = b_frame;
+ }
+
+ have_trace = true;
+#endif //! defined(PYPY_VERSION)
+ }
+
+ if (!message_error_string.empty()) {
+ if (!have_trace) {
+ result += '\n';
+ }
+ result += "\nMESSAGE UNAVAILABLE DUE TO EXCEPTION: " + message_error_string;
+ }
+
+ return result;
}
- error_already_set(const error_already_set &) = default;
- error_already_set(error_already_set &&) = default;
+ std::string const &error_string() const {
+ if (!m_lazy_error_string_completed) {
+ m_lazy_error_string += ": " + format_value_and_trace();
+ m_lazy_error_string_completed = true;
+ }
+ return m_lazy_error_string;
+ }
+
+ void restore() {
+ if (m_restore_called) {
+ pybind11_fail("Internal error: pybind11::detail::error_fetch_and_normalize::restore() "
+ "called a second time. ORIGINAL ERROR: "
+ + error_string());
+ }
+ PyErr_Restore(m_type.inc_ref().ptr(), m_value.inc_ref().ptr(), m_trace.inc_ref().ptr());
+ m_restore_called = true;
+ }
+
+ bool matches(handle exc) const {
+ return (PyErr_GivenExceptionMatches(m_type.ptr(), exc.ptr()) != 0);
+ }
+
+ // Not protecting these for simplicity.
+ object m_type, m_value, m_trace;
+
+private:
+ // Only protecting invariants.
+ mutable std::string m_lazy_error_string;
+ mutable bool m_lazy_error_string_completed = false;
+ mutable bool m_restore_called = false;
+};
- inline ~error_already_set() override;
+inline std::string error_string() {
+ return error_fetch_and_normalize("pybind11::detail::error_string").error_string();
+}
- /// 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).
- void restore() { PyErr_Restore(m_type.release().ptr(), m_value.release().ptr(), m_trace.release().ptr()); }
+PYBIND11_NAMESPACE_END(detail)
- /// If it is impossible to raise the currently-held error, such as in destructor, we can write
- /// it out using Python's unraisable hook (sys.unraisablehook). The error context should be
- /// some object whose repr() helps identify the location of the error. Python already knows the
- /// type and value of the error, so there is no need to repeat that. For example, __func__ could
- /// be helpful. After this call, the current object no longer stores the error variables,
- /// and neither does Python.
+/// Fetch and hold an error which was already set in Python. An instance of this is typically
+/// thrown to propagate python-side errors back through C++ which can either be caught manually or
+/// else falls back to the function dispatcher (which then raises the captured error back to
+/// python).
+class PYBIND11_EXPORT_EXCEPTION error_already_set : public std::exception {
+public:
+ /// Fetches the current Python exception (using PyErr_Fetch()), which will clear the
+ /// current Python error indicator.
+ error_already_set()
+ : m_fetched_error{new detail::error_fetch_and_normalize("pybind11::error_already_set"),
+ m_fetched_error_deleter} {}
+
+ /// The what() result is built lazily on demand.
+ /// WARNING: This member function needs to acquire the Python GIL. This can lead to
+ /// crashes (undefined behavior) if the Python interpreter is finalizing.
+ const char *what() const noexcept override;
+
+ /// Restores the currently-held Python error (which will clear the Python error indicator first
+ /// if already set).
+ /// 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() { m_fetched_error->restore(); }
+
+ /// If it is impossible to raise the currently-held error, such as in a destructor, we can
+ /// write it out using Python's unraisable hook (`sys.unraisablehook`). The error context
+ /// should be some object whose `repr()` helps identify the location of the error. Python
+ /// already knows the type and value of the error, so there is no need to repeat that.
void discard_as_unraisable(object err_context) {
restore();
PyErr_WriteUnraisable(err_context.ptr());
}
+ /// 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)));
}
@@ -360,17 +756,57 @@ public:
/// Check if the currently trapped error type matches the given Python exception class (or a
/// subclass thereof). May also be passed a tuple to search for any exception class matches in
/// the given tuple.
- bool matches(handle exc) const { return PyErr_GivenExceptionMatches(m_type.ptr(), exc.ptr()); }
+ bool matches(handle exc) const { return m_fetched_error->matches(exc); }
- const object& type() const { return m_type; }
- const object& value() const { return m_value; }
- const object& trace() const { return m_trace; }
+ const object &type() const { return m_fetched_error->m_type; }
+ const object &value() const { return m_fetched_error->m_value; }
+ const object &trace() const { return m_fetched_error->m_trace; }
private:
- object m_type, m_value, m_trace;
+ std::shared_ptr<detail::error_fetch_and_normalize> m_fetched_error;
+
+ /// WARNING: This custom deleter needs to acquire the Python GIL. This can lead to
+ /// crashes (undefined behavior) if the Python interpreter is finalizing.
+ static void m_fetched_error_deleter(detail::error_fetch_and_normalize *raw_ptr);
};
-/** \defgroup python_builtins _
+/// Replaces the current Python error indicator with the chosen error, performing a
+/// 'raise from' to indicate that the chosen error was caused by the original error.
+inline void raise_from(PyObject *type, const char *message) {
+ // Based on _PyErr_FormatVFromCause:
+ // https://github.com/python/cpython/blob/467ab194fc6189d9f7310c89937c51abeac56839/Python/errors.c#L405
+ // See https://github.com/pybind/pybind11/pull/2112 for details.
+ PyObject *exc = nullptr, *val = nullptr, *val2 = nullptr, *tb = nullptr;
+
+ assert(PyErr_Occurred());
+ PyErr_Fetch(&exc, &val, &tb);
+ PyErr_NormalizeException(&exc, &val, &tb);
+ if (tb != nullptr) {
+ PyException_SetTraceback(val, tb);
+ Py_DECREF(tb);
+ }
+ Py_DECREF(exc);
+ assert(!PyErr_Occurred());
+
+ PyErr_SetString(type, message);
+
+ PyErr_Fetch(&exc, &val2, &tb);
+ PyErr_NormalizeException(&exc, &val2, &tb);
+ Py_INCREF(val);
+ PyException_SetCause(val2, val);
+ PyException_SetContext(val2, val);
+ PyErr_Restore(exc, val2, tb);
+}
+
+/// Sets the current Python error indicator with the chosen error, performing a 'raise from'
+/// from the error contained in error_already_set to indicate that the chosen error was
+/// caused by the original error.
+inline void raise_from(error_already_set &err, PyObject *type, const char *message) {
+ err.restore();
+ raise_from(type, message);
+}
+
+/** \defgroup python_builtins const_name
Unless stated otherwise, the following C++ functions behave the same
as their Python counterparts.
*/
@@ -381,20 +817,29 @@ private:
`object` or a class which was exposed to Python as ``py::class_<T>``.
\endrst */
template <typename T, detail::enable_if_t<std::is_base_of<object, T>::value, int> = 0>
-bool isinstance(handle obj) { return T::check_(obj); }
+bool isinstance(handle obj) {
+ return T::check_(obj);
+}
template <typename T, detail::enable_if_t<!std::is_base_of<object, T>::value, int> = 0>
-bool isinstance(handle obj) { return detail::isinstance_generic(obj, typeid(T)); }
+bool isinstance(handle obj) {
+ return detail::isinstance_generic(obj, typeid(T));
+}
-template <> inline bool isinstance<handle>(handle) = delete;
-template <> inline bool isinstance<object>(handle obj) { return obj.ptr() != nullptr; }
+template <>
+inline bool isinstance<handle>(handle) = delete;
+template <>
+inline bool isinstance<object>(handle obj) {
+ return obj.ptr() != nullptr;
+}
/// \ingroup python_builtins
/// Return true if ``obj`` is an instance of the ``type``.
inline bool isinstance(handle obj, handle type) {
const auto result = PyObject_IsInstance(obj.ptr(), type.ptr());
- if (result == -1)
+ if (result == -1) {
throw error_already_set();
+ }
return result != 0;
}
@@ -409,54 +854,66 @@ inline bool hasattr(handle obj, const char *name) {
}
inline void delattr(handle obj, handle name) {
- if (PyObject_DelAttr(obj.ptr(), name.ptr()) != 0) { throw error_already_set(); }
+ if (PyObject_DelAttr(obj.ptr(), name.ptr()) != 0) {
+ throw error_already_set();
+ }
}
inline void delattr(handle obj, const char *name) {
- if (PyObject_DelAttrString(obj.ptr(), name) != 0) { throw error_already_set(); }
+ if (PyObject_DelAttrString(obj.ptr(), name) != 0) {
+ throw error_already_set();
+ }
}
inline object getattr(handle obj, handle name) {
PyObject *result = PyObject_GetAttr(obj.ptr(), name.ptr());
- if (!result) { throw error_already_set(); }
+ if (!result) {
+ throw error_already_set();
+ }
return reinterpret_steal<object>(result);
}
inline object getattr(handle obj, const char *name) {
PyObject *result = PyObject_GetAttrString(obj.ptr(), name);
- if (!result) { throw error_already_set(); }
+ if (!result) {
+ throw error_already_set();
+ }
return reinterpret_steal<object>(result);
}
inline object getattr(handle obj, handle name, handle default_) {
if (PyObject *result = PyObject_GetAttr(obj.ptr(), name.ptr())) {
return reinterpret_steal<object>(result);
- } else {
- PyErr_Clear();
- return reinterpret_borrow<object>(default_);
}
+ PyErr_Clear();
+ return reinterpret_borrow<object>(default_);
}
inline object getattr(handle obj, const char *name, handle default_) {
if (PyObject *result = PyObject_GetAttrString(obj.ptr(), name)) {
return reinterpret_steal<object>(result);
- } else {
- PyErr_Clear();
- return reinterpret_borrow<object>(default_);
}
+ PyErr_Clear();
+ return reinterpret_borrow<object>(default_);
}
inline void setattr(handle obj, handle name, handle value) {
- if (PyObject_SetAttr(obj.ptr(), name.ptr(), value.ptr()) != 0) { throw error_already_set(); }
+ if (PyObject_SetAttr(obj.ptr(), name.ptr(), value.ptr()) != 0) {
+ throw error_already_set();
+ }
}
inline void setattr(handle obj, const char *name, handle value) {
- if (PyObject_SetAttrString(obj.ptr(), name, value.ptr()) != 0) { throw error_already_set(); }
+ if (PyObject_SetAttrString(obj.ptr(), name, value.ptr()) != 0) {
+ throw error_already_set();
+ }
}
inline ssize_t hash(handle obj) {
auto h = PyObject_Hash(obj.ptr());
- if (h == -1) { throw error_already_set(); }
+ if (h == -1) {
+ throw error_already_set();
+ }
return h;
}
@@ -465,68 +922,112 @@ inline ssize_t hash(handle obj) {
PYBIND11_NAMESPACE_BEGIN(detail)
inline handle get_function(handle value) {
if (value) {
-#if PY_MAJOR_VERSION >= 3
- if (PyInstanceMethod_Check(value.ptr()))
+ if (PyInstanceMethod_Check(value.ptr())) {
value = PyInstanceMethod_GET_FUNCTION(value.ptr());
- else
-#endif
- if (PyMethod_Check(value.ptr()))
+ } else if (PyMethod_Check(value.ptr())) {
value = PyMethod_GET_FUNCTION(value.ptr());
+ }
}
return value;
}
-// Helper aliases/functions to support implicit casting of values given to python accessors/methods.
-// When given a pyobject, this simply returns the pyobject as-is; for other C++ type, the value goes
-// through pybind11::cast(obj) to convert it to an `object`.
+// Reimplementation of python's dict helper functions to ensure that exceptions
+// aren't swallowed (see #2862)
+
+// copied from cpython _PyDict_GetItemStringWithError
+inline PyObject *dict_getitemstring(PyObject *v, const char *key) {
+ PyObject *kv = nullptr, *rv = nullptr;
+ kv = PyUnicode_FromString(key);
+ if (kv == nullptr) {
+ throw error_already_set();
+ }
+
+ rv = PyDict_GetItemWithError(v, kv);
+ Py_DECREF(kv);
+ if (rv == nullptr && PyErr_Occurred()) {
+ throw error_already_set();
+ }
+ return rv;
+}
+
+inline PyObject *dict_getitem(PyObject *v, PyObject *key) {
+ PyObject *rv = PyDict_GetItemWithError(v, key);
+ if (rv == nullptr && PyErr_Occurred()) {
+ throw error_already_set();
+ }
+ return rv;
+}
+
+// Helper aliases/functions to support implicit casting of values given to python
+// accessors/methods. When given a pyobject, this simply returns the pyobject as-is; for other C++
+// type, the value goes through pybind11::cast(obj) to convert it to an `object`.
template <typename T, enable_if_t<is_pyobject<T>::value, int> = 0>
-auto object_or_cast(T &&o) -> decltype(std::forward<T>(o)) { return std::forward<T>(o); }
+auto object_or_cast(T &&o) -> decltype(std::forward<T>(o)) {
+ return std::forward<T>(o);
+}
// The following casting version is implemented in cast.h:
template <typename T, enable_if_t<!is_pyobject<T>::value, int> = 0>
object object_or_cast(T &&o);
// Match a PyObject*, which we want to convert directly to handle via its converting constructor
inline handle object_or_cast(PyObject *ptr) { return ptr; }
+PYBIND11_WARNING_PUSH
+PYBIND11_WARNING_DISABLE_MSVC(4522) // warning C4522: multiple assignment operators specified
template <typename Policy>
class accessor : public object_api<accessor<Policy>> {
using key_type = typename Policy::key_type;
public:
- accessor(handle obj, key_type key) : obj(obj), key(std::move(key)) { }
+ accessor(handle obj, key_type key) : obj(obj), key(std::move(key)) {}
accessor(const accessor &) = default;
- accessor(accessor &&) = default;
+ accessor(accessor &&) noexcept = default;
- // accessor overload required to override default assignment operator (templates are not allowed
- // to replace default compiler-generated assignments).
+ // accessor overload required to override default assignment operator (templates are not
+ // allowed to replace default compiler-generated assignments).
void operator=(const accessor &a) && { std::move(*this).operator=(handle(a)); }
void operator=(const accessor &a) & { operator=(handle(a)); }
- template <typename T> void operator=(T &&value) && {
+ template <typename T>
+ void operator=(T &&value) && {
Policy::set(obj, key, object_or_cast(std::forward<T>(value)));
}
- template <typename T> void operator=(T &&value) & {
- get_cache() = reinterpret_borrow<object>(object_or_cast(std::forward<T>(value)));
+ template <typename T>
+ void operator=(T &&value) & {
+ get_cache() = ensure_object(object_or_cast(std::forward<T>(value)));
}
template <typename T = Policy>
- PYBIND11_DEPRECATED("Use of obj.attr(...) as bool is deprecated in favor of pybind11::hasattr(obj, ...)")
- explicit operator enable_if_t<std::is_same<T, accessor_policies::str_attr>::value ||
- std::is_same<T, accessor_policies::obj_attr>::value, bool>() const {
+ PYBIND11_DEPRECATED(
+ "Use of obj.attr(...) as bool is deprecated in favor of pybind11::hasattr(obj, ...)")
+ explicit
+ operator enable_if_t<std::is_same<T, accessor_policies::str_attr>::value
+ || std::is_same<T, accessor_policies::obj_attr>::value,
+ bool>() const {
return hasattr(obj, key);
}
template <typename T = Policy>
PYBIND11_DEPRECATED("Use of obj[key] as bool is deprecated in favor of obj.contains(key)")
- explicit operator enable_if_t<std::is_same<T, accessor_policies::generic_item>::value, bool>() const {
+ explicit
+ operator enable_if_t<std::is_same<T, accessor_policies::generic_item>::value, bool>() const {
return obj.contains(key);
}
+ // NOLINTNEXTLINE(google-explicit-constructor)
operator object() const { return get_cache(); }
PyObject *ptr() const { return get_cache().ptr(); }
- template <typename T> T cast() const { return get_cache().template cast<T>(); }
+ template <typename T>
+ T cast() const {
+ return get_cache().template cast<T>();
+ }
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); }
+ if (!cache) {
+ cache = Policy::get(obj, key);
+ }
return cache;
}
@@ -535,6 +1036,7 @@ private:
key_type key;
mutable object cache;
};
+PYBIND11_WARNING_POP
PYBIND11_NAMESPACE_BEGIN(accessor_policies)
struct obj_attr {
@@ -554,27 +1056,35 @@ struct generic_item {
static object get(handle obj, handle key) {
PyObject *result = PyObject_GetItem(obj.ptr(), key.ptr());
- if (!result) { throw error_already_set(); }
+ if (!result) {
+ throw error_already_set();
+ }
return reinterpret_steal<object>(result);
}
static void set(handle obj, handle key, handle val) {
- if (PyObject_SetItem(obj.ptr(), key.ptr(), val.ptr()) != 0) { throw error_already_set(); }
+ if (PyObject_SetItem(obj.ptr(), key.ptr(), val.ptr()) != 0) {
+ throw error_already_set();
+ }
}
};
struct sequence_item {
using key_type = size_t;
- static object get(handle obj, size_t index) {
- PyObject *result = PySequence_GetItem(obj.ptr(), static_cast<ssize_t>(index));
- if (!result) { throw error_already_set(); }
+ template <typename IdxType, detail::enable_if_t<std::is_integral<IdxType>::value, int> = 0>
+ static object get(handle obj, const IdxType &index) {
+ PyObject *result = PySequence_GetItem(obj.ptr(), ssize_t_cast(index));
+ if (!result) {
+ throw error_already_set();
+ }
return reinterpret_steal<object>(result);
}
- static void set(handle obj, size_t index, handle val) {
+ template <typename IdxType, detail::enable_if_t<std::is_integral<IdxType>::value, int> = 0>
+ static void set(handle obj, const IdxType &index, handle val) {
// PySequence_SetItem does not steal a reference to 'val'
- if (PySequence_SetItem(obj.ptr(), static_cast<ssize_t>(index), val.ptr()) != 0) {
+ if (PySequence_SetItem(obj.ptr(), ssize_t_cast(index), val.ptr()) != 0) {
throw error_already_set();
}
}
@@ -583,15 +1093,19 @@ struct sequence_item {
struct list_item {
using key_type = size_t;
- static object get(handle obj, size_t index) {
- PyObject *result = PyList_GetItem(obj.ptr(), static_cast<ssize_t>(index));
- if (!result) { throw error_already_set(); }
+ template <typename IdxType, detail::enable_if_t<std::is_integral<IdxType>::value, int> = 0>
+ static object get(handle obj, const IdxType &index) {
+ PyObject *result = PyList_GetItem(obj.ptr(), ssize_t_cast(index));
+ if (!result) {
+ throw error_already_set();
+ }
return reinterpret_borrow<object>(result);
}
- static void set(handle obj, size_t index, handle val) {
+ template <typename IdxType, detail::enable_if_t<std::is_integral<IdxType>::value, int> = 0>
+ static void set(handle obj, const IdxType &index, handle val) {
// PyList_SetItem steals a reference to 'val'
- if (PyList_SetItem(obj.ptr(), static_cast<ssize_t>(index), val.inc_ref().ptr()) != 0) {
+ if (PyList_SetItem(obj.ptr(), ssize_t_cast(index), val.inc_ref().ptr()) != 0) {
throw error_already_set();
}
}
@@ -600,15 +1114,19 @@ struct list_item {
struct tuple_item {
using key_type = size_t;
- static object get(handle obj, size_t index) {
- PyObject *result = PyTuple_GetItem(obj.ptr(), static_cast<ssize_t>(index));
- if (!result) { throw error_already_set(); }
+ template <typename IdxType, detail::enable_if_t<std::is_integral<IdxType>::value, int> = 0>
+ static object get(handle obj, const IdxType &index) {
+ PyObject *result = PyTuple_GetItem(obj.ptr(), ssize_t_cast(index));
+ if (!result) {
+ throw error_already_set();
+ }
return reinterpret_borrow<object>(result);
}
- static void set(handle obj, size_t index, handle val) {
+ template <typename IdxType, detail::enable_if_t<std::is_integral<IdxType>::value, int> = 0>
+ static void set(handle obj, const IdxType &index, handle val) {
// PyTuple_SetItem steals a reference to 'val'
- if (PyTuple_SetItem(obj.ptr(), static_cast<ssize_t>(index), val.inc_ref().ptr()) != 0) {
+ if (PyTuple_SetItem(obj.ptr(), ssize_t_cast(index), val.inc_ref().ptr()) != 0) {
throw error_already_set();
}
}
@@ -628,28 +1146,56 @@ public:
using pointer = typename Policy::pointer;
generic_iterator() = default;
- generic_iterator(handle seq, ssize_t index) : Policy(seq, index) { }
+ generic_iterator(handle seq, ssize_t index) : Policy(seq, index) {}
+ // NOLINTNEXTLINE(readability-const-return-type) // PR #3263
reference operator*() const { return Policy::dereference(); }
+ // NOLINTNEXTLINE(readability-const-return-type) // PR #3263
reference operator[](difference_type n) const { return *(*this + n); }
pointer operator->() const { return **this; }
- It &operator++() { Policy::increment(); return *this; }
- It operator++(int) { auto copy = *this; Policy::increment(); return copy; }
- It &operator--() { Policy::decrement(); return *this; }
- It operator--(int) { auto copy = *this; Policy::decrement(); return copy; }
- It &operator+=(difference_type n) { Policy::advance(n); return *this; }
- It &operator-=(difference_type n) { Policy::advance(-n); return *this; }
+ It &operator++() {
+ Policy::increment();
+ return *this;
+ }
+ It operator++(int) {
+ auto copy = *this;
+ Policy::increment();
+ return copy;
+ }
+ It &operator--() {
+ Policy::decrement();
+ return *this;
+ }
+ It operator--(int) {
+ auto copy = *this;
+ Policy::decrement();
+ return copy;
+ }
+ It &operator+=(difference_type n) {
+ Policy::advance(n);
+ return *this;
+ }
+ It &operator-=(difference_type n) {
+ Policy::advance(-n);
+ return *this;
+ }
- friend It operator+(const It &a, difference_type n) { auto copy = a; return copy += n; }
+ friend It operator+(const It &a, difference_type n) {
+ auto copy = a;
+ return copy += n;
+ }
friend It operator+(difference_type n, const It &b) { return b + n; }
- friend It operator-(const It &a, difference_type n) { auto copy = a; return copy -= n; }
+ friend It operator-(const It &a, difference_type n) {
+ auto copy = a;
+ return copy -= n;
+ }
friend difference_type operator-(const It &a, const It &b) { return a.distance_to(b); }
friend bool operator==(const It &a, const It &b) { return a.equal(b); }
friend bool operator!=(const It &a, const It &b) { return !(a == b); }
- friend bool operator< (const It &a, const It &b) { return b - a > 0; }
- friend bool operator> (const It &a, const It &b) { return b < a; }
+ friend bool operator<(const It &a, const It &b) { return b - a > 0; }
+ friend bool operator>(const It &a, const It &b) { return b < a; }
friend bool operator>=(const It &a, const It &b) { return !(a < b); }
friend bool operator<=(const It &a, const It &b) { return !(a > b); }
};
@@ -660,7 +1206,8 @@ template <typename T>
struct arrow_proxy {
T value;
- arrow_proxy(T &&value) : value(std::move(value)) { }
+ // NOLINTNEXTLINE(google-explicit-constructor)
+ arrow_proxy(T &&value) noexcept : value(std::move(value)) {}
T *operator->() const { return &value; }
};
@@ -669,11 +1216,12 @@ class sequence_fast_readonly {
protected:
using iterator_category = std::random_access_iterator_tag;
using value_type = handle;
- using reference = const handle;
+ using reference = const handle; // PR #3263
using pointer = arrow_proxy<const handle>;
- sequence_fast_readonly(handle obj, ssize_t n) : ptr(PySequence_Fast_ITEMS(obj.ptr()) + n) { }
+ sequence_fast_readonly(handle obj, ssize_t n) : ptr(PySequence_Fast_ITEMS(obj.ptr()) + n) {}
+ // NOLINTNEXTLINE(readability-const-return-type) // PR #3263
reference dereference() const { return *ptr; }
void increment() { ++ptr; }
void decrement() { --ptr; }
@@ -693,7 +1241,7 @@ protected:
using reference = sequence_accessor;
using pointer = arrow_proxy<const sequence_accessor>;
- sequence_slow_readwrite(handle obj, ssize_t index) : obj(obj), index(index) { }
+ sequence_slow_readwrite(handle obj, ssize_t index) : obj(obj), index(index) {}
reference dereference() const { return {obj, static_cast<size_t>(index)}; }
void increment() { ++index; }
@@ -712,14 +1260,19 @@ class dict_readonly {
protected:
using iterator_category = std::forward_iterator_tag;
using value_type = std::pair<handle, handle>;
- using reference = const value_type;
+ using reference = const value_type; // PR #3263
using pointer = arrow_proxy<const value_type>;
dict_readonly() = default;
dict_readonly(handle obj, ssize_t pos) : obj(obj), pos(pos) { increment(); }
+ // NOLINTNEXTLINE(readability-const-return-type) // PR #3263
reference dereference() const { return {key, value}; }
- void increment() { if (!PyDict_Next(obj.ptr(), &pos, &key, &value)) { pos = -1; } }
+ void increment() {
+ if (PyDict_Next(obj.ptr(), &pos, &key, &value) == 0) {
+ pos = -1;
+ }
+ }
bool equal(const dict_readonly &b) const { return pos == b.pos; }
private:
@@ -745,38 +1298,47 @@ inline bool PyIterable_Check(PyObject *obj) {
if (iter) {
Py_DECREF(iter);
return true;
- } else {
- PyErr_Clear();
- return false;
}
+ PyErr_Clear();
+ return false;
}
inline bool PyNone_Check(PyObject *o) { return o == Py_None; }
inline bool PyEllipsis_Check(PyObject *o) { return o == Py_Ellipsis; }
-inline bool PyUnicode_Check_Permissive(PyObject *o) { return PyUnicode_Check(o) || PYBIND11_BYTES_CHECK(o); }
+#ifdef PYBIND11_STR_LEGACY_PERMISSIVE
+inline bool PyUnicode_Check_Permissive(PyObject *o) {
+ return PyUnicode_Check(o) || PYBIND11_BYTES_CHECK(o);
+}
+# define PYBIND11_STR_CHECK_FUN detail::PyUnicode_Check_Permissive
+#else
+# define PYBIND11_STR_CHECK_FUN PyUnicode_Check
+#endif
inline bool PyStaticMethod_Check(PyObject *o) { return o->ob_type == &PyStaticMethod_Type; }
class kwargs_proxy : public handle {
public:
- explicit kwargs_proxy(handle h) : handle(h) { }
+ explicit kwargs_proxy(handle h) : handle(h) {}
};
class args_proxy : public handle {
public:
- explicit args_proxy(handle h) : handle(h) { }
+ explicit args_proxy(handle h) : handle(h) {}
kwargs_proxy operator*() const { return kwargs_proxy(*this); }
};
/// Python argument categories (using PEP 448 terms)
-template <typename T> using is_keyword = std::is_base_of<arg, T>;
-template <typename T> using is_s_unpacking = std::is_same<args_proxy, T>; // * unpacking
-template <typename T> using is_ds_unpacking = std::is_same<kwargs_proxy, T>; // ** unpacking
-template <typename T> using is_positional = satisfies_none_of<T,
- is_keyword, is_s_unpacking, is_ds_unpacking
->;
-template <typename T> using is_keyword_or_ds = satisfies_any_of<T, is_keyword, is_ds_unpacking>;
+template <typename T>
+using is_keyword = std::is_base_of<arg, T>;
+template <typename T>
+using is_s_unpacking = std::is_same<args_proxy, T>; // * unpacking
+template <typename T>
+using is_ds_unpacking = std::is_same<kwargs_proxy, T>; // ** unpacking
+template <typename T>
+using is_positional = satisfies_none_of<T, is_keyword, is_s_unpacking, is_ds_unpacking>;
+template <typename T>
+using is_keyword_or_ds = satisfies_any_of<T, is_keyword, is_ds_unpacking>;
// Call argument collector forward declarations
template <return_value_policy policy = return_value_policy::automatic_reference>
@@ -790,44 +1352,60 @@ PYBIND11_NAMESPACE_END(detail)
// inheriting ctors: `using Parent::Parent`. It's not an option right now because
// the `using` statement triggers the parent deprecation warning even if the ctor
// isn't even used.
-#define PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \
- public: \
- PYBIND11_DEPRECATED("Use reinterpret_borrow<"#Name">() or reinterpret_steal<"#Name">()") \
- Name(handle h, bool is_borrowed) : Parent(is_borrowed ? Parent(h, borrowed_t{}) : Parent(h, stolen_t{})) { } \
- Name(handle h, borrowed_t) : Parent(h, borrowed_t{}) { } \
- Name(handle h, stolen_t) : Parent(h, stolen_t{}) { } \
- PYBIND11_DEPRECATED("Use py::isinstance<py::python_type>(obj) instead") \
- bool check() const { return m_ptr != nullptr && (bool) CheckFun(m_ptr); } \
- static bool check_(handle h) { return h.ptr() != nullptr && CheckFun(h.ptr()); } \
- template <typename Policy_> \
- Name(const ::pybind11::detail::accessor<Policy_> &a) : Name(object(a)) { }
-
-#define PYBIND11_OBJECT_CVT(Name, Parent, CheckFun, ConvertFun) \
- PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \
- /* This is deliberately not 'explicit' to allow implicit conversion from object: */ \
- Name(const object &o) \
- : Parent(check_(o) ? o.inc_ref().ptr() : ConvertFun(o.ptr()), stolen_t{}) \
- { if (!m_ptr) throw error_already_set(); } \
- Name(object &&o) \
- : Parent(check_(o) ? o.release().ptr() : ConvertFun(o.ptr()), stolen_t{}) \
- { if (!m_ptr) throw error_already_set(); }
-
-#define PYBIND11_OBJECT_CHECK_FAILED(Name, o_ptr) \
- ::pybind11::type_error("Object of type '" + \
- ::pybind11::detail::get_fully_qualified_tp_name(Py_TYPE(o_ptr)) + \
- "' is not an instance of '" #Name "'")
-
-#define PYBIND11_OBJECT(Name, Parent, CheckFun) \
- PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \
- /* This is deliberately not 'explicit' to allow implicit conversion from object: */ \
- Name(const object &o) : Parent(o) \
- { if (m_ptr && !check_(m_ptr)) throw PYBIND11_OBJECT_CHECK_FAILED(Name, m_ptr); } \
- Name(object &&o) : Parent(std::move(o)) \
- { if (m_ptr && !check_(m_ptr)) throw PYBIND11_OBJECT_CHECK_FAILED(Name, m_ptr); }
-
-#define PYBIND11_OBJECT_DEFAULT(Name, Parent, CheckFun) \
- PYBIND11_OBJECT(Name, Parent, CheckFun) \
- Name() : Parent() { }
+#define PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \
+public: \
+ PYBIND11_DEPRECATED("Use reinterpret_borrow<" #Name ">() or reinterpret_steal<" #Name ">()") \
+ Name(handle h, bool is_borrowed) \
+ : Parent(is_borrowed ? Parent(h, borrowed_t{}) : Parent(h, stolen_t{})) {} \
+ Name(handle h, borrowed_t) : Parent(h, borrowed_t{}) {} \
+ Name(handle h, stolen_t) : Parent(h, stolen_t{}) {} \
+ PYBIND11_DEPRECATED("Use py::isinstance<py::python_type>(obj) instead") \
+ bool check() const { return m_ptr != nullptr && (CheckFun(m_ptr) != 0); } \
+ static bool check_(handle h) { return h.ptr() != nullptr && CheckFun(h.ptr()); } \
+ template <typename Policy_> /* NOLINTNEXTLINE(google-explicit-constructor) */ \
+ Name(const ::pybind11::detail::accessor<Policy_> &a) : Name(object(a)) {}
+
+#define PYBIND11_OBJECT_CVT(Name, Parent, CheckFun, ConvertFun) \
+ PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \
+ /* This is deliberately not 'explicit' to allow implicit conversion from object: */ \
+ /* NOLINTNEXTLINE(google-explicit-constructor) */ \
+ Name(const object &o) \
+ : Parent(check_(o) ? o.inc_ref().ptr() : ConvertFun(o.ptr()), stolen_t{}) { \
+ if (!m_ptr) \
+ throw ::pybind11::error_already_set(); \
+ } \
+ /* NOLINTNEXTLINE(google-explicit-constructor) */ \
+ Name(object &&o) : Parent(check_(o) ? o.release().ptr() : ConvertFun(o.ptr()), stolen_t{}) { \
+ if (!m_ptr) \
+ throw ::pybind11::error_already_set(); \
+ }
+
+#define PYBIND11_OBJECT_CVT_DEFAULT(Name, Parent, CheckFun, ConvertFun) \
+ PYBIND11_OBJECT_CVT(Name, Parent, CheckFun, ConvertFun) \
+ Name() = default;
+
+#define PYBIND11_OBJECT_CHECK_FAILED(Name, o_ptr) \
+ ::pybind11::type_error("Object of type '" \
+ + ::pybind11::detail::get_fully_qualified_tp_name(Py_TYPE(o_ptr)) \
+ + "' is not an instance of '" #Name "'")
+
+#define PYBIND11_OBJECT(Name, Parent, CheckFun) \
+ PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \
+ /* This is deliberately not 'explicit' to allow implicit conversion from object: */ \
+ /* NOLINTNEXTLINE(google-explicit-constructor) */ \
+ Name(const object &o) : Parent(o) { \
+ if (m_ptr && !check_(m_ptr)) \
+ throw PYBIND11_OBJECT_CHECK_FAILED(Name, m_ptr); \
+ } \
+ /* NOLINTNEXTLINE(google-explicit-constructor) */ \
+ Name(object &&o) : Parent(std::move(o)) { \
+ if (m_ptr && !check_(m_ptr)) \
+ throw PYBIND11_OBJECT_CHECK_FAILED(Name, m_ptr); \
+ }
+
+#define PYBIND11_OBJECT_DEFAULT(Name, Parent, CheckFun) \
+ PYBIND11_OBJECT(Name, Parent, CheckFun) \
+ Name() = default;
/// \addtogroup pytypes
/// @{
@@ -845,12 +1423,12 @@ public:
using iterator_category = std::input_iterator_tag;
using difference_type = ssize_t;
using value_type = handle;
- using reference = const handle;
+ using reference = const handle; // PR #3263
using pointer = const handle *;
PYBIND11_OBJECT_DEFAULT(iterator, object, PyIter_Check)
- iterator& operator++() {
+ iterator &operator++() {
advance();
return *this;
}
@@ -861,15 +1439,19 @@ public:
return rv;
}
+ // NOLINTNEXTLINE(readability-const-return-type) // PR #3263
reference operator*() const {
if (m_ptr && !value.ptr()) {
- auto& self = const_cast<iterator &>(*this);
+ auto &self = const_cast<iterator &>(*this);
self.advance();
}
return value;
}
- pointer operator->() const { operator*(); return &value; }
+ pointer operator->() const {
+ operator*();
+ return &value;
+ }
/** \rst
The value which marks the end of the iteration. ``it == iterator::sentinel()``
@@ -892,21 +1474,21 @@ public:
private:
void advance() {
value = reinterpret_steal<object>(PyIter_Next(m_ptr));
- if (PyErr_Occurred()) { throw error_already_set(); }
+ if (value.ptr() == nullptr && PyErr_Occurred()) {
+ throw error_already_set();
+ }
}
private:
object value = {};
};
-
-
class type : public object {
public:
PYBIND11_OBJECT(type, object, PyType_Check)
/// Return a type handle from a handle or an object
- static handle handle_of(handle h) { return handle((PyObject*) Py_TYPE(h.ptr())); }
+ static handle handle_of(handle h) { return handle((PyObject *) Py_TYPE(h.ptr())); }
/// Return a type object from a handle or an object
static type of(handle h) { return type(type::handle_of(h), borrowed_t{}); }
@@ -915,14 +1497,16 @@ public:
/// Convert C++ type to handle if previously registered. Does not convert
/// standard types, like int, float. etc. yet.
/// See https://github.com/pybind/pybind11/issues/2486
- template<typename T>
+ template <typename T>
static handle handle_of();
/// Convert C++ type to type if previously registered. Does not convert
/// standard types, like int, float. etc. yet.
/// See https://github.com/pybind/pybind11/issues/2486
- template<typename T>
- static type of() {return type(type::handle_of<T>(), borrowed_t{}); }
+ template <typename T>
+ static type of() {
+ return type(type::handle_of<T>(), borrowed_t{});
+ }
};
class iterable : public object {
@@ -934,20 +1518,47 @@ class bytes;
class str : public object {
public:
- PYBIND11_OBJECT_CVT(str, object, detail::PyUnicode_Check_Permissive, raw_str)
-
- str(const char *c, size_t n)
- : object(PyUnicode_FromStringAndSize(c, (ssize_t) n), stolen_t{}) {
- if (!m_ptr) pybind11_fail("Could not allocate string object!");
+ PYBIND11_OBJECT_CVT(str, object, PYBIND11_STR_CHECK_FUN, raw_str)
+
+ template <typename SzType, detail::enable_if_t<std::is_integral<SzType>::value, int> = 0>
+ str(const char *c, const SzType &n)
+ : object(PyUnicode_FromStringAndSize(c, ssize_t_cast(n)), stolen_t{}) {
+ if (!m_ptr) {
+ if (PyErr_Occurred()) {
+ throw error_already_set();
+ }
+ pybind11_fail("Could not allocate string object!");
+ }
}
- // 'explicit' is explicitly omitted from the following constructors to allow implicit conversion to py::str from C++ string-like objects
- str(const char *c = "")
- : object(PyUnicode_FromString(c), stolen_t{}) {
- if (!m_ptr) pybind11_fail("Could not allocate string object!");
+ // 'explicit' is explicitly omitted from the following constructors to allow implicit
+ // conversion to py::str from C++ string-like objects
+ // NOLINTNEXTLINE(google-explicit-constructor)
+ str(const char *c = "") : object(PyUnicode_FromString(c), stolen_t{}) {
+ if (!m_ptr) {
+ if (PyErr_Occurred()) {
+ throw error_already_set();
+ }
+ pybind11_fail("Could not allocate string object!");
+ }
}
- str(const std::string &s) : str(s.data(), s.size()) { }
+ // NOLINTNEXTLINE(google-explicit-constructor)
+ str(const std::string &s) : str(s.data(), s.size()) {}
+
+#ifdef PYBIND11_HAS_STRING_VIEW
+ // enable_if is needed to avoid "ambiguous conversion" errors (see PR #3521).
+ template <typename T, detail::enable_if_t<std::is_same<T, std::string_view>::value, int> = 0>
+ // NOLINTNEXTLINE(google-explicit-constructor)
+ str(T s) : str(s.data(), s.size()) {}
+
+# ifdef PYBIND11_HAS_U8STRING
+ // reinterpret_cast here is safe (C++20 guarantees char8_t has the same size/alignment as char)
+ // NOLINTNEXTLINE(google-explicit-constructor)
+ str(std::u8string_view s) : str(reinterpret_cast<const char *>(s.data()), s.size()) {}
+# endif
+
+#endif
explicit str(const bytes &b);
@@ -955,19 +1566,26 @@ public:
Return a string representation of the object. This is analogous to
the ``str()`` function in Python.
\endrst */
- explicit str(handle h) : object(raw_str(h.ptr()), stolen_t{}) { if (!m_ptr) throw error_already_set(); }
+ explicit str(handle h) : object(raw_str(h.ptr()), stolen_t{}) {
+ if (!m_ptr) {
+ throw error_already_set();
+ }
+ }
+ // NOLINTNEXTLINE(google-explicit-constructor)
operator std::string() const {
object temp = *this;
if (PyUnicode_Check(m_ptr)) {
temp = reinterpret_steal<object>(PyUnicode_AsUTF8String(m_ptr));
- if (!temp)
- pybind11_fail("Unable to extract string contents! (encoding issue)");
+ if (!temp) {
+ throw error_already_set();
+ }
+ }
+ char *buffer = nullptr;
+ ssize_t length = 0;
+ if (PyBytes_AsStringAndSize(temp.ptr(), &buffer, &length) != 0) {
+ throw error_already_set();
}
- char *buffer;
- ssize_t length;
- if (PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), &buffer, &length))
- pybind11_fail("Unable to extract string contents! (invalid type)");
return std::string(buffer, (size_t) length);
}
@@ -980,11 +1598,6 @@ private:
/// Return string representation -- always returns a new reference, even if already a str
static PyObject *raw_str(PyObject *op) {
PyObject *str_value = PyObject_Str(op);
-#if PY_MAJOR_VERSION < 3
- if (!str_value) throw error_already_set();
- PyObject *unicode = PyUnicode_FromEncodedObject(str_value, "utf-8", nullptr);
- Py_XDECREF(str_value); str_value = unicode;
-#endif
return str_value;
}
};
@@ -1004,27 +1617,51 @@ public:
PYBIND11_OBJECT(bytes, object, PYBIND11_BYTES_CHECK)
// Allow implicit conversion:
- bytes(const char *c = "")
- : object(PYBIND11_BYTES_FROM_STRING(c), stolen_t{}) {
- if (!m_ptr) pybind11_fail("Could not allocate bytes object!");
+ // NOLINTNEXTLINE(google-explicit-constructor)
+ bytes(const char *c = "") : object(PYBIND11_BYTES_FROM_STRING(c), stolen_t{}) {
+ if (!m_ptr) {
+ pybind11_fail("Could not allocate bytes object!");
+ }
}
- bytes(const char *c, size_t n)
- : object(PYBIND11_BYTES_FROM_STRING_AND_SIZE(c, (ssize_t) n), stolen_t{}) {
- if (!m_ptr) pybind11_fail("Could not allocate bytes object!");
+ template <typename SzType, detail::enable_if_t<std::is_integral<SzType>::value, int> = 0>
+ bytes(const char *c, const SzType &n)
+ : object(PYBIND11_BYTES_FROM_STRING_AND_SIZE(c, ssize_t_cast(n)), stolen_t{}) {
+ if (!m_ptr) {
+ pybind11_fail("Could not allocate bytes object!");
+ }
}
// Allow implicit conversion:
- bytes(const std::string &s) : bytes(s.data(), s.size()) { }
+ // NOLINTNEXTLINE(google-explicit-constructor)
+ bytes(const std::string &s) : bytes(s.data(), s.size()) {}
explicit bytes(const pybind11::str &s);
- operator std::string() const {
- char *buffer;
- ssize_t length;
- if (PYBIND11_BYTES_AS_STRING_AND_SIZE(m_ptr, &buffer, &length))
- pybind11_fail("Unable to extract bytes contents!");
- return std::string(buffer, (size_t) length);
+ // NOLINTNEXTLINE(google-explicit-constructor)
+ operator std::string() const { return string_op<std::string>(); }
+
+#ifdef PYBIND11_HAS_STRING_VIEW
+ // enable_if is needed to avoid "ambiguous conversion" errors (see PR #3521).
+ template <typename T, detail::enable_if_t<std::is_same<T, std::string_view>::value, int> = 0>
+ // NOLINTNEXTLINE(google-explicit-constructor)
+ bytes(T s) : bytes(s.data(), s.size()) {}
+
+ // Obtain a string view that views the current `bytes` buffer value. Note that this is only
+ // valid so long as the `bytes` instance remains alive and so generally should not outlive the
+ // lifetime of the `bytes` instance.
+ // NOLINTNEXTLINE(google-explicit-constructor)
+ operator std::string_view() const { return string_op<std::string_view>(); }
+#endif
+private:
+ template <typename T>
+ T string_op() const {
+ char *buffer = nullptr;
+ ssize_t length = 0;
+ if (PyBytes_AsStringAndSize(m_ptr, &buffer, &length) != 0) {
+ throw error_already_set();
+ }
+ return {buffer, static_cast<size_t>(length)};
}
};
// Note: breathe >= 4.17.0 will fail to build docs if the below two constructors
@@ -1035,58 +1672,100 @@ inline bytes::bytes(const pybind11::str &s) {
object temp = s;
if (PyUnicode_Check(s.ptr())) {
temp = reinterpret_steal<object>(PyUnicode_AsUTF8String(s.ptr()));
- if (!temp)
- pybind11_fail("Unable to extract string contents! (encoding issue)");
+ if (!temp) {
+ throw error_already_set();
+ }
+ }
+ char *buffer = nullptr;
+ ssize_t length = 0;
+ if (PyBytes_AsStringAndSize(temp.ptr(), &buffer, &length) != 0) {
+ throw error_already_set();
}
- char *buffer;
- ssize_t length;
- if (PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), &buffer, &length))
- pybind11_fail("Unable to extract string contents! (invalid type)");
auto obj = reinterpret_steal<object>(PYBIND11_BYTES_FROM_STRING_AND_SIZE(buffer, length));
- if (!obj)
+ if (!obj) {
pybind11_fail("Could not allocate bytes object!");
+ }
m_ptr = obj.release().ptr();
}
-inline str::str(const bytes& b) {
- char *buffer;
- ssize_t length;
- if (PYBIND11_BYTES_AS_STRING_AND_SIZE(b.ptr(), &buffer, &length))
- pybind11_fail("Unable to extract bytes contents!");
- auto obj = reinterpret_steal<object>(PyUnicode_FromStringAndSize(buffer, (ssize_t) length));
- if (!obj)
+inline str::str(const bytes &b) {
+ char *buffer = nullptr;
+ ssize_t length = 0;
+ if (PyBytes_AsStringAndSize(b.ptr(), &buffer, &length) != 0) {
+ throw error_already_set();
+ }
+ auto obj = reinterpret_steal<object>(PyUnicode_FromStringAndSize(buffer, length));
+ if (!obj) {
+ if (PyErr_Occurred()) {
+ throw error_already_set();
+ }
pybind11_fail("Could not allocate string object!");
+ }
m_ptr = obj.release().ptr();
}
/// \addtogroup pytypes
/// @{
+class bytearray : public object {
+public:
+ PYBIND11_OBJECT_CVT(bytearray, object, PyByteArray_Check, PyByteArray_FromObject)
+
+ template <typename SzType, detail::enable_if_t<std::is_integral<SzType>::value, int> = 0>
+ bytearray(const char *c, const SzType &n)
+ : object(PyByteArray_FromStringAndSize(c, ssize_t_cast(n)), stolen_t{}) {
+ if (!m_ptr) {
+ pybind11_fail("Could not allocate bytearray object!");
+ }
+ }
+
+ bytearray() : bytearray("", 0) {}
+
+ explicit bytearray(const std::string &s) : bytearray(s.data(), s.size()) {}
+
+ size_t size() const { return static_cast<size_t>(PyByteArray_Size(m_ptr)); }
+
+ explicit operator std::string() const {
+ char *buffer = PyByteArray_AS_STRING(m_ptr);
+ ssize_t size = PyByteArray_GET_SIZE(m_ptr);
+ return std::string(buffer, static_cast<size_t>(size));
+ }
+};
+// Note: breathe >= 4.17.0 will fail to build docs if the below two constructors
+// are included in the doxygen group; close here and reopen after as a workaround
+/// @} pytypes
+
+/// \addtogroup pytypes
+/// @{
class none : public object {
public:
PYBIND11_OBJECT(none, object, detail::PyNone_Check)
- none() : object(Py_None, borrowed_t{}) { }
+ none() : object(Py_None, borrowed_t{}) {}
};
class ellipsis : public object {
public:
PYBIND11_OBJECT(ellipsis, object, detail::PyEllipsis_Check)
- ellipsis() : object(Py_Ellipsis, borrowed_t{}) { }
+ ellipsis() : object(Py_Ellipsis, borrowed_t{}) {}
};
class bool_ : public object {
public:
PYBIND11_OBJECT_CVT(bool_, object, PyBool_Check, raw_bool)
- bool_() : object(Py_False, borrowed_t{}) { }
+ bool_() : object(Py_False, borrowed_t{}) {}
// Allow implicit conversion from and to `bool`:
- bool_(bool value) : object(value ? Py_True : Py_False, borrowed_t{}) { }
- operator bool() const { return m_ptr && PyLong_AsLong(m_ptr) != 0; }
+ // NOLINTNEXTLINE(google-explicit-constructor)
+ bool_(bool value) : object(value ? Py_True : Py_False, borrowed_t{}) {}
+ // NOLINTNEXTLINE(google-explicit-constructor)
+ operator bool() const { return (m_ptr != nullptr) && PyLong_AsLong(m_ptr) != 0; }
private:
/// Return the truth value of an object -- always returns a new reference
static PyObject *raw_bool(PyObject *op) {
const auto value = PyObject_IsTrue(op);
- if (value == -1) return nullptr;
- return handle(value ? Py_True : Py_False).inc_ref().ptr();
+ if (value == -1) {
+ return nullptr;
+ }
+ return handle(value != 0 ? Py_True : Py_False).inc_ref().ptr();
}
};
@@ -1097,51 +1776,47 @@ PYBIND11_NAMESPACE_BEGIN(detail)
// unsigned type: (A)-1 != (B)-1 when A and B are unsigned types of different sizes).
template <typename Unsigned>
Unsigned as_unsigned(PyObject *o) {
- if (sizeof(Unsigned) <= sizeof(unsigned long)
-#if PY_VERSION_HEX < 0x03000000
- || PyInt_Check(o)
-#endif
- ) {
+ if (sizeof(Unsigned) <= sizeof(unsigned long)) {
unsigned long v = PyLong_AsUnsignedLong(o);
return v == (unsigned long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v;
}
- else {
- unsigned long long v = PyLong_AsUnsignedLongLong(o);
- return v == (unsigned long long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v;
- }
+ unsigned long long v = PyLong_AsUnsignedLongLong(o);
+ return v == (unsigned long long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v;
}
PYBIND11_NAMESPACE_END(detail)
class int_ : public object {
public:
PYBIND11_OBJECT_CVT(int_, object, PYBIND11_LONG_CHECK, PyNumber_Long)
- int_() : object(PyLong_FromLong(0), stolen_t{}) { }
+ int_() : object(PyLong_FromLong(0), stolen_t{}) {}
// Allow implicit conversion from C++ integral types:
- template <typename T,
- detail::enable_if_t<std::is_integral<T>::value, int> = 0>
+ template <typename T, detail::enable_if_t<std::is_integral<T>::value, int> = 0>
+ // NOLINTNEXTLINE(google-explicit-constructor)
int_(T value) {
if (sizeof(T) <= sizeof(long)) {
- if (std::is_signed<T>::value)
+ if (std::is_signed<T>::value) {
m_ptr = PyLong_FromLong((long) value);
- else
+ } else {
m_ptr = PyLong_FromUnsignedLong((unsigned long) value);
+ }
} else {
- if (std::is_signed<T>::value)
+ if (std::is_signed<T>::value) {
m_ptr = PyLong_FromLongLong((long long) value);
- else
+ } else {
m_ptr = PyLong_FromUnsignedLongLong((unsigned long long) value);
+ }
+ }
+ if (!m_ptr) {
+ pybind11_fail("Could not allocate int object!");
}
- if (!m_ptr) pybind11_fail("Could not allocate int object!");
}
- template <typename T,
- detail::enable_if_t<std::is_integral<T>::value, int> = 0>
+ template <typename T, detail::enable_if_t<std::is_integral<T>::value, int> = 0>
+ // NOLINTNEXTLINE(google-explicit-constructor)
operator T() const {
- return std::is_unsigned<T>::value
- ? detail::as_unsigned<T>(m_ptr)
- : sizeof(T) <= sizeof(long)
- ? (T) PyLong_AsLong(m_ptr)
- : (T) PYBIND11_LONG_AS_LONGLONG(m_ptr);
+ return std::is_unsigned<T>::value ? detail::as_unsigned<T>(m_ptr)
+ : sizeof(T) <= sizeof(long) ? (T) PyLong_AsLong(m_ptr)
+ : (T) PYBIND11_LONG_AS_LONGLONG(m_ptr);
}
};
@@ -1149,46 +1824,80 @@ class float_ : public object {
public:
PYBIND11_OBJECT_CVT(float_, object, PyFloat_Check, PyNumber_Float)
// Allow implicit conversion from float/double:
+ // NOLINTNEXTLINE(google-explicit-constructor)
float_(float value) : object(PyFloat_FromDouble((double) value), stolen_t{}) {
- if (!m_ptr) pybind11_fail("Could not allocate float object!");
+ if (!m_ptr) {
+ pybind11_fail("Could not allocate float object!");
+ }
}
+ // NOLINTNEXTLINE(google-explicit-constructor)
float_(double value = .0) : object(PyFloat_FromDouble((double) value), stolen_t{}) {
- if (!m_ptr) pybind11_fail("Could not allocate float object!");
+ if (!m_ptr) {
+ pybind11_fail("Could not allocate float object!");
+ }
}
+ // NOLINTNEXTLINE(google-explicit-constructor)
operator float() const { return (float) PyFloat_AsDouble(m_ptr); }
+ // NOLINTNEXTLINE(google-explicit-constructor)
operator double() const { return (double) PyFloat_AsDouble(m_ptr); }
};
class weakref : public object {
public:
- PYBIND11_OBJECT_DEFAULT(weakref, object, PyWeakref_Check)
+ PYBIND11_OBJECT_CVT_DEFAULT(weakref, object, PyWeakref_Check, raw_weakref)
explicit weakref(handle obj, handle callback = {})
: object(PyWeakref_NewRef(obj.ptr(), callback.ptr()), stolen_t{}) {
- if (!m_ptr) pybind11_fail("Could not allocate weak reference!");
+ if (!m_ptr) {
+ if (PyErr_Occurred()) {
+ throw error_already_set();
+ }
+ pybind11_fail("Could not allocate weak reference!");
+ }
}
+
+private:
+ static PyObject *raw_weakref(PyObject *o) { return PyWeakref_NewRef(o, nullptr); }
};
class slice : public object {
public:
PYBIND11_OBJECT_DEFAULT(slice, object, PySlice_Check)
- slice(ssize_t start_, ssize_t stop_, ssize_t step_) {
- int_ start(start_), stop(stop_), step(step_);
- m_ptr = PySlice_New(start.ptr(), stop.ptr(), step.ptr());
- if (!m_ptr) pybind11_fail("Could not allocate slice object!");
+ slice(handle start, handle stop, handle step)
+ : object(PySlice_New(start.ptr(), stop.ptr(), step.ptr()), stolen_t{}) {
+ if (!m_ptr) {
+ pybind11_fail("Could not allocate slice object!");
+ }
}
- bool compute(size_t length, size_t *start, size_t *stop, size_t *step,
- size_t *slicelength) const {
+
+#ifdef PYBIND11_HAS_OPTIONAL
+ slice(std::optional<ssize_t> start, std::optional<ssize_t> stop, std::optional<ssize_t> step)
+ : slice(index_to_object(start), index_to_object(stop), index_to_object(step)) {}
+#else
+ slice(ssize_t start_, ssize_t stop_, ssize_t step_)
+ : slice(int_(start_), int_(stop_), int_(step_)) {}
+#endif
+
+ bool
+ compute(size_t length, size_t *start, size_t *stop, size_t *step, size_t *slicelength) const {
return PySlice_GetIndicesEx((PYBIND11_SLICE_OBJECT *) m_ptr,
- (ssize_t) length, (ssize_t *) start,
- (ssize_t *) stop, (ssize_t *) step,
- (ssize_t *) slicelength) == 0;
+ (ssize_t) length,
+ (ssize_t *) start,
+ (ssize_t *) stop,
+ (ssize_t *) step,
+ (ssize_t *) slicelength)
+ == 0;
}
- bool compute(ssize_t length, ssize_t *start, ssize_t *stop, ssize_t *step,
- ssize_t *slicelength) const {
- return PySlice_GetIndicesEx((PYBIND11_SLICE_OBJECT *) m_ptr,
- length, start,
- stop, step,
- slicelength) == 0;
+ bool compute(
+ ssize_t length, ssize_t *start, ssize_t *stop, ssize_t *step, ssize_t *slicelength) const {
+ return PySlice_GetIndicesEx(
+ (PYBIND11_SLICE_OBJECT *) m_ptr, length, start, stop, step, slicelength)
+ == 0;
+ }
+
+private:
+ template <typename T>
+ static object index_to_object(T index) {
+ return index ? object(int_(*index)) : object(none());
}
};
@@ -1196,77 +1905,146 @@ class capsule : public object {
public:
PYBIND11_OBJECT_DEFAULT(capsule, object, PyCapsule_CheckExact)
PYBIND11_DEPRECATED("Use reinterpret_borrow<capsule>() or reinterpret_steal<capsule>()")
- capsule(PyObject *ptr, bool is_borrowed) : object(is_borrowed ? object(ptr, borrowed_t{}) : object(ptr, stolen_t{})) { }
+ capsule(PyObject *ptr, bool is_borrowed)
+ : object(is_borrowed ? object(ptr, borrowed_t{}) : object(ptr, stolen_t{})) {}
- explicit capsule(const void *value, const char *name = nullptr, void (*destructor)(PyObject *) = nullptr)
+ explicit capsule(const void *value,
+ const char *name = nullptr,
+ PyCapsule_Destructor destructor = nullptr)
: object(PyCapsule_New(const_cast<void *>(value), name, destructor), stolen_t{}) {
- if (!m_ptr)
- pybind11_fail("Could not allocate capsule object!");
+ if (!m_ptr) {
+ throw error_already_set();
+ }
}
- PYBIND11_DEPRECATED("Please pass a destructor that takes a void pointer as input")
- capsule(const void *value, void (*destruct)(PyObject *))
- : object(PyCapsule_New(const_cast<void*>(value), nullptr, destruct), stolen_t{}) {
- if (!m_ptr)
- pybind11_fail("Could not allocate capsule object!");
+ PYBIND11_DEPRECATED("Please use the ctor with value, name, destructor args")
+ capsule(const void *value, PyCapsule_Destructor destructor)
+ : object(PyCapsule_New(const_cast<void *>(value), nullptr, destructor), stolen_t{}) {
+ if (!m_ptr) {
+ throw error_already_set();
+ }
}
+ /// Capsule name is nullptr.
capsule(const void *value, void (*destructor)(void *)) {
- m_ptr = PyCapsule_New(const_cast<void *>(value), nullptr, [](PyObject *o) {
- auto destructor = reinterpret_cast<void (*)(void *)>(PyCapsule_GetContext(o));
- void *ptr = PyCapsule_GetPointer(o, nullptr);
- destructor(ptr);
- });
-
- if (!m_ptr)
- pybind11_fail("Could not allocate capsule object!");
+ initialize_with_void_ptr_destructor(value, nullptr, destructor);
+ }
- if (PyCapsule_SetContext(m_ptr, (void *) destructor) != 0)
- pybind11_fail("Could not set capsule context!");
+ capsule(const void *value, const char *name, void (*destructor)(void *)) {
+ initialize_with_void_ptr_destructor(value, name, destructor);
}
- capsule(void (*destructor)()) {
+ explicit capsule(void (*destructor)()) {
m_ptr = PyCapsule_New(reinterpret_cast<void *>(destructor), nullptr, [](PyObject *o) {
- auto destructor = reinterpret_cast<void (*)()>(PyCapsule_GetPointer(o, nullptr));
+ const char *name = get_name_in_error_scope(o);
+ auto destructor = reinterpret_cast<void (*)()>(PyCapsule_GetPointer(o, name));
+ if (destructor == nullptr) {
+ throw error_already_set();
+ }
destructor();
});
- if (!m_ptr)
- pybind11_fail("Could not allocate capsule object!");
+ if (!m_ptr) {
+ throw error_already_set();
+ }
}
- template <typename T> operator T *() const {
+ template <typename T>
+ operator T *() const { // NOLINT(google-explicit-constructor)
return get_pointer<T>();
}
/// Get the pointer the capsule holds.
- template<typename T = void>
- T* get_pointer() const {
- auto name = this->name();
+ template <typename T = void>
+ T *get_pointer() const {
+ const auto *name = this->name();
T *result = static_cast<T *>(PyCapsule_GetPointer(m_ptr, name));
- if (!result) pybind11_fail("Unable to extract capsule contents!");
+ if (!result) {
+ throw error_already_set();
+ }
return result;
}
/// Replaces a capsule's pointer *without* calling the destructor on the existing one.
void set_pointer(const void *value) {
- if (PyCapsule_SetPointer(m_ptr, const_cast<void *>(value)) != 0)
- pybind11_fail("Could not set capsule pointer");
+ if (PyCapsule_SetPointer(m_ptr, const_cast<void *>(value)) != 0) {
+ throw error_already_set();
+ }
+ }
+
+ const char *name() const {
+ const char *name = PyCapsule_GetName(m_ptr);
+ if ((name == nullptr) && PyErr_Occurred()) {
+ throw error_already_set();
+ }
+ return name;
+ }
+
+ /// Replaces a capsule's name *without* calling the destructor on the existing one.
+ void set_name(const char *new_name) {
+ if (PyCapsule_SetName(m_ptr, new_name) != 0) {
+ throw error_already_set();
+ }
+ }
+
+private:
+ static const char *get_name_in_error_scope(PyObject *o) {
+ error_scope error_guard;
+
+ const char *name = PyCapsule_GetName(o);
+ if ((name == nullptr) && PyErr_Occurred()) {
+ // write out and consume error raised by call to PyCapsule_GetName
+ PyErr_WriteUnraisable(o);
+ }
+
+ return name;
}
- const char *name() const { return PyCapsule_GetName(m_ptr); }
+ void initialize_with_void_ptr_destructor(const void *value,
+ const char *name,
+ void (*destructor)(void *)) {
+ m_ptr = PyCapsule_New(const_cast<void *>(value), name, [](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 && PyErr_Occurred()) {
+ throw error_already_set();
+ }
+ const char *name = get_name_in_error_scope(o);
+ void *ptr = PyCapsule_GetPointer(o, name);
+ if (ptr == nullptr) {
+ throw error_already_set();
+ }
+
+ if (destructor != nullptr) {
+ destructor(ptr);
+ }
+ });
+
+ if (!m_ptr || PyCapsule_SetContext(m_ptr, reinterpret_cast<void *>(destructor)) != 0) {
+ throw error_already_set();
+ }
+ }
};
class tuple : public object {
public:
PYBIND11_OBJECT_CVT(tuple, object, PyTuple_Check, PySequence_Tuple)
- explicit tuple(size_t size = 0) : object(PyTuple_New((ssize_t) size), stolen_t{}) {
- if (!m_ptr) pybind11_fail("Could not allocate tuple object!");
+ template <typename SzType = ssize_t,
+ detail::enable_if_t<std::is_integral<SzType>::value, int> = 0>
+ // Some compilers generate link errors when using `const SzType &` here:
+ explicit tuple(SzType size = 0) : object(PyTuple_New(ssize_t_cast(size)), stolen_t{}) {
+ if (!m_ptr) {
+ pybind11_fail("Could not allocate tuple object!");
+ }
}
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)}; }
};
@@ -1275,37 +2053,45 @@ public:
// fails to compile enable_if_t<all_of<is_keyword_or_ds<Args>...>::value> part below
// (tested with ICC 2021.1 Beta 20200827).
template <typename... Args>
-constexpr bool args_are_all_keyword_or_ds()
-{
- return detail::all_of<detail::is_keyword_or_ds<Args>...>::value;
+constexpr bool args_are_all_keyword_or_ds() {
+ return detail::all_of<detail::is_keyword_or_ds<Args>...>::value;
}
class dict : public object {
public:
PYBIND11_OBJECT_CVT(dict, object, PyDict_Check, raw_dict)
dict() : object(PyDict_New(), stolen_t{}) {
- if (!m_ptr) pybind11_fail("Could not allocate dict object!");
+ if (!m_ptr) {
+ pybind11_fail("Could not allocate dict object!");
+ }
}
template <typename... Args,
typename = detail::enable_if_t<args_are_all_keyword_or_ds<Args...>()>,
- // MSVC workaround: it can't compile an out-of-line definition, so defer the collector
+ // MSVC workaround: it can't compile an out-of-line definition, so defer the
+ // collector
typename collector = detail::deferred_t<detail::unpacking_collector<>, Args...>>
- explicit dict(Args &&...args) : dict(collector(std::forward<Args>(args)...).kwargs()) { }
+ explicit dict(Args &&...args) : dict(collector(std::forward<Args>(args)...).kwargs()) {}
size_t size() const { return (size_t) PyDict_Size(m_ptr); }
bool empty() const { return size() == 0; }
detail::dict_iterator begin() const { return {*this, 0}; }
detail::dict_iterator end() const { return {}; }
- void clear() const { PyDict_Clear(ptr()); }
- template <typename T> bool contains(T &&key) const {
- return PyDict_Contains(m_ptr, detail::object_or_cast(std::forward<T>(key)).ptr()) == 1;
+ void clear() /* py-non-const */ { PyDict_Clear(ptr()); }
+ template <typename T>
+ bool contains(T &&key) const {
+ auto result = PyDict_Contains(m_ptr, detail::object_or_cast(std::forward<T>(key)).ptr());
+ if (result == -1) {
+ throw error_already_set();
+ }
+ return result == 1;
}
private:
/// Call the `dict` Python type -- always returns a new reference
static PyObject *raw_dict(PyObject *op) {
- if (PyDict_Check(op))
+ if (PyDict_Check(op)) {
return handle(op).inc_ref().ptr();
+ }
return PyObject_CallFunctionObjArgs((PyObject *) &PyDict_Type, op, nullptr);
}
};
@@ -1315,13 +2101,17 @@ public:
PYBIND11_OBJECT_DEFAULT(sequence, object, PySequence_Check)
size_t size() const {
ssize_t result = PySequence_Size(m_ptr);
- if (result == -1)
+ if (result == -1) {
throw error_already_set();
+ }
return (size_t) result;
}
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)}; }
};
@@ -1329,42 +2119,82 @@ public:
class list : public object {
public:
PYBIND11_OBJECT_CVT(list, object, PyList_Check, PySequence_List)
- explicit list(size_t size = 0) : object(PyList_New((ssize_t) size), stolen_t{}) {
- if (!m_ptr) pybind11_fail("Could not allocate list object!");
+ template <typename SzType = ssize_t,
+ detail::enable_if_t<std::is_integral<SzType>::value, int> = 0>
+ // Some compilers generate link errors when using `const SzType &` here:
+ explicit list(SzType size = 0) : object(PyList_New(ssize_t_cast(size)), stolen_t{}) {
+ if (!m_ptr) {
+ pybind11_fail("Could not allocate list object!");
+ }
}
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> void append(T &&val) const {
- PyList_Append(m_ptr, detail::object_or_cast(std::forward<T>(val)).ptr());
+ template <typename T>
+ void append(T &&val) /* py-non-const */ {
+ if (PyList_Append(m_ptr, detail::object_or_cast(std::forward<T>(val)).ptr()) != 0) {
+ throw error_already_set();
+ }
}
- template <typename T> void insert(size_t index, T &&val) const {
- PyList_Insert(m_ptr, static_cast<ssize_t>(index),
- detail::object_or_cast(std::forward<T>(val)).ptr());
+ template <typename IdxType,
+ typename ValType,
+ detail::enable_if_t<std::is_integral<IdxType>::value, int> = 0>
+ void insert(const IdxType &index, ValType &&val) /* py-non-const */ {
+ if (PyList_Insert(m_ptr,
+ ssize_t_cast(index),
+ detail::object_or_cast(std::forward<ValType>(val)).ptr())
+ != 0) {
+ throw error_already_set();
+ }
}
};
-class args : public tuple { PYBIND11_OBJECT_DEFAULT(args, tuple, PyTuple_Check) };
-class kwargs : public dict { PYBIND11_OBJECT_DEFAULT(kwargs, dict, PyDict_Check) };
+class args : public tuple {
+ PYBIND11_OBJECT_DEFAULT(args, tuple, PyTuple_Check)
+};
+class kwargs : public dict {
+ PYBIND11_OBJECT_DEFAULT(kwargs, dict, PyDict_Check)
+};
-class set : public object {
+class anyset : public object {
public:
- PYBIND11_OBJECT_CVT(set, object, PySet_Check, PySet_New)
- set() : object(PySet_New(nullptr), stolen_t{}) {
- if (!m_ptr) pybind11_fail("Could not allocate set object!");
- }
- size_t size() const { return (size_t) PySet_Size(m_ptr); }
+ PYBIND11_OBJECT(anyset, object, PyAnySet_Check)
+ size_t size() const { return static_cast<size_t>(PySet_Size(m_ptr)); }
bool empty() const { return size() == 0; }
- template <typename T> bool add(T &&val) const {
- return PySet_Add(m_ptr, detail::object_or_cast(std::forward<T>(val)).ptr()) == 0;
+ template <typename T>
+ bool contains(T &&val) const {
+ auto result = PySet_Contains(m_ptr, detail::object_or_cast(std::forward<T>(val)).ptr());
+ if (result == -1) {
+ throw error_already_set();
+ }
+ return result == 1;
}
- void clear() const { PySet_Clear(m_ptr); }
- template <typename T> bool contains(T &&val) const {
- return PySet_Contains(m_ptr, detail::object_or_cast(std::forward<T>(val)).ptr()) == 1;
+};
+
+class set : public anyset {
+public:
+ PYBIND11_OBJECT_CVT(set, anyset, PySet_Check, PySet_New)
+ set() : anyset(PySet_New(nullptr), stolen_t{}) {
+ if (!m_ptr) {
+ pybind11_fail("Could not allocate set object!");
+ }
}
+ template <typename T>
+ bool add(T &&val) /* py-non-const */ {
+ return PySet_Add(m_ptr, detail::object_or_cast(std::forward<T>(val)).ptr()) == 0;
+ }
+ void clear() /* py-non-const */ { PySet_Clear(m_ptr); }
+};
+
+class frozenset : public anyset {
+public:
+ PYBIND11_OBJECT_CVT(frozenset, anyset, PyFrozenSet_Check, PyFrozenSet_New)
};
class function : public object {
@@ -1372,8 +2202,9 @@ public:
PYBIND11_OBJECT_DEFAULT(function, object, PyCallable_Check)
handle cpp_function() const {
handle fun = detail::get_function(m_ptr);
- if (fun && PyCFunction_Check(fun.ptr()))
+ if (fun && PyCFunction_Check(fun.ptr())) {
return fun;
+ }
return handle();
}
bool is_cpp_function() const { return (bool) cpp_function(); }
@@ -1390,7 +2221,9 @@ public:
buffer_info request(bool writable = false) const {
int flags = PyBUF_STRIDES | PyBUF_FORMAT;
- if (writable) flags |= PyBUF_WRITABLE;
+ if (writable) {
+ flags |= PyBUF_WRITABLE;
+ }
auto *view = new Py_buffer();
if (PyObject_GetBuffer(m_ptr, view, flags) != 0) {
delete view;
@@ -1413,15 +2246,16 @@ public:
For creating a ``memoryview`` from objects that support buffer protocol,
use ``memoryview(const object& obj)`` instead of this constructor.
\endrst */
- explicit memoryview(const buffer_info& info) {
- if (!info.view())
+ explicit memoryview(const buffer_info &info) {
+ if (!info.view()) {
pybind11_fail("Prohibited to create memoryview without Py_buffer");
+ }
// Note: PyMemoryView_FromBuffer never increments obj reference.
- m_ptr = (info.view()->obj) ?
- PyMemoryView_FromObject(info.view()->obj) :
- PyMemoryView_FromBuffer(info.view());
- if (!m_ptr)
+ m_ptr = (info.view()->obj) ? PyMemoryView_FromObject(info.view()->obj)
+ : PyMemoryView_FromBuffer(info.view());
+ if (!m_ptr) {
pybind11_fail("Unable to create memoryview from buffer descriptor");
+ }
}
/** \rst
@@ -1434,7 +2268,8 @@ public:
See also: Python C API documentation for `PyMemoryView_FromBuffer`_.
- .. _PyMemoryView_FromBuffer: https://docs.python.org/c-api/memoryview.html#c.PyMemoryView_FromBuffer
+ .. _PyMemoryView_FromBuffer:
+ https://docs.python.org/c-api/memoryview.html#c.PyMemoryView_FromBuffer
:param ptr: Pointer to the buffer.
:param itemsize: Byte size of an element.
@@ -1447,37 +2282,43 @@ public:
:param readonly: Flag to indicate if the underlying storage may be
written to.
\endrst */
- static memoryview from_buffer(
- void *ptr, ssize_t itemsize, const char *format,
- detail::any_container<ssize_t> shape,
- detail::any_container<ssize_t> strides, bool readonly = false);
-
- static memoryview from_buffer(
- const void *ptr, ssize_t itemsize, const char *format,
- detail::any_container<ssize_t> shape,
- detail::any_container<ssize_t> strides) {
+ static memoryview from_buffer(void *ptr,
+ ssize_t itemsize,
+ const char *format,
+ detail::any_container<ssize_t> shape,
+ detail::any_container<ssize_t> strides,
+ bool readonly = false);
+
+ static memoryview from_buffer(const void *ptr,
+ ssize_t itemsize,
+ const char *format,
+ detail::any_container<ssize_t> shape,
+ detail::any_container<ssize_t> strides) {
return memoryview::from_buffer(
- const_cast<void*>(ptr), itemsize, format, shape, strides, true);
- }
-
- template<typename T>
- static memoryview from_buffer(
- T *ptr, detail::any_container<ssize_t> shape,
- detail::any_container<ssize_t> strides, bool readonly = false) {
+ const_cast<void *>(ptr), itemsize, format, std::move(shape), std::move(strides), true);
+ }
+
+ template <typename T>
+ static memoryview from_buffer(T *ptr,
+ detail::any_container<ssize_t> shape,
+ detail::any_container<ssize_t> strides,
+ bool readonly = false) {
+ return memoryview::from_buffer(reinterpret_cast<void *>(ptr),
+ sizeof(T),
+ format_descriptor<T>::value,
+ std::move(shape),
+ std::move(strides),
+ readonly);
+ }
+
+ template <typename T>
+ static memoryview from_buffer(const T *ptr,
+ detail::any_container<ssize_t> shape,
+ detail::any_container<ssize_t> strides) {
return memoryview::from_buffer(
- reinterpret_cast<void*>(ptr), sizeof(T),
- format_descriptor<T>::value, shape, strides, readonly);
+ const_cast<T *>(ptr), std::move(shape), std::move(strides), true);
}
- template<typename T>
- static memoryview from_buffer(
- const T *ptr, detail::any_container<ssize_t> shape,
- detail::any_container<ssize_t> strides) {
- return memoryview::from_buffer(
- const_cast<T*>(ptr), shape, strides, true);
- }
-
-#if PY_MAJOR_VERSION >= 3
/** \rst
Creates ``memoryview`` from static memory.
@@ -1485,56 +2326,65 @@ public:
managed by Python. The caller is responsible for managing the lifetime
of ``mem``, which MUST outlive the memoryview constructed here.
- This method is not available in Python 2.
-
See also: Python C API documentation for `PyMemoryView_FromBuffer`_.
- .. _PyMemoryView_FromMemory: https://docs.python.org/c-api/memoryview.html#c.PyMemoryView_FromMemory
+ .. _PyMemoryView_FromMemory:
+ https://docs.python.org/c-api/memoryview.html#c.PyMemoryView_FromMemory
\endrst */
static memoryview from_memory(void *mem, ssize_t size, bool readonly = false) {
- PyObject* ptr = PyMemoryView_FromMemory(
- reinterpret_cast<char*>(mem), size,
- (readonly) ? PyBUF_READ : PyBUF_WRITE);
- if (!ptr)
+ PyObject *ptr = PyMemoryView_FromMemory(
+ reinterpret_cast<char *>(mem), size, (readonly) ? PyBUF_READ : PyBUF_WRITE);
+ if (!ptr) {
pybind11_fail("Could not allocate memoryview object!");
+ }
return memoryview(object(ptr, stolen_t{}));
}
static memoryview from_memory(const void *mem, ssize_t size) {
- return memoryview::from_memory(const_cast<void*>(mem), size, true);
+ return memoryview::from_memory(const_cast<void *>(mem), size, true);
+ }
+
+#ifdef PYBIND11_HAS_STRING_VIEW
+ static memoryview from_memory(std::string_view mem) {
+ return from_memory(const_cast<char *>(mem.data()), static_cast<ssize_t>(mem.size()), true);
}
#endif
};
-#ifndef DOXYGEN_SHOULD_SKIP_THIS
-inline memoryview memoryview::from_buffer(
- void *ptr, ssize_t itemsize, const char* format,
- detail::any_container<ssize_t> shape,
- detail::any_container<ssize_t> strides, bool readonly) {
+/// @cond DUPLICATE
+inline memoryview memoryview::from_buffer(void *ptr,
+ ssize_t itemsize,
+ const char *format,
+ detail::any_container<ssize_t> shape,
+ detail::any_container<ssize_t> strides,
+ bool readonly) {
size_t ndim = shape->size();
- if (ndim != strides->size())
+ if (ndim != strides->size()) {
pybind11_fail("memoryview: shape length doesn't match strides length");
- ssize_t size = ndim ? 1 : 0;
- for (size_t i = 0; i < ndim; ++i)
+ }
+ ssize_t size = ndim != 0u ? 1 : 0;
+ for (size_t i = 0; i < ndim; ++i) {
size *= (*shape)[i];
+ }
Py_buffer view;
view.buf = ptr;
view.obj = nullptr;
view.len = size * itemsize;
view.readonly = static_cast<int>(readonly);
view.itemsize = itemsize;
- view.format = const_cast<char*>(format);
+ view.format = const_cast<char *>(format);
view.ndim = static_cast<int>(ndim);
view.shape = shape->data();
view.strides = strides->data();
view.suboffsets = nullptr;
view.internal = nullptr;
- PyObject* obj = PyMemoryView_FromBuffer(&view);
- if (!obj)
+ PyObject *obj = PyMemoryView_FromBuffer(&view);
+ if (!obj) {
throw error_already_set();
+ }
return memoryview(object(obj, stolen_t{}));
}
-#endif // DOXYGEN_SHOULD_SKIP_THIS
+/// @endcond
/// @} pytypes
/// \addtogroup python_builtins
@@ -1543,19 +2393,16 @@ inline memoryview memoryview::from_buffer(
/// Get the length of a Python object.
inline size_t len(handle h) {
ssize_t result = PyObject_Length(h.ptr());
- if (result < 0)
+ if (result < 0) {
throw error_already_set();
+ }
return (size_t) result;
}
/// Get the length hint of a Python object.
/// Returns 0 when this cannot be determined.
inline size_t len_hint(handle h) {
-#if PY_VERSION_HEX >= 0x03040000
ssize_t result = PyObject_LengthHint(h.ptr(), 0);
-#else
- ssize_t result = PyObject_Length(h.ptr());
-#endif
if (result < 0) {
// Sometimes a length can't be determined at all (eg generators)
// In which case simply return 0
@@ -1567,102 +2414,139 @@ inline size_t len_hint(handle h) {
inline str repr(handle h) {
PyObject *str_value = PyObject_Repr(h.ptr());
- if (!str_value) throw error_already_set();
-#if PY_MAJOR_VERSION < 3
- PyObject *unicode = PyUnicode_FromEncodedObject(str_value, "utf-8", nullptr);
- Py_XDECREF(str_value); str_value = unicode;
- if (!str_value) throw error_already_set();
-#endif
+ if (!str_value) {
+ throw error_already_set();
+ }
return reinterpret_steal<str>(str_value);
}
inline iterator iter(handle obj) {
PyObject *result = PyObject_GetIter(obj.ptr());
- if (!result) { throw error_already_set(); }
+ if (!result) {
+ throw error_already_set();
+ }
return reinterpret_steal<iterator>(result);
}
/// @} python_builtins
PYBIND11_NAMESPACE_BEGIN(detail)
-template <typename D> iterator object_api<D>::begin() const { return iter(derived()); }
-template <typename D> iterator object_api<D>::end() const { return iterator::sentinel(); }
-template <typename D> item_accessor object_api<D>::operator[](handle key) const {
+template <typename D>
+iterator object_api<D>::begin() const {
+ return iter(derived());
+}
+template <typename D>
+iterator object_api<D>::end() const {
+ return iterator::sentinel();
+}
+template <typename D>
+item_accessor object_api<D>::operator[](handle key) const {
return {derived(), reinterpret_borrow<object>(key)};
}
-template <typename D> item_accessor object_api<D>::operator[](const char *key) const {
+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)};
}
-template <typename D> obj_attr_accessor object_api<D>::attr(handle key) const {
+template <typename D>
+obj_attr_accessor object_api<D>::attr(handle key) const {
return {derived(), reinterpret_borrow<object>(key)};
}
-template <typename D> str_attr_accessor object_api<D>::attr(const char *key) const {
+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};
}
-template <typename D> args_proxy object_api<D>::operator*() const {
+template <typename D>
+args_proxy object_api<D>::operator*() const {
return args_proxy(derived().ptr());
}
-template <typename D> template <typename T> bool object_api<D>::contains(T &&item) const {
+template <typename D>
+template <typename T>
+bool object_api<D>::contains(T &&item) const {
return attr("__contains__")(std::forward<T>(item)).template cast<bool>();
}
template <typename D>
-pybind11::str object_api<D>::str() const { return pybind11::str(derived()); }
+pybind11::str object_api<D>::str() const {
+ return pybind11::str(derived());
+}
template <typename D>
-str_attr_accessor object_api<D>::doc() const { return attr("__doc__"); }
+str_attr_accessor object_api<D>::doc() const {
+ return attr("__doc__");
+}
template <typename D>
-handle object_api<D>::get_type() const { return type::handle_of(derived()); }
+handle object_api<D>::get_type() const {
+ return type::handle_of(derived());
+}
template <typename D>
bool object_api<D>::rich_compare(object_api const &other, int value) const {
int rv = PyObject_RichCompareBool(derived().ptr(), other.derived().ptr(), value);
- if (rv == -1)
+ if (rv == -1) {
throw error_already_set();
+ }
return rv == 1;
}
-#define PYBIND11_MATH_OPERATOR_UNARY(op, fn) \
- template <typename D> object object_api<D>::op() const { \
- object result = reinterpret_steal<object>(fn(derived().ptr())); \
- if (!result.ptr()) \
- throw error_already_set(); \
- return result; \
- }
-
-#define PYBIND11_MATH_OPERATOR_BINARY(op, fn) \
- template <typename D> \
- object object_api<D>::op(object_api const &other) const { \
- object result = reinterpret_steal<object>( \
- fn(derived().ptr(), other.derived().ptr())); \
- if (!result.ptr()) \
- throw error_already_set(); \
- return result; \
- }
-
-PYBIND11_MATH_OPERATOR_UNARY (operator~, PyNumber_Invert)
-PYBIND11_MATH_OPERATOR_UNARY (operator-, PyNumber_Negative)
-PYBIND11_MATH_OPERATOR_BINARY(operator+, PyNumber_Add)
-PYBIND11_MATH_OPERATOR_BINARY(operator+=, PyNumber_InPlaceAdd)
-PYBIND11_MATH_OPERATOR_BINARY(operator-, PyNumber_Subtract)
-PYBIND11_MATH_OPERATOR_BINARY(operator-=, PyNumber_InPlaceSubtract)
-PYBIND11_MATH_OPERATOR_BINARY(operator*, PyNumber_Multiply)
-PYBIND11_MATH_OPERATOR_BINARY(operator*=, PyNumber_InPlaceMultiply)
-PYBIND11_MATH_OPERATOR_BINARY(operator/, PyNumber_TrueDivide)
-PYBIND11_MATH_OPERATOR_BINARY(operator/=, PyNumber_InPlaceTrueDivide)
-PYBIND11_MATH_OPERATOR_BINARY(operator|, PyNumber_Or)
-PYBIND11_MATH_OPERATOR_BINARY(operator|=, PyNumber_InPlaceOr)
-PYBIND11_MATH_OPERATOR_BINARY(operator&, PyNumber_And)
-PYBIND11_MATH_OPERATOR_BINARY(operator&=, PyNumber_InPlaceAnd)
-PYBIND11_MATH_OPERATOR_BINARY(operator^, PyNumber_Xor)
-PYBIND11_MATH_OPERATOR_BINARY(operator^=, PyNumber_InPlaceXor)
-PYBIND11_MATH_OPERATOR_BINARY(operator<<, PyNumber_Lshift)
-PYBIND11_MATH_OPERATOR_BINARY(operator<<=, PyNumber_InPlaceLshift)
-PYBIND11_MATH_OPERATOR_BINARY(operator>>, PyNumber_Rshift)
-PYBIND11_MATH_OPERATOR_BINARY(operator>>=, PyNumber_InPlaceRshift)
+#define PYBIND11_MATH_OPERATOR_UNARY(op, fn) \
+ template <typename D> \
+ object object_api<D>::op() const { \
+ object result = reinterpret_steal<object>(fn(derived().ptr())); \
+ if (!result.ptr()) \
+ throw error_already_set(); \
+ return result; \
+ }
+
+#define PYBIND11_MATH_OPERATOR_BINARY(op, fn) \
+ template <typename D> \
+ object object_api<D>::op(object_api const &other) const { \
+ object result = reinterpret_steal<object>(fn(derived().ptr(), other.derived().ptr())); \
+ if (!result.ptr()) \
+ throw error_already_set(); \
+ return result; \
+ }
+
+#define PYBIND11_MATH_OPERATOR_BINARY_INPLACE(iop, fn) \
+ template <typename D> \
+ object object_api<D>::iop(object_api const &other) { \
+ object result = reinterpret_steal<object>(fn(derived().ptr(), other.derived().ptr())); \
+ if (!result.ptr()) \
+ throw error_already_set(); \
+ return result; \
+ }
+
+PYBIND11_MATH_OPERATOR_UNARY(operator~, PyNumber_Invert)
+PYBIND11_MATH_OPERATOR_UNARY(operator-, PyNumber_Negative)
+PYBIND11_MATH_OPERATOR_BINARY(operator+, PyNumber_Add)
+PYBIND11_MATH_OPERATOR_BINARY_INPLACE(operator+=, PyNumber_InPlaceAdd)
+PYBIND11_MATH_OPERATOR_BINARY(operator-, PyNumber_Subtract)
+PYBIND11_MATH_OPERATOR_BINARY_INPLACE(operator-=, PyNumber_InPlaceSubtract)
+PYBIND11_MATH_OPERATOR_BINARY(operator*, PyNumber_Multiply)
+PYBIND11_MATH_OPERATOR_BINARY_INPLACE(operator*=, PyNumber_InPlaceMultiply)
+PYBIND11_MATH_OPERATOR_BINARY(operator/, PyNumber_TrueDivide)
+PYBIND11_MATH_OPERATOR_BINARY_INPLACE(operator/=, PyNumber_InPlaceTrueDivide)
+PYBIND11_MATH_OPERATOR_BINARY(operator|, PyNumber_Or)
+PYBIND11_MATH_OPERATOR_BINARY_INPLACE(operator|=, PyNumber_InPlaceOr)
+PYBIND11_MATH_OPERATOR_BINARY(operator&, PyNumber_And)
+PYBIND11_MATH_OPERATOR_BINARY_INPLACE(operator&=, PyNumber_InPlaceAnd)
+PYBIND11_MATH_OPERATOR_BINARY(operator^, PyNumber_Xor)
+PYBIND11_MATH_OPERATOR_BINARY_INPLACE(operator^=, PyNumber_InPlaceXor)
+PYBIND11_MATH_OPERATOR_BINARY(operator<<, PyNumber_Lshift)
+PYBIND11_MATH_OPERATOR_BINARY_INPLACE(operator<<=, PyNumber_InPlaceLshift)
+PYBIND11_MATH_OPERATOR_BINARY(operator>>, PyNumber_Rshift)
+PYBIND11_MATH_OPERATOR_BINARY_INPLACE(operator>>=, PyNumber_InPlaceRshift)
#undef PYBIND11_MATH_OPERATOR_UNARY
#undef PYBIND11_MATH_OPERATOR_BINARY
+#undef PYBIND11_MATH_OPERATOR_BINARY_INPLACE
PYBIND11_NAMESPACE_END(detail)
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)