diff --git a/direct/src/distributed/cConnectionRepository.cxx b/direct/src/distributed/cConnectionRepository.cxx index 86b5869e14..8dd11eadbe 100644 --- a/direct/src/distributed/cConnectionRepository.cxx +++ b/direct/src/distributed/cConnectionRepository.cxx @@ -688,11 +688,12 @@ handle_update_field() { nassertr(doId2do != nullptr, false); PyObject *doId = PyLong_FromUnsignedLong(do_id); - PyObject *distobj = PyDict_GetItem(doId2do, doId); + PyObject *distobj; + int result = PyDict_GetItemRef(doId2do, doId, &distobj); Py_DECREF(doId); Py_DECREF(doId2do); - if (distobj != nullptr) { + if (result > 0) { PyObject *dclass_obj = PyObject_GetAttrString(distobj, "dclass"); nassertr(dclass_obj != nullptr, false); @@ -715,6 +716,7 @@ handle_update_field() { if (!cNeverDisable) { // in quiet zone and distobj is disable-able drop update on the // floor + Py_DECREF(distobj); #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) PyGILState_Release(gstate); #endif @@ -722,13 +724,8 @@ handle_update_field() { } } - // It's a good idea to ensure the reference count to distobj is raised - // while we call the update method--otherwise, the update method might - // get into trouble if it tried to delete the object from the doId2do - // map. - PyObject *distobj_ref = Py_NewRef(distobj); - invoke_extension(dclass).receive_update(distobj_ref, _di); - Py_DECREF(distobj_ref); + invoke_extension(dclass).receive_update(distobj, _di); + Py_DECREF(distobj); if (PyErr_Occurred()) { #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) @@ -778,10 +775,11 @@ handle_update_field_owner() { PyObject *doId = PyLong_FromUnsignedLong(do_id); // pass the update to the owner view first - PyObject *distobjOV = PyDict_GetItem(doId2ownerView, doId); + PyObject *distobjOV; + int result = PyDict_GetItemRef(doId2ownerView, doId, &distobjOV); Py_DECREF(doId2ownerView); - if (distobjOV != nullptr) { + if (result > 0) { PyObject *dclass_obj = PyObject_GetAttrString(distobjOV, "dclass"); nassertr(dclass_obj != nullptr, false); @@ -799,32 +797,29 @@ handle_update_field_owner() { int field_id = packer.raw_unpack_uint16(); DCField *field = dclass->get_field_by_index(field_id); if (field->is_ownrecv()) { - // It's a good idea to ensure the reference count to distobjOV is - // raised while we call the update method--otherwise, the update - // method might get into trouble if it tried to delete the object from - // the doId2do map. - PyObject *distobjOV_ref = Py_NewRef(distobjOV); // make a copy of the datagram iterator so that we can use the main // iterator for the non-owner update DatagramIterator _odi(_di); - invoke_extension(dclass).receive_update(distobjOV_ref, _odi); - Py_DECREF(distobjOV_ref); + invoke_extension(dclass).receive_update(distobjOV, _odi); if (PyErr_Occurred()) { + Py_DECREF(distobjOV); #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) PyGILState_Release(gstate); #endif return false; } } + Py_DECREF(distobjOV); } // now pass the update to the visible view - PyObject *distobj = PyDict_GetItem(doId2do, doId); + PyObject *distobj; + result = PyDict_GetItemRef(doId2do, doId, &distobj); Py_DECREF(doId); Py_DECREF(doId2do); - if (distobj != nullptr) { + if (result > 0) { PyObject *dclass_obj = PyObject_GetAttrString(distobj, "dclass"); nassertr(dclass_obj != nullptr, false); @@ -843,21 +838,17 @@ handle_update_field_owner() { //int field_id = packer.raw_unpack_uint16(); //DCField *field = dclass->get_field_by_index(field_id); if (true) {//field->is_broadcast()) { - // It's a good idea to ensure the reference count to distobj is raised - // while we call the update method--otherwise, the update method might - // get into trouble if it tried to delete the object from the doId2do - // map. - PyObject *distobj_ref = Py_NewRef(distobj); - invoke_extension(dclass).receive_update(distobj_ref, _di); - Py_DECREF(distobj_ref); + invoke_extension(dclass).receive_update(distobj, _di); if (PyErr_Occurred()) { + Py_DECREF(distobj); #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) PyGILState_Release(gstate); #endif return false; } } + Py_DECREF(distobj); } } @@ -957,12 +948,14 @@ describe_message(std::ostream &out, const string &prefix, nassertv(doId2do != nullptr); PyObject *doId = PyLong_FromUnsignedLong(do_id); - PyObject *distobj = PyDict_GetItem(doId2do, doId); + PyObject *distobj; + int result = PyDict_GetItemRef(doId2do, doId, &distobj); Py_DECREF(doId); Py_DECREF(doId2do); - if (distobj != nullptr) { + if (result > 0) { PyObject *dclass_obj = PyObject_GetAttrString(distobj, "dclass"); + Py_DECREF(distobj); nassertv(dclass_obj != nullptr); PyObject *dclass_this = PyObject_GetAttrString(dclass_obj, "this"); diff --git a/dtool/Config.cmake b/dtool/Config.cmake index 771e105be5..e9b1badd6d 100644 --- a/dtool/Config.cmake +++ b/dtool/Config.cmake @@ -265,7 +265,7 @@ if(BUILD_INTERROGATE) panda3d-interrogate GIT_REPOSITORY https://github.com/panda3d/interrogate.git - GIT_TAG e297384b30f1eae8ab839ec096fc3bb6bdb3edeb + GIT_TAG 742ea56a1934a7a05ee2518787cf6816f30ee096 PREFIX ${_interrogate_dir} CMAKE_ARGS diff --git a/dtool/src/dtoolbase/typeHandle_ext.cxx b/dtool/src/dtoolbase/typeHandle_ext.cxx index 5e0d80f2a8..be97efd0a6 100644 --- a/dtool/src/dtoolbase/typeHandle_ext.cxx +++ b/dtool/src/dtoolbase/typeHandle_ext.cxx @@ -49,19 +49,26 @@ __reduce__() const { PyTypeObject *py_type = _this->get_python_type(); if (py_type != nullptr && py_type->tp_dict != nullptr) { // Look for a get_class_type method, if it returns this handle. - PyObject *func = PyDict_GetItemString(py_type->tp_dict, "get_class_type"); - if (func != nullptr && PyCallable_Check(func)) { - PyObject *result = PyObject_CallNoArgs(func); - TypeHandle *result_handle = nullptr; - if (result == nullptr) { - // Never mind. - PyErr_Clear(); - } - else if (DtoolInstance_GetPointer(result, result_handle, Dtool_TypeHandle) && - *result_handle == *_this) { - // It returned the correct result, so we can use this. - return Py_BuildValue("O()", func); + PyObject *func; + int result = PyDict_GetItemStringRef(py_type->tp_dict, "get_class_type", &func); + if (result > 0) { + if (PyCallable_Check(func)) { + PyObject *result = PyObject_CallNoArgs(func); + TypeHandle *result_handle = nullptr; + if (result == nullptr) { + // Never mind. + PyErr_Clear(); + } + else if (DtoolInstance_GetPointer(result, result_handle, Dtool_TypeHandle) && + *result_handle == *_this) { + // It returned the correct result, so we can use this. + return Py_BuildValue("N()", func); + } } + Py_DECREF(func); + } + else if (result < 0) { + PyErr_Clear(); } } diff --git a/dtool/src/interrogatedb/py_compat.h b/dtool/src/interrogatedb/py_compat.h index 7ef6532c33..b24485cf88 100644 --- a/dtool/src/interrogatedb/py_compat.h +++ b/dtool/src/interrogatedb/py_compat.h @@ -285,6 +285,71 @@ INLINE bool PyLong_IsNonNegative(PyObject *value) { # define PyLong_AsInt(x) (_PyLong_AsInt(x)) #endif +#if PY_VERSION_HEX < 0x030D00A1 +ALWAYS_INLINE int +PyModule_Add(PyObject *mod, const char *name, PyObject *value) { + int res = PyModule_AddObjectRef(mod, name, value); + Py_XDECREF(value); + return res; +} +#endif + +#if PY_VERSION_HEX < 0x030D00A1 +INLINE int +PyDict_GetItemRef(PyObject *mp, PyObject *key, PyObject **result) { +#if PY_MAJOR_VERSION >= 3 + PyObject *item = PyDict_GetItemWithError(mp, key); +#else + PyObject *item = _PyDict_GetItemWithError(mp, key); +#endif + if (item != nullptr) { + *result = Py_NewRef(item); + return 1; + } + *result = nullptr; + return PyErr_Occurred() ? -1 : 0; +} + +INLINE int +PyDict_GetItemStringRef(PyObject *mp, const char *key, PyObject **result) { + PyObject *item = nullptr; +#if PY_MAJOR_VERSION >= 3 + PyObject *key_obj = PyUnicode_FromString(key); + item = key_obj ? PyDict_GetItemWithError(mp, key_obj) : nullptr; +#else + PyObject *key_obj = PyString_FromString(key); + item = key_obj ? _PyDict_GetItemWithError(mp, key_obj) : nullptr; +#endif + Py_DECREF(key_obj); + if (item != nullptr) { + *result = Py_NewRef(item); + return 1; + } + *result = nullptr; + return PyErr_Occurred() ? -1 : 0; +} +#endif + +#if PY_VERSION_HEX >= 0x03050200 && PY_VERSION_HEX < 0x030D00A1 +# define PyThreadState_GetUnchecked() (_PyThreadState_UncheckedGet()) +#endif + +#if PY_VERSION_HEX < 0x030D00A2 +# define PyList_Extend(list, iterable) (PyList_SetSlice((list), PY_SSIZE_T_MAX, PY_SSIZE_T_MAX, (iterable))) +# define PyList_Clear(list) (PyList_SetSlice((list), 0, PY_SSIZE_T_MAX, nullptr)) +#endif + +#if PY_VERSION_HEX < 0x030D00A4 +# define PyList_GetItemRef(op, index) (Py_XNewRef(PyList_GetItem((op), (index)))) +#endif + +#if PY_VERSION_HEX < 0x030D00B3 +# define Py_BEGIN_CRITICAL_SECTION(op) { +# define Py_END_CRITICAL_SECTION() } +# define Py_BEGIN_CRITICAL_SECTION2(a, b) { +# define Py_END_CRITICAL_SECTION2() } +#endif + /* Other Python implementations */ #endif // HAVE_PYTHON diff --git a/dtool/src/interrogatedb/py_panda.cxx b/dtool/src/interrogatedb/py_panda.cxx index 7cabfe0485..f8b2881039 100644 --- a/dtool/src/interrogatedb/py_panda.cxx +++ b/dtool/src/interrogatedb/py_panda.cxx @@ -328,9 +328,9 @@ static PyObject *Dtool_EnumType_New(PyTypeObject *subtype, PyObject *args, PyObj PyObject *value2member = PyDict_GetItemString(subtype->tp_dict, "_value2member_map_"); nassertr_always(value2member != nullptr, nullptr); - PyObject *member = PyDict_GetItem(value2member, arg); - if (member != nullptr) { - return Py_NewRef(member); + PyObject *member; + if (PyDict_GetItemRef(value2member, arg, &member) > 0) { + return member; } PyObject *repr = PyObject_Repr(arg); diff --git a/dtool/src/interrogatedb/py_wrappers.cxx b/dtool/src/interrogatedb/py_wrappers.cxx index f0be01a3fb..57965b737e 100644 --- a/dtool/src/interrogatedb/py_wrappers.cxx +++ b/dtool/src/interrogatedb/py_wrappers.cxx @@ -24,16 +24,18 @@ static void _register_collection(PyTypeObject *type, const char *abc) { PyObject *dict = PyModule_GetDict(module); if (module != nullptr) { #if PY_MAJOR_VERSION >= 3 - static PyObject *register_str = PyUnicode_InternFromString("register"); + PyObject *register_str = PyUnicode_InternFromString("register"); #else - static PyObject *register_str = PyString_InternFromString("register"); + PyObject *register_str = PyString_InternFromString("register"); #endif - PyObject *sequence = PyDict_GetItemString(dict, abc); - if (sequence != nullptr) { - if (PyObject_CallMethodOneArg(sequence, register_str, (PyObject *)type) == nullptr) { - PyErr_Print(); - } + PyObject *obj = nullptr; + if (register_str == nullptr || + PyDict_GetItemStringRef(dict, abc, &obj) <= 0 || + PyObject_CallMethodOneArg(obj, register_str, (PyObject *)type) == nullptr) { + PyErr_Print(); } + Py_XDECREF(obj); + Py_XDECREF(register_str); } } } @@ -1075,14 +1077,19 @@ static PyObject *Dtool_MutableMappingWrapper_update(PyObject *self, PyObject *ar return PyErr_Format(PyExc_TypeError, "%s.update() takes either a dict argument or keyword arguments", wrap->_base._name); } + PyObject *result = Py_None; PyObject *key, *value; Py_ssize_t pos = 0; + Py_BEGIN_CRITICAL_SECTION(dict); while (PyDict_Next(dict, &pos, &key, &value)) { if (wrap->_setitem_func(wrap->_base._self, key, value) != 0) { - return nullptr; + result = nullptr; + break; } } - return Py_NewRef(Py_None); + Py_END_CRITICAL_SECTION(); + + return Py_XNewRef(result); } /** diff --git a/makepanda/makepandacore.py b/makepanda/makepandacore.py index 843687ef2e..4729be69b7 100644 --- a/makepanda/makepandacore.py +++ b/makepanda/makepandacore.py @@ -592,8 +592,8 @@ def GetInterrogateDir(): return INTERROGATE_DIR dir = os.path.join(GetOutputDir(), "tmp", "interrogate") - if not os.path.isdir(os.path.join(dir, "panda3d_interrogate-0.1.0.dist-info")): - oscmd("\"%s\" -m pip install --force-reinstall -t \"%s\" panda3d-interrogate==0.1.0" % (sys.executable, dir)) + if not os.path.isdir(os.path.join(dir, "panda3d_interrogate-0.1.1.dist-info")): + oscmd("\"%s\" -m pip install --force-reinstall -t \"%s\" panda3d-interrogate==0.1.1" % (sys.executable, dir)) INTERROGATE_DIR = dir diff --git a/panda/src/display/frameBufferProperties_ext.cxx b/panda/src/display/frameBufferProperties_ext.cxx index 96ed9d3afe..423d66d748 100644 --- a/panda/src/display/frameBufferProperties_ext.cxx +++ b/panda/src/display/frameBufferProperties_ext.cxx @@ -52,13 +52,14 @@ __setstate__(PyObject *self, PyObject *props) { PyObject *key, *value; Py_ssize_t pos = 0; + Py_BEGIN_CRITICAL_SECTION(props); while (PyDict_Next(props, &pos, &key, &value)) { // Look for a writable property on the type by this name. PyObject *descr = _PyType_Lookup(type, key); if (descr != nullptr && Py_TYPE(descr)->tp_descr_set != nullptr) { if (Py_TYPE(descr)->tp_descr_set(descr, self, value) < 0) { - return; + break; } } else { PyObject *key_repr = PyObject_Repr(key); @@ -67,9 +68,10 @@ __setstate__(PyObject *self, PyObject *props) { PyUnicode_AsUTF8(key_repr) ); Py_DECREF(key_repr); - return; + break; } } + Py_END_CRITICAL_SECTION(); } #endif // HAVE_PYTHON diff --git a/panda/src/display/windowProperties_ext.cxx b/panda/src/display/windowProperties_ext.cxx index 45b32e822c..d9975750b7 100644 --- a/panda/src/display/windowProperties_ext.cxx +++ b/panda/src/display/windowProperties_ext.cxx @@ -91,13 +91,14 @@ __setstate__(PyObject *self, PyObject *props) { PyObject *key, *value; Py_ssize_t pos = 0; + Py_BEGIN_CRITICAL_SECTION(props); while (PyDict_Next(props, &pos, &key, &value)) { // Look for a writable property on the type by this name. PyObject *descr = _PyType_Lookup(type, key); if (descr != nullptr && Py_TYPE(descr)->tp_descr_set != nullptr) { if (Py_TYPE(descr)->tp_descr_set(descr, self, value) < 0) { - return; + break; } } else { PyObject *key_repr = PyObject_Repr(key); @@ -106,9 +107,10 @@ __setstate__(PyObject *self, PyObject *props) { PyUnicode_AsUTF8(key_repr) ); Py_DECREF(key_repr); - return; + break; } } + Py_END_CRITICAL_SECTION(); } #endif // HAVE_PYTHON diff --git a/panda/src/egg/eggNode_ext.cxx b/panda/src/egg/eggNode_ext.cxx index 3056e40d6d..6109c62c49 100644 --- a/panda/src/egg/eggNode_ext.cxx +++ b/panda/src/egg/eggNode_ext.cxx @@ -30,9 +30,12 @@ __reduce__() const { PyObject *module_name = PyObject_GetAttrString((PyObject *)&Dtool_EggNode, "__module__"); nassertr_always(module_name != nullptr, nullptr); - PyObject *module = PyDict_GetItem(sys_modules, module_name); + PyObject *module; + int res = PyDict_GetItemRef(sys_modules, module_name, &module); Py_DECREF(module_name); - nassertr_always(module != nullptr, nullptr); + if (res <= 0) { + return nullptr; + } PyObject *func; if (_this->is_of_type(EggData::get_class_type())) { @@ -40,6 +43,7 @@ __reduce__() const { } else { func = PyObject_GetAttrString(module, "parse_egg_node"); } + Py_DECREF(module); nassertr_always(func != nullptr, nullptr); // Get the egg syntax to pass to the parse_egg_node function. diff --git a/panda/src/event/pythonTask.cxx b/panda/src/event/pythonTask.cxx index 6e513dbe4c..4c00e2db7b 100644 --- a/panda/src/event/pythonTask.cxx +++ b/panda/src/event/pythonTask.cxx @@ -363,11 +363,9 @@ PyObject *PythonTask:: __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 returns a borrowed reference. - return Py_NewRef(item); + PyObject *item; + if (PyDict_GetItemRef(__dict__, attr, &item) > 0) { + return item; } return PyObject_GenericGetAttr(self, attr); diff --git a/panda/src/pgraph/nodePath_ext.cxx b/panda/src/pgraph/nodePath_ext.cxx index 23cfb172d2..7995717979 100644 --- a/panda/src/pgraph/nodePath_ext.cxx +++ b/panda/src/pgraph/nodePath_ext.cxx @@ -56,10 +56,10 @@ __deepcopy__(PyObject *self, PyObject *memo) const { extern struct Dtool_PyTypedObject Dtool_NodePath; // Borrowed reference. - PyObject *dupe = PyDict_GetItem(memo, self); - if (dupe != nullptr) { + PyObject *dupe; + if (PyDict_GetItemRef(memo, self, &dupe) > 0) { // Already in the memo dictionary. - return Py_NewRef(dupe); + return dupe; } NodePath *np_dupe; @@ -279,19 +279,21 @@ set_shader_inputs(PyObject *args, PyObject *kwargs) { PyObject *key, *value; Py_ssize_t pos = 0; + Py_BEGIN_CRITICAL_SECTION(dict); while (PyDict_Next(kwargs, &pos, &key, &value)) { char *buffer; Py_ssize_t length; buffer = (char *)PyUnicode_AsUTF8AndSize(key, &length); if (buffer == nullptr) { Dtool_Raise_TypeError("NodePath.set_shader_inputs accepts only string keywords"); - return; + break; } CPT_InternalName name(std::string(buffer, length)); ShaderInput &input = attrib->_inputs[name]; invoke_extension(&input).__init__(std::move(name), value); } + Py_END_CRITICAL_SECTION(); if (!PyErr_Occurred()) { node->set_attrib(ShaderAttrib::return_new(attrib)); diff --git a/panda/src/pgraph/pandaNode_ext.cxx b/panda/src/pgraph/pandaNode_ext.cxx index cd7ca8b252..f1d25ed08e 100644 --- a/panda/src/pgraph/pandaNode_ext.cxx +++ b/panda/src/pgraph/pandaNode_ext.cxx @@ -47,10 +47,10 @@ __deepcopy__(PyObject *self, PyObject *memo) const { extern struct Dtool_PyTypedObject Dtool_PandaNode; // Borrowed reference. - PyObject *dupe = PyDict_GetItem(memo, self); - if (dupe != nullptr) { + PyObject *dupe; + if (PyDict_GetItemRef(memo, self, &dupe) > 0) { // Already in the memo dictionary. - return Py_NewRef(dupe); + return dupe; } PT(PandaNode) node_dupe = _this->copy_subgraph(); @@ -122,12 +122,11 @@ get_python_tag(PyObject *key) const { } PyObject *dict = ((PythonTagDataImpl *)_this->_python_tag_data.p())->_dict; - PyObject *value = PyDict_GetItem(dict, key); - if (value == nullptr) { - value = Py_None; + PyObject *value; + if (PyDict_GetItemRef(dict, key, &value) == 0) { + value = Py_NewRef(Py_None); } - // PyDict_GetItem returns a borrowed reference. - return Py_NewRef(value); + return value; } /** @@ -142,7 +141,7 @@ has_python_tag(PyObject *key) const { } PyObject *dict = ((PythonTagDataImpl *)_this->_python_tag_data.p())->_dict; - return (PyDict_GetItem(dict, key) != nullptr); + return PyDict_Contains(dict, key); } /** @@ -157,7 +156,8 @@ clear_python_tag(PyObject *key) { } PyObject *dict = do_get_python_tags(); - if (PyDict_GetItem(dict, key) != nullptr) { + Py_BEGIN_CRITICAL_SECTION(dict); + if (PyDict_Contains(dict, key)) { PyDict_DelItem(dict, key); } @@ -166,6 +166,7 @@ clear_python_tag(PyObject *key) { // unique reference to the tags, so clear the tag object. _this->_python_tag_data.clear(); } + Py_END_CRITICAL_SECTION(); } /** diff --git a/panda/src/pgraph/renderState_ext.cxx b/panda/src/pgraph/renderState_ext.cxx index 6cfe05965e..085d79d6f9 100644 --- a/panda/src/pgraph/renderState_ext.cxx +++ b/panda/src/pgraph/renderState_ext.cxx @@ -56,7 +56,7 @@ make(PyObject *args, PyObject *kwds) { int override = 0; if (py_override != nullptr) { - override = _PyLong_AsInt(py_override); + override = PyLong_AsInt(py_override); if (override == -1 && PyErr_Occurred()) { return nullptr; } diff --git a/panda/src/pgraph/shaderAttrib_ext.cxx b/panda/src/pgraph/shaderAttrib_ext.cxx index b599d5ab25..012095935a 100644 --- a/panda/src/pgraph/shaderAttrib_ext.cxx +++ b/panda/src/pgraph/shaderAttrib_ext.cxx @@ -46,22 +46,28 @@ set_shader_inputs(PyObject *args, PyObject *kwargs) const { PyObject *key, *value; Py_ssize_t pos = 0; + Py_BEGIN_CRITICAL_SECTION(kwargs); while (PyDict_Next(kwargs, &pos, &key, &value)) { char *buffer; Py_ssize_t length; buffer = (char *)PyUnicode_AsUTF8AndSize(key, &length); if (buffer == nullptr) { Dtool_Raise_TypeError("ShaderAttrib.set_shader_inputs accepts only string keywords"); - delete attrib; - return nullptr; + break; } CPT_InternalName name(std::string(buffer, length)); ShaderInput &input = attrib->_inputs[name]; invoke_extension(&input).__init__(std::move(name), value); } + Py_END_CRITICAL_SECTION(); - return ShaderAttrib::return_new(attrib); + if (!PyErr_Occurred()) { + return ShaderAttrib::return_new(attrib); + } else { + delete attrib; + return nullptr; + } } #endif // HAVE_PYTHON diff --git a/panda/src/pstatclient/pStatClient_ext.cxx b/panda/src/pstatclient/pStatClient_ext.cxx index 0724470cfb..f786b07dcd 100644 --- a/panda/src/pstatclient/pStatClient_ext.cxx +++ b/panda/src/pstatclient/pStatClient_ext.cxx @@ -72,11 +72,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); @@ -85,6 +95,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 @@ -179,23 +194,26 @@ make_c_function_collector(PyCFunctionObject *meth) { len = (dot - cls->tp_name) + 1; } else { // If there's no module name, we need to get it from __module__. - PyObject *py_mod_name = cls->tp_dict ? PyDict_GetItemString(cls->tp_dict, "__module__") : nullptr; + PyObject *py_mod_name = nullptr; const char *mod_name = nullptr; - if (py_mod_name != nullptr) { + if (cls->tp_dict != nullptr && + PyDict_GetItemStringRef(cls->tp_dict, "__module__", &py_mod_name) > 0) { if (PyUnicode_Check(py_mod_name)) { mod_name = PyUnicode_AsUTF8(py_mod_name); } else { // Might be a descriptor. + Py_DECREF(py_mod_name); py_mod_name = PyObject_GetAttrString(meth->m_self, "__module__"); if (py_mod_name != nullptr) { if (PyUnicode_Check(py_mod_name)) { mod_name = PyUnicode_AsUTF8(py_mod_name); } - Py_DECREF(py_mod_name); } else PyErr_Clear(); } } + else PyErr_Clear(); + if (mod_name == nullptr) { // Is it a built-in, like int or dict? PyObject *builtins = PyEval_GetBuiltins(); @@ -206,6 +224,7 @@ make_c_function_collector(PyCFunctionObject *meth) { } } len = snprintf(buffer, sizeof(buffer), "%s:%s:%s()", mod_name, cls->tp_name, meth->m_ml->ml_name) - 2; + Py_XDECREF(py_mod_name); } } else if (meth->m_self != nullptr) {