diff --git a/panda/src/event/pythonTask.cxx b/panda/src/event/pythonTask.cxx index 4b3a79f7ef..f5af3f48de 100644 --- a/panda/src/event/pythonTask.cxx +++ b/panda/src/event/pythonTask.cxx @@ -368,6 +368,17 @@ __getattr__(PyObject *attr) const { // tp_getattro slot (a la __getattribute__). So, we won't get here when the // attribute has already been found via other methods. +#if PY_VERSION_HEX >= 0x030D00A1 // 3.13 + PyObject *item = nullptr; + if (PyDict_GetItemRef(__dict__, attr, &item) == 0) { + // PyDict_GetItemRef does not raise an exception on missing attribute. + PyErr_Format(PyExc_AttributeError, + "'PythonTask' object has no attribute '%U'", + attr); + } + return item; + +#else // 3.12 and lower PyObject *item = PyDict_GetItem(__dict__, attr); if (item == nullptr) { @@ -387,6 +398,7 @@ __getattr__(PyObject *attr) const { // PyDict_GetItem returns a borrowed reference. Py_INCREF(item); return item; +#endif } /** diff --git a/panda/src/pgraph/nodePath_ext.cxx b/panda/src/pgraph/nodePath_ext.cxx index c4f7925f24..61e6e74907 100644 --- a/panda/src/pgraph/nodePath_ext.cxx +++ b/panda/src/pgraph/nodePath_ext.cxx @@ -55,6 +55,13 @@ PyObject *Extension:: __deepcopy__(PyObject *self, PyObject *memo) const { extern struct Dtool_PyTypedObject Dtool_NodePath; +#if PY_VERSION_HEX >= 0x030D00A1 // 3.13 + PyObject *dupe; + if (PyDict_GetItemRef(memo, self, &dupe) != 0) { + // Already in the memo dictionary (or an error happened). + return dupe; + } +#else // Borrowed reference. PyObject *dupe = PyDict_GetItem(memo, self); if (dupe != nullptr) { @@ -62,6 +69,7 @@ __deepcopy__(PyObject *self, PyObject *memo) const { Py_INCREF(dupe); return dupe; } +#endif NodePath *np_dupe; if (_this->is_empty()) { diff --git a/panda/src/pgraph/pandaNode_ext.cxx b/panda/src/pgraph/pandaNode_ext.cxx index d4071ff4c6..95e04aca14 100644 --- a/panda/src/pgraph/pandaNode_ext.cxx +++ b/panda/src/pgraph/pandaNode_ext.cxx @@ -46,6 +46,13 @@ PyObject *Extension:: __deepcopy__(PyObject *self, PyObject *memo) const { extern struct Dtool_PyTypedObject Dtool_PandaNode; +#if PY_VERSION_HEX >= 0x030D00A1 // 3.13 + PyObject *dupe; + if (PyDict_GetItemRef(memo, self, &dupe) != 0) { + // Already in the memo dictionary (or an error happened). + return dupe; + } +#else // Borrowed reference. PyObject *dupe = PyDict_GetItem(memo, self); if (dupe != nullptr) { @@ -53,6 +60,7 @@ __deepcopy__(PyObject *self, PyObject *memo) const { Py_INCREF(dupe); return dupe; } +#endif PT(PandaNode) node_dupe = _this->copy_subgraph(); diff --git a/panda/src/pstatclient/pStatClient_ext.cxx b/panda/src/pstatclient/pStatClient_ext.cxx index 684ac1db2a..6071b7dfbc 100644 --- a/panda/src/pstatclient/pStatClient_ext.cxx +++ b/panda/src/pstatclient/pStatClient_ext.cxx @@ -73,11 +73,21 @@ __declspec(noinline) make_python_frame_collector(PyFrameObject *frame, PyCodeObject *code) { #if PY_VERSION_HEX >= 0x030B0000 // 3.11 // Fetch the module name out of the frame's global scope. + const char *mod_name = ""; + PyObject *py_mod_name = nullptr; PyObject *globals = PyFrame_GetGlobals(frame); - PyObject *py_mod_name = PyDict_GetItemString(globals, "__name__"); +#if PY_VERSION_HEX >= 0x030D00A1 // 3.13 + if (PyDict_GetItemStringRef(globals, "__name__", &py_mod_name) > 0) { + mod_name = PyUnicode_AsUTF8(py_mod_name); + } +#else + py_mod_name = PyDict_GetItemString(globals, "__name__"); + if (py_mod_name != nullptr) { + mod_name = PyUnicode_AsUTF8(py_mod_name); + } +#endif Py_DECREF(globals); - const char *mod_name = py_mod_name ? PyUnicode_AsUTF8(py_mod_name) : ""; const char *meth_name = PyUnicode_AsUTF8(code->co_qualname); char buffer[1024]; size_t len = snprintf(buffer, sizeof(buffer), "%s:%s", mod_name, meth_name); @@ -86,6 +96,11 @@ make_python_frame_collector(PyFrameObject *frame, PyCodeObject *code) { buffer[i] = ':'; } } + +#if PY_VERSION_HEX >= 0x030D00A1 // 3.13 + Py_XDECREF(py_mod_name); +#endif + #else // Try to figure out the type name. There's no obvious way to do this. // It's possible that the first argument passed to this function is the