Python: add weakref support to builtin types

These changes add a weakreflist member to SwigPyObject, and set
tp_weaklistoffset to its offset. This fixes #1792.

The Py_TPFLAGS_MANAGED_WEAKREF introduced in Python 3.12 requires
Py_TPFLAGS_HAVE_GC to be set as well, which it currently isn't for
SwigPyObjectType. (See https://github.com/python/cpython/issues/134786).
This commit is contained in:
Jim Easterbrook 2025-07-10 08:11:38 +01:00
parent 40378d0405
commit 976205ef0d
5 changed files with 25 additions and 1 deletions

View File

@ -111,6 +111,7 @@ C_TEST_CASES += \
li_cwstring \
python_nondynamic \
python_varargs_typemap \
python_weakref \
MULTI_CPP_TEST_CASES += \
import_callback \

View File

@ -0,0 +1,6 @@
import weakref
import python_weakref
ts = python_weakref.TestStruct()
ts_ref = weakref.ref(ts)

View File

@ -0,0 +1,9 @@
// Test if SwigPyObject derived class can be weakrefed
%module python_weakref
%inline %{
struct TestStruct {
int x;
};
%}

View File

@ -540,6 +540,8 @@ SWIGINTERN void
SwigPyBuiltin_destructor_closure(SwigPyWrapperFunction wrapper, const char *wrappername, PyObject *a) {
SwigPyObject *sobj;
sobj = (SwigPyObject *)a;
if (sobj->weakreflist != NULL)
PyObject_ClearWeakRefs(a);
SWIG_Py_XDECREF(sobj->swigdict);
if (sobj->own) {
PyObject *o;

View File

@ -627,6 +627,7 @@ typedef struct {
int own;
PyObject *next;
PyObject *swigdict;
PyObject *weakreflist;
} SwigPyObject;
@ -1019,7 +1020,7 @@ SwigPyObject_TypeOnce(void) {
0, /* tp_traverse */
0, /* tp_clear */
(richcmpfunc)SwigPyObject_richcompare,/* tp_richcompare */
0, /* tp_weaklistoffset */
offsetof(SwigPyObject, weakreflist), /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
SwigPyObject_methods, /* tp_methods */
@ -1108,6 +1109,8 @@ SwigPyObject_TypeOnce(void) {
((PyTypeObject *)pytype)->tp_dictoffset = offsetof(SwigPyObject, swigdict);
#endif
#endif
if (pytype)
((PyTypeObject *)pytype)->tp_weaklistoffset = offsetof(SwigPyObject, weakreflist);
if (pytype && PyModule_AddObject(runtime_data_module, "SwigPyObject", pytype) == 0)
SWIG_Py_INCREF(pytype);
return (PyTypeObject *)pytype;
@ -1124,6 +1127,7 @@ SwigPyObject_New(void *ptr, swig_type_info *ty, int own)
sobj->own = own;
sobj->next = 0;
sobj->swigdict = 0;
sobj->weakreflist = 0;
if (own == SWIG_POINTER_OWN) {
/* Obtain a reference to the Python capsule wrapping the module information, so that the
* module information is correctly destroyed after all SWIG python objects have been freed
@ -1789,11 +1793,13 @@ SWIG_Python_NewPointerObj(PyObject *self, void *ptr, swig_type_info *type, int f
newobj->next = next_self;
newobj = (SwigPyObject *)next_self;
newobj->swigdict = 0;
newobj->weakreflist = 0;
}
} else {
newobj = PyObject_New(SwigPyObject, clientdata->pytype);
if (newobj) {
newobj->swigdict = 0;
newobj->weakreflist = 0;
}
}
if (newobj) {