diff --git a/panda/src/event/pythonTask.cxx b/panda/src/event/pythonTask.cxx index 86200802a9..fdf6be440c 100644 --- a/panda/src/event/pythonTask.cxx +++ b/panda/src/event/pythonTask.cxx @@ -283,15 +283,25 @@ exception() const { */ int PythonTask:: __setattr__(PyObject *self, PyObject *attr, PyObject *v) { - if (PyObject_GenericSetAttr(self, attr, v) == 0) { - return 0; - } - - if (!PyErr_ExceptionMatches(PyExc_AttributeError)) { +#if PY_MAJOR_VERSION >= 3 + if (!PyUnicode_Check(attr)) { +#else + if (!PyString_Check(attr)) { +#endif + PyErr_Format(PyExc_TypeError, + "attribute name must be string, not '%.200s'", + attr->ob_type->tp_name); return -1; } - PyErr_Clear(); + PyObject *descr = _PyType_Lookup(Py_TYPE(self), attr); + if (descr != nullptr) { + Py_INCREF(descr); + descrsetfunc f = descr->ob_type->tp_descr_set; + if (f != nullptr) { + return f(descr, self, v); + } + } if (task_cat.is_debug()) { PyObject *str = PyObject_Repr(v); @@ -352,31 +362,18 @@ __delattr__(PyObject *self, PyObject *attr) { * object. */ PyObject *PythonTask:: -__getattr__(PyObject *attr) const { - // Note that with the new Interrogate behavior, this method behaves more - // like the Python __getattr__ rather than being directly assigned to the - // tp_getattro slot (a la __getattribute__). So, we won't get here when the - // attribute has already been found via other methods. - +__getattribute__(PyObject *self, PyObject *attr) const { + // We consult the instance dict first, since the user may have overridden a + // method or something. PyObject *item = PyDict_GetItem(__dict__, attr); - if (item == nullptr) { - // PyDict_GetItem does not raise an exception. -#if PY_MAJOR_VERSION < 3 - PyErr_Format(PyExc_AttributeError, - "'PythonTask' object has no attribute '%.400s'", - PyString_AS_STRING(attr)); -#else - PyErr_Format(PyExc_AttributeError, - "'PythonTask' object has no attribute '%U'", - attr); -#endif - return nullptr; + if (item != nullptr) { + // PyDict_GetItem returns a borrowed reference. + Py_INCREF(item); + return item; } - // PyDict_GetItem returns a borrowed reference. - Py_INCREF(item); - return item; + return PyObject_GenericGetAttr(self, attr); } /** diff --git a/panda/src/event/pythonTask.h b/panda/src/event/pythonTask.h index 06ff8b6fe4..0861827f94 100644 --- a/panda/src/event/pythonTask.h +++ b/panda/src/event/pythonTask.h @@ -55,7 +55,7 @@ public: PUBLISHED: int __setattr__(PyObject *self, PyObject *attr, PyObject *v); int __delattr__(PyObject *self, PyObject *attr); - PyObject *__getattr__(PyObject *attr) const; + PyObject *__getattribute__(PyObject *self, PyObject *attr) const; int __traverse__(visitproc visit, void *arg); int __clear__();