diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 9938b0b71..4d7bc5c73 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -636,7 +636,7 @@ protected: bool bad_arg = false; for (; args_copied < args_to_copy; ++args_copied) { const argument_record *arg_rec = args_copied < func.args.size() ? &func.args[args_copied] : nullptr; - if (kwargs_in && arg_rec && arg_rec->name && PyDict_GetItemString(kwargs_in, arg_rec->name)) { + if (kwargs_in && arg_rec && arg_rec->name && dict_getitemstring(kwargs_in, arg_rec->name)) { bad_arg = true; break; } @@ -684,7 +684,7 @@ protected: handle value; if (kwargs_in && arg_rec.name) - value = PyDict_GetItemString(kwargs.ptr(), arg_rec.name); + value = dict_getitemstring(kwargs.ptr(), arg_rec.name); if (value) { // Consume a kwargs value @@ -692,7 +692,9 @@ protected: kwargs = reinterpret_steal(PyDict_Copy(kwargs.ptr())); copied_kwargs = true; } - PyDict_DelItemString(kwargs.ptr(), arg_rec.name); + if (PyDict_DelItemString(kwargs.ptr(), arg_rec.name) == -1) { + throw error_already_set(); + } } else if (arg_rec.value) { value = arg_rec.value; } @@ -2139,7 +2141,7 @@ inline function get_type_override(const void *this_ptr, const type_info *this_ty if (frame && (std::string) str(frame->f_code->co_name) == name && frame->f_code->co_argcount > 0) { PyFrame_FastToLocals(frame); - PyObject *self_caller = PyDict_GetItem( + PyObject *self_caller = dict_getitem( frame->f_locals, PyTuple_GET_ITEM(frame->f_code->co_varnames, 0)); if (self_caller == self.ptr()) return function(); diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index d1d92af6a..80637fb98 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -485,6 +485,43 @@ inline handle get_function(handle value) { return value; } +// 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) +{ +#if PY_MAJOR_VERSION >= 3 + PyObject *kv, *rv; + kv = PyUnicode_FromString(key); + if (kv == NULL) { + throw error_already_set(); + } + + rv = PyDict_GetItemWithError(v, kv); + Py_DECREF(kv); + if (rv == NULL && PyErr_Occurred()) { + throw error_already_set(); + } + return rv; +#else + return PyDict_GetItemString(v, key); +#endif +} + +inline PyObject * dict_getitem(PyObject *v, PyObject *key) +{ +#if PY_MAJOR_VERSION >= 3 + PyObject *rv = PyDict_GetItemWithError(v, key); + if (rv == NULL && PyErr_Occurred()) { + throw error_already_set(); + } + return rv; +#else + return PyDict_GetItem(v, key); +#endif +} + // 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`.