diff options
Diffstat (limited to 'include/pybind11/pytypes.h')
-rw-r--r-- | include/pybind11/pytypes.h | 1902 |
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) |