dbus-python: Unimportable module when building in Python 3.15

Maximiliano Curia <[email protected]>
Newsgroups gmane.linux.debian.devel.python
Message-ID <[email protected]>
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 /\/\ /\ >< `/
no-managed-weakref-without-gc.patch (text/x-diff, 5.3 KB)
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",
signature.asc (application/pgp-signature, 833 B)
-----BEGIN PGP SIGNATURE-----

iQIzBAABCgAdFiEE+JIdOnQEyG4RNSIVxxl2mbKbIyoFAmqBumUACgkQxxl2mbKb
Iyro3g/9EHffZENxLbCDoyX8YZvKJ+MA4Zyx/Td8w/+ztSVK78TnhUnsmU7694ba
B5qNYxWvMnV4j8lSPTvd1bbZNDnUtWsMSf8+M24cc9UXtMHxgQ1LCbdCta+dG2AT
oNt+o0wIIq1o+ODKvtWoFwuhip6m40YKiehK/816J/aTG2/n8C4aSpLCzV1RoGSo
3ujjaif2GfZaFLxNrjsScLp0hGA1h2Z4CabEXLJhumYn6Dv9GZ5w2Ge1TavnElMB
oy0xBz01RIK+XsMIXR/ACgjgITZNKMAD865ZdxS18AuBkPCqIrJhe0HX0HlnHXda
xjpvIrE3Pyw5cCdwbyWAvkCUy0u+lB7HQ7vvxZqVx1+0xft2aZqjTrrvyj2kIy2E
3TRVYYg5V/fAD1aNuZFNxuIVEkL7dZ7vh5GhAwOa6L9S0FH29/aIR5G3E9WinluK
fxcZAK2ahwJiTKCZB3W2crLu8Vgsx79abKI9oW+95ygZ53dDLC2bimjCEIJYIgSd
KMp+JMDEoHSZX/PMjKTYBNZGP02dy9ePANBvWHRSioEQX+DQU0VN0m6YqmukPrFr
AaOsIY+euPd1LxgWi9ZroCWtJUR8LulrjpzWVcpByzJl74WPoqvKwGh1eRFVx8+x
9g/tcVhdYsSi5tTST/3CQe/fteHvlJQ63lelhA/qVjA+VGBBY4M=
=2k7R
-----END PGP SIGNATURE-----
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.