Changes for compatibility with Python 3.13

Includes some preliminary work to make free-threading safe, see also #1683
This commit is contained in:
rdb 2024-09-19 16:00:59 +02:00
parent b32459ffa1
commit 5106fc879f
16 changed files with 196 additions and 90 deletions

View File

@ -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");

View File

@ -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

View File

@ -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();
}
}

View File

@ -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

View File

@ -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);

View File

@ -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);
}
/**

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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);

View File

@ -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));

View File

@ -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();
}
/**

View File

@ -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;
}

View File

@ -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

View File

@ -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 = "<unknown>";
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) : "<unknown>";
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) {