Package: src:dbus-python
Version: 1.4.0-1
User: [email protected]
Usertags: python3.15

Hi!

While rebuilding packages against Python 3.15 we found that dbus-python
failed to build from source due to failing tests. All the tests were
saying something like:

test:         dbus-python:standalone
start time:   10:20:31
duration:     0.05s
result:       exit status 1
command:      
DBUS_TEST_TMPDIR=/build/reproducible-path/dbus-python-1.4.0/build-3.15/test 
DBUS_TOP_SRCDIR=/build/reproducible-path/dbus-python-1.4.0 
DBUS_PYTHON_VERSION=1.4.0 MALLOC_PERTURB_=171 
UBSAN_OPTIONS=halt_on_error=1:abort_on_error=1:print_summary=1:print_stacktrace=1
 
PYTHONPATH=/build/reproducible-path/dbus-python-1.4.0:/build/reproducible-path/dbus-python-1.4.0/test:/build/reproducible-path/dbus-python-1.4.0/build-3.15:/build/reproducible-path/dbus-python-1.4.0/build-3.15/test
 
MSAN_OPTIONS=halt_on_error=1:abort_on_error=1:print_summary=1:print_stacktrace=1
 ASAN_OPTIONS=halt_on_error=1:abort_on_error=1:print_summary=1 
DBUS_TEST_UNINSTALLED=1 DBUS_FATAL_WARNINGS=1 MESON_TEST_ITERATION=1 
DBUS_TOP_BUILDDIR=/build/reproducible-path/dbus-python-1.4.0/build-3.15 
/usr/bin/dbus-run-session --config-file 
/build/reproducible-path/dbus-python-1.4.0/build-3.15/test/tmp-session-bus.conf 
-- /usr/bin/python3.15 
/build/reproducible-path/dbus-python-1.4.0/build-3.15/../test/test-standalone.py
----------------------------------- stderr -----------------------------------
Traceback (most recent call last):
  File 
"/build/reproducible-path/dbus-python-1.4.0/build-3.15/../test/test-standalone.py", 
line 39, in <module>
    import _dbus_bindings
SystemError: type _dbus_bindings.Connection has the Py_TPFLAGS_MANAGED_WEAKREF 
flag but not Py_TPFLAGS_HAVE_GC flag

The problem seems to be caused by the fact that the Connection is a
GenericAlloc that's missing the PyGC_Head header header, this is now detected in
Python 3.15 (see: https://github.com/python/cpython/issues/134786).

The weakref was introduced by ebecd174 but the previous mechanism still
works, so I'm reverting ebecd174 and adding a regression test that
should fail in python 3.12 to 3.14.

Happy hacking,
--
"Backtracking algorithms are nondeterministic, not in the sense of being random, but in the sense of having free will."
-- Robert W. Floyd
Saludos /\/\ /\ >< `/
From: Maximiliano Curia <[email protected]>
Date: Sat, 16 Aug 2026 12:00:00 +0200
Subject: Don't use Py_TPFLAGS_MANAGED_WEAKREF on types without
 Py_TPFLAGS_HAVE_GC

Revert ebecd174 plus regression test.

Forwarded: no
Last-Update: 2026-08-16
---
Index: dbus-python/dbus_bindings/conn-internal.h
===================================================================
--- dbus-python.orig/dbus_bindings/conn-internal.h
+++ dbus-python/dbus_bindings/conn-internal.h
@@ -42,10 +42,8 @@ typedef struct {
      */
     PyObject *object_paths;
 
-#if !DBUSPY_PY_VERSION_AT_LEAST(3, 12, 0, 0)
     /* Weak-references list to make Connections weakly referenceable */
     PyObject *weaklist;
-#endif
 
     dbus_bool_t has_mainloop;
 } Connection;
Index: dbus-python/dbus_bindings/conn.c
===================================================================
--- dbus-python.orig/dbus_bindings/conn.c
+++ dbus-python/dbus_bindings/conn.c
@@ -238,9 +238,7 @@ DBusPyConnection_NewConsumingDBusConnect
     self->has_mainloop = (mainloop != Py_None);
     self->conn = NULL;
     self->filters = PyList_New(0);
-#if !DBUSPY_PY_VERSION_AT_LEAST(3, 12, 0, 0)
     self->weaklist = NULL;
-#endif
     if (!self->filters) goto err;
     self->object_paths = PyDict_New();
     if (!self->object_paths) goto err;
@@ -394,10 +392,7 @@ static void Connection_tp_dealloc(Connec
     /* avoid clobbering any pending exception */
     PyErr_Fetch(&et, &ev, &etb);
 
-#if !DBUSPY_PY_VERSION_AT_LEAST(3, 12, 0, 0)
-    if (self->weaklist)
-#endif
-    {
+    if (self->weaklist) {
         PyObject_ClearWeakRefs((PyObject *)self);
     }
 
@@ -460,19 +455,12 @@ PyTypeObject DBusPyConnection_Type = {
     0,                      /*tp_getattro*/
     0,                      /*tp_setattro*/
     0,                      /*tp_as_buffer*/
-#if DBUSPY_PY_VERSION_AT_LEAST(3, 12, 0, 0)
-    Py_TPFLAGS_MANAGED_WEAKREF |
-#endif
     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
     Connection_tp_doc,      /*tp_doc*/
     0,                      /*tp_traverse*/
     0,                      /*tp_clear*/
     0,                      /*tp_richcompare*/
-#if DBUSPY_PY_VERSION_AT_LEAST(3, 12, 0, 0)
-    0,                      /*tp_weaklistoffset*/
-#else
     offsetof(Connection, weaklist),   /*tp_weaklistoffset*/
-#endif
     0,                      /*tp_iter*/
     0,                      /*tp_iternext*/
     DBusPyConnection_tp_methods,  /*tp_methods*/
Index: dbus-python/dbus_bindings/server.c
===================================================================
--- dbus-python.orig/dbus_bindings/server.c
+++ dbus-python/dbus_bindings/server.c
@@ -38,10 +38,8 @@ typedef struct {
     /* The Connection subtype for which this Server is a factory */
     PyObject *conn_class;
 
-#if !DBUSPY_PY_VERSION_AT_LEAST(3, 12, 0, 0)
     /* Weak-references list to make server weakly referenceable */
     PyObject *weaklist;
-#endif
 
     PyObject *mainloop;
 } Server;
@@ -425,9 +423,7 @@ Server_tp_new(PyTypeObject *cls, PyObjec
         return NULL;
     }
 
-#if !DBUSPY_PY_VERSION_AT_LEAST(3, 12, 0, 0)
     ((Server *)self)->weaklist = NULL;
-#endif
     TRACE(self);
 
     return self;
@@ -442,10 +438,7 @@ static void Server_tp_dealloc(Server *se
     /* avoid clobbering any pending exception */
     PyErr_Fetch(&et, &ev, &etb);
 
-#if !DBUSPY_PY_VERSION_AT_LEAST(3, 12, 0, 0)
-    if (self->weaklist)
-#endif
-    {
+    if (self->weaklist) {
         PyObject_ClearWeakRefs((PyObject *)self);
     }
 
@@ -577,19 +570,12 @@ PyTypeObject DBusPyServer_Type = {
     0,                      /*tp_getattro*/
     0,                      /*tp_setattro*/
     0,                      /*tp_as_buffer*/
-#if DBUSPY_PY_VERSION_AT_LEAST(3, 12, 0, 0)
-    Py_TPFLAGS_MANAGED_WEAKREF |
-#endif
     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
     Server_tp_doc,          /*tp_doc*/
     0,                      /*tp_traverse*/
     0,                      /*tp_clear*/
     0,                      /*tp_richcompare*/
-#if DBUSPY_PY_VERSION_AT_LEAST(3, 12, 0, 0)
-    0,                      /*tp_weaklistoffset*/
-#else
     offsetof(Server, weaklist),   /*tp_weaklistoffset*/
-#endif
     0,                      /*tp_iter*/
     0,                      /*tp_iternext*/
     DBusPyServer_tp_methods,/*tp_methods*/
Index: dbus-python/test/test-client.py
===================================================================
--- dbus-python.orig/test/test-client.py
+++ dbus-python/test/test-client.py
@@ -110,6 +110,22 @@ class TestDBusBindings(unittest.TestCase
         del bus
         self.assertTrue(ref() is None)
 
+    def testWeakRefsToBaseConnection(self):
+        # dbus.SessionBus() is a Python subclass, which always has
+        # Py_TPFLAGS_HAVE_GC; the C types do not, so they need their own
+        # coverage. Marking a non-GC type Py_TPFLAGS_MANAGED_WEAKREF makes
+        # CPython look for the weakref slot in the PyGC_Head that was never
+        # allocated, which reads and writes off the front of the object.
+        address = os.environ['DBUS_SESSION_BUS_ADDRESS']
+        conn = _dbus_bindings.Connection(address)
+        try:
+            ref = weakref.ref(conn)
+            self.assertTrue(ref() is conn)
+        finally:
+            conn.close()
+        del conn
+        self.assertTrue(ref() is None)
+
     def testInterfaceKeyword(self):
         #test dbus_interface parameter
         print(self.remote_object.Echo("dbus_interface on Proxy test Passed", 

Attachment: signature.asc
Description: PGP signature

Reply via email to