Signals in Python without glib

Justin Mazzola Paluska jmp at MIT.EDU
Thu Jul 27 02:53:16 PDT 2006


On Wed, Jul 26, 2006 at 10:30:43AM -0400, John (J5) Palmieri wrote:
> What would be nice is to wrap this in the lower level bindings
> directly and then add a method on the Bus class in _dbus.py.
> Perhaps even have something like
> 
> connection = dbus.SessionBus() 
> connection.generic_main_loop()
> 
> Make sure the method is clear that a person shouldn't be using this
> if they can be using a real main loop.

I added a read_write_dispatch(timeout) method to the low-level
DBUSConnection bindings and then wrote the Bus.generic_main_loop()
function.  Please see the attached patch.  It seems to be working on
my machine with Python 2.4.4c0 and D-BUS 0.62.

The main complication in this basic version was remembering to release
the GIL when I call dbus_connection_read_write_dispatch.

There are improvements that can be made to it:

- make the read_write_dispatch method more "Pythonic", that is, take
  in a float as a number of seconds to block, like time.sleep.

- make the system work on non-threaded Python systems.  In the 'cdef
  extern from "Python.h"' block of code, I include API only defined on
  threaded Python systems.
  <http://article.gmane.org/gmane.comp.python.pyrex/967> has the no-op
  defs that should be used on non-threaded Pythons, but I don't know
  how to do ifdefs with Pyrex.

- write tests.  I see that the Python bindings include a tests
  directory with some unittests that use the glib mainloop.  Should I
  extend the test file to use the brain-dead mainloop?

Let me know if you are amenable to the patch and my proposed changes
and make a new patch with the changes.
	--Justin
-------------- next part --------------
diff --git a/dbus/_dbus.py b/dbus/_dbus.py
index 7e17d9f..8700963 100644
--- a/dbus/_dbus.py
+++ b/dbus/_dbus.py
@@ -240,6 +240,20 @@ class Bus(object):
     def start_service_by_name(self, named_service):
         return dbus_bindings.bus_start_service_by_name(self._connection, named_service)
 
+    def generic_main_loop(self):
+
+        """ Run a brain-dead simple mainloop.
+
+        If you can use a real mainloop, like the Glib one, you're best
+        off using that.
+
+        This function will return only after the bus has been
+        disconnected."""
+
+        while self._connection.read_write_dispatch():
+            pass
+        return
+
     def __repr__(self):
         if self._bus_type == self.TYPE_SESSION:
             name = 'SESSION'
diff --git a/dbus/dbus_bindings.pyx b/dbus/dbus_bindings.pyx
index c29dea2..4e9045c 100644
--- a/dbus/dbus_bindings.pyx
+++ b/dbus/dbus_bindings.pyx
@@ -25,6 +25,9 @@ cdef extern from "Python.h":
     void PyErr_Clear()
     PyGILState_STATE PyGILState_Ensure()
     void PyGILState_Release(PyGILState_STATE)
+    struct PyThreadState
+    PyThreadState* PyEval_SaveThread()
+    void PyEval_RestoreThread(PyThreadState *tstate)
 
 ctypedef struct DBusError:
     char *name
@@ -387,6 +390,27 @@ cdef class Connection:
     def dispatch(self):
         return dbus_connection_dispatch(self.conn)
 
+    def read_write_dispatch(self,
+                            timeout=None):
+
+        # `read_write_dispatch blocks`, so we need to make sure that
+        # we *don't* hold the GIL.  The cdefs force Pyrex to convert
+        # our Python objects before we release the GIL.
+
+        cdef int to, rval
+        cdef PyThreadState *_save
+
+        if timeout is None:
+            timeout = -1
+        to = timeout
+
+        _save = PyEval_SaveThread()
+        rval = dbus_connection_read_write_dispatch(self.conn,
+                                                   to)
+        PyEval_RestoreThread(_save)
+
+        return rval
+
     def send(self, Message message):
         #cdef dbus_uint32_t client_serial
         #if type(message) != Message:


More information about the dbus mailing list