diff --git a/include/pybind11/detail/init.h b/include/pybind11/detail/init.h index ed95afe58c..92b20cb972 100644 --- a/include/pybind11/detail/init.h +++ b/include/pybind11/detail/init.h @@ -451,7 +451,16 @@ void setstate(value_and_holder &v_h, std::pair &&result, bool need_alias) // See PR #2972 for details. return; } - setattr((PyObject *) v_h.inst, "__dict__", d); + // Our tests never run into an unset dict, but being careful here for now (see #5658) + auto dict = getattr((PyObject *) v_h.inst, "__dict__", none()); + if (dict.is_none()) { + setattr((PyObject *) v_h.inst, "__dict__", d); + } else { + // Keep the original object dict and just update it + if (PyDict_Update(dict.ptr(), d.ptr()) < 0) { + throw error_already_set(); + } + } } /// Implementation for py::pickle(GetState, SetState) diff --git a/tests/test_pickling.cpp b/tests/test_pickling.cpp index e154bc483c..6ba3d30eb9 100644 --- a/tests/test_pickling.cpp +++ b/tests/test_pickling.cpp @@ -35,10 +35,7 @@ void wrap(py::module m) { .def_readwrite("num", &SimpleBase::num) .def(py::pickle( [](const py::object &self) { - py::dict d; - if (py::hasattr(self, "__dict__")) { - d = self.attr("__dict__"); - } + py::dict d = py::getattr(self, "__dict__", py::dict()); return py::make_tuple(self.attr("num"), d); }, [](const py::tuple &t) {