From: Claude Code Subject: [PATCH] Fix PyQt4 compatibility with Python 3.11+ This patch fixes PyQt4 (v4.12.x) for compatibility with Python 3.11, 3.12, and 3.13. Changes: 1. Fix PyFrameObject access (frame->f_back) in qpycore_classinfo.cpp - CRITICAL for Python 3.11+ - Use PyFrame_GetBack() API which properly handles strong references 2. Replace deprecated PyWeakref_GetObject() in qpycore_qtlib.cpp for Python 3.13+ - Use PyWeakref_GetRef() with proper signature (returns int, takes PyObject** out param) 3. Replace removed PyCFunction_Call() in qpycore_pyqtsignal.cpp for Python 3.9+ - Use PyObject_Call() which works for all callable objects 4. Replace removed PyEval_CallObject() in qpycore_qtlib.cpp for Python 3.13+ - Use PyObject_Call() with NULL for kwargs 5. Replace removed PyObject_AsCharBuffer() in qpycore_qstring.cpp for Python 3.13+ - Use new buffer protocol: PyObject_GetBuffer() with PyBuffer_Release() - Convert buffer to bytes to ensure data lifetime 6. Add missing struct field initializers for Python 3.13 in all PyTypeObject declarations: - tp_vectorcall (Python 3.8+) - tp_watched (Python 3.13+) - tp_versions_used (Python 3.13+) diff -Naur PyQt4_gpl_mac-4.12.3.orig/qpy/QtCore/qpycore_classinfo.cpp PyQt4_gpl_mac-4.12.3/qpy/QtCore/qpycore_classinfo.cpp --- PyQt4_gpl_mac-4.12.3.orig/qpy/QtCore/qpycore_classinfo.cpp 2018-08-31 07:40:13.000000000 +0000 +++ PyQt4_gpl_mac-4.12.3/qpy/QtCore/qpycore_classinfo.cpp 2026-03-10 10:01:21.995513387 +0000 @@ -36,7 +36,17 @@ // We need the frame we were called from, not the current one. if (frame) + { +#if PY_VERSION_HEX >= 0x030b0000 + /* Python 3.11+: use PyFrame_GetBack() which returns a strong reference */ + PyFrameObject *back = PyFrame_GetBack(frame); + Py_XDECREF(frame); + frame = back; +#else + /* Python 3.10 and earlier: direct structure access */ frame = frame->f_back; +#endif + } if (!frame) { diff -Naur PyQt4_gpl_mac-4.12.3.orig/qpy/QtCore/qpycore_pyqtboundsignal.cpp PyQt4_gpl_mac-4.12.3/qpy/QtCore/qpycore_pyqtboundsignal.cpp --- PyQt4_gpl_mac-4.12.3.orig/qpy/QtCore/qpycore_pyqtboundsignal.cpp 2018-08-31 07:40:13.000000000 +0000 +++ PyQt4_gpl_mac-4.12.3/qpy/QtCore/qpycore_pyqtboundsignal.cpp 2026-03-10 10:01:47.287166208 +0000 @@ -184,6 +184,15 @@ #if PY_VERSION_HEX >= 0x03040000 0, /* tp_finalize */ #endif +#if PY_VERSION_HEX >= 0x03080000 + 0, /* tp_vectorcall */ +#endif +#if PY_VERSION_HEX >= 0x030d0000 + 0, /* tp_watched */ +#endif +#if PY_VERSION_HEX >= 0x030d0000 + 0, /* tp_versions_used */ +#endif }; diff -Naur PyQt4_gpl_mac-4.12.3.orig/qpy/QtCore/qpycore_pyqtmethodproxy.cpp PyQt4_gpl_mac-4.12.3/qpy/QtCore/qpycore_pyqtmethodproxy.cpp --- PyQt4_gpl_mac-4.12.3.orig/qpy/QtCore/qpycore_pyqtmethodproxy.cpp 2018-08-31 07:40:13.000000000 +0000 +++ PyQt4_gpl_mac-4.12.3/qpy/QtCore/qpycore_pyqtmethodproxy.cpp 2026-03-10 10:01:47.763178496 +0000 @@ -99,6 +99,15 @@ #if PY_VERSION_HEX >= 0x03040000 0, /* tp_finalize */ #endif +#if PY_VERSION_HEX >= 0x03080000 + 0, /* tp_vectorcall */ +#endif +#if PY_VERSION_HEX >= 0x030d0000 + 0, /* tp_watched */ +#endif +#if PY_VERSION_HEX >= 0x030d0000 + 0, /* tp_versions_used */ +#endif }; diff -Naur PyQt4_gpl_mac-4.12.3.orig/qpy/QtCore/qpycore_pyqtsignal.cpp PyQt4_gpl_mac-4.12.3/qpy/QtCore/qpycore_pyqtsignal.cpp --- PyQt4_gpl_mac-4.12.3.orig/qpy/QtCore/qpycore_pyqtsignal.cpp 2018-08-31 07:40:13.000000000 +0000 +++ PyQt4_gpl_mac-4.12.3/qpy/QtCore/qpycore_pyqtsignal.cpp 2026-03-10 10:10:46.401099609 +0000 @@ -132,6 +132,15 @@ #if PY_VERSION_HEX >= 0x03040000 0, /* tp_finalize */ #endif +#if PY_VERSION_HEX >= 0x03080000 + 0, /* tp_vectorcall */ +#endif +#if PY_VERSION_HEX >= 0x030d0000 + 0, /* tp_watched */ +#endif +#if PY_VERSION_HEX >= 0x030d0000 + 0, /* tp_versions_used */ +#endif }; @@ -658,7 +667,12 @@ if (!func) return 0; +#if PY_VERSION_HEX >= 0x03090000 + /* Python 3.9+: PyCFunction_Call deprecated, use PyObject_Call */ + PyObject *result = PyObject_Call(func, args, kw); +#else PyObject *result = PyCFunction_Call(func, args, kw); +#endif Py_DECREF(func); diff -Naur PyQt4_gpl_mac-4.12.3.orig/qpy/QtCore/qpycore_qstring.cpp PyQt4_gpl_mac-4.12.3/qpy/QtCore/qpycore_qstring.cpp --- PyQt4_gpl_mac-4.12.3.orig/qpy/QtCore/qpycore_qstring.cpp 2018-08-31 07:40:13.000000000 +0000 +++ PyQt4_gpl_mac-4.12.3/qpy/QtCore/qpycore_qstring.cpp 2026-03-10 10:15:12.857993744 +0000 @@ -298,10 +298,28 @@ Py_INCREF(obj); } #endif +#if PY_VERSION_HEX >= 0x030d0000 + /* Python 3.13+: PyObject_AsCharBuffer removed, use new buffer protocol */ + else if (PyObject_CheckBuffer(obj)) + { + Py_buffer view; + if (PyObject_GetBuffer(obj, &view, PyBUF_SIMPLE) >= 0) + { + /* Convert to bytes to ensure data lifetime */ + obj = PyBytes_FromStringAndSize((const char *)view.buf, view.len); + PyBuffer_Release(&view); + if (obj) + { + es = PyBytes_AS_STRING(obj); + } + } + } +#else else if (PyObject_AsCharBuffer(obj, &es, &sz) >= 0) { Py_INCREF(obj); } +#endif if (es) { diff -Naur PyQt4_gpl_mac-4.12.3.orig/qpy/QtCore/qpycore_qtlib.cpp PyQt4_gpl_mac-4.12.3/qpy/QtCore/qpycore_qtlib.cpp --- PyQt4_gpl_mac-4.12.3.orig/qpy/QtCore/qpycore_qtlib.cpp 2018-08-31 07:40:13.000000000 +0000 +++ PyQt4_gpl_mac-4.12.3/qpy/QtCore/qpycore_qtlib.cpp 2026-03-10 10:15:18.261133570 +0000 @@ -45,12 +45,27 @@ } else { +#if PY_VERSION_HEX >= 0x030d0000 + /* Python 3.13+: PyWeakref_GetRef returns 1 if alive, 0 if dead, -1 on error */ + int rc = PyWeakref_GetRef(slot->weakSlot, &sref); + if (rc < 0) + return 0; /* Error */ + if (rc == 0) + { + /* Referent is dead, return Py_None */ + Py_INCREF(Py_None); + return Py_None; + } + /* rc == 1: sref now holds a strong reference, no need to INCREF */ +#else sref = PyWeakref_GetObject(slot->weakSlot); if (!sref) return 0; +#endif } +#if PY_VERSION_HEX < 0x030d0000 if (sref == Py_None) { // If the real object has gone then we pretend everything is Ok. This @@ -61,6 +76,7 @@ } Py_XINCREF(sref); +#endif if (!slot->pyobj) { @@ -127,7 +143,12 @@ for (;;) { +#if PY_VERSION_HEX >= 0x030d0000 + /* Python 3.13+: PyEval_CallObject removed, use PyObject_Call */ + PyObject *resobj = PyObject_Call(sfunc, sa, NULL); +#else PyObject *resobj = PyEval_CallObject(sfunc, sa); +#endif if (resobj) { diff -Naur PyQt4_gpl_mac-4.12.3.orig/qpy/QtCore/qpycore_types.cpp PyQt4_gpl_mac-4.12.3/qpy/QtCore/qpycore_types.cpp --- PyQt4_gpl_mac-4.12.3.orig/qpy/QtCore/qpycore_types.cpp 2018-08-31 07:40:13.000000000 +0000 +++ PyQt4_gpl_mac-4.12.3/qpy/QtCore/qpycore_types.cpp 2026-03-10 10:01:48.209190009 +0000 @@ -146,6 +146,15 @@ #if PY_VERSION_HEX >= 0x03040000 0, /* tp_finalize */ #endif +#if PY_VERSION_HEX >= 0x03080000 + 0, /* tp_vectorcall */ +#endif +#if PY_VERSION_HEX >= 0x030d0000 + 0, /* tp_watched */ +#endif +#if PY_VERSION_HEX >= 0x030d0000 + 0, /* tp_versions_used */ +#endif };