[PATCH 01/17] Implement o.fd.DBus method wrappers in Python instead of C.

Simon McVittie simon.mcvittie at collabora.co.uk
Mon Apr 30 03:20:53 PDT 2007


This reduces the need to have _dbus_bindings.BusImplementation and makes
peer-to-peer connections easier to implement.

diff --git a/_dbus_bindings/bus.c b/_dbus_bindings/bus.c
index 2ae0383..761b67e 100644
--- a/_dbus_bindings/bus.c
+++ b/_dbus_bindings/bus.c
@@ -131,246 +131,11 @@ Bus_get_unique_name(Connection *self, PyObject *args UNUSED)
     return PyString_FromString(name);
 }
 
-PyDoc_STRVAR(Bus_get_unix_user__doc__,
-"get_unix_user(bus_name) -> int\n"
-"\n"
-"Get the numeric uid of the process which owns the given bus name\n"
-"on the connected bus daemon.\n"
-"\n"
-":Parameters:\n"
-"    `bus_name` : str\n"
-"        A bus name (may be either a unique name or a well-known name)\n"
-);
-static PyObject *
-Bus_get_unix_user(Connection *self, PyObject *args)
-{
-    DBusError error;
-    unsigned long uid;
-    const char *bus_name;
-
-    TRACE(self);
-    DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
-    if (!PyArg_ParseTuple(args, "s:get_unix_user", &bus_name)) {
-        return NULL;
-    }
-
-    dbus_error_init(&error);
-    Py_BEGIN_ALLOW_THREADS
-    uid = dbus_bus_get_unix_user(self->conn, bus_name, &error);
-    Py_END_ALLOW_THREADS
-    if (uid == (unsigned long)(-1)) return DBusPyException_ConsumeError(&error);
-    return PyLong_FromUnsignedLong(uid);
-}
-
-PyDoc_STRVAR(Bus_start_service_by_name__doc__,
-"start_service_by_name(bus_name)\n\
-\n\
-Start a service which will implement the given bus name on this\n\
-Bus.\n\
-\n\
-:Parameters:\n\
-    `bus_name` : str\n\
-        The well-known bus name for which an implementation is required\n\
-\n\
-:Returns: A tuple of 2 elements. The first is always True, the second is\n\
-          either START_REPLY_SUCCESS or START_REPLY_ALREADY_RUNNING.\n\
-\n\
-:Raises DBusException: if the service could not be started.\n\
-\n\
-FIXME: Fix return signature?\n\
-");
-static PyObject *
-Bus_start_service_by_name(Connection *self, PyObject *args)
-{
-    DBusError error;
-    const char *bus_name;
-    dbus_uint32_t ret;
-    dbus_bool_t success;
-
-    TRACE(self);
-    DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
-    if (!PyArg_ParseTuple(args, "s:start_service_by_name", &bus_name)) {
-        return NULL;
-    }
-    dbus_error_init(&error);
-    Py_BEGIN_ALLOW_THREADS
-    success = dbus_bus_start_service_by_name(self->conn, bus_name,
-                                             0 /* flags */, &ret, &error);
-    Py_END_ALLOW_THREADS
-    if (!success) {
-        return DBusPyException_ConsumeError(&error);
-    }
-    return Py_BuildValue("(Ol)", Py_True, (long)ret);
-}
-
-/* FIXME: signal IN_QUEUE, EXISTS by exception? */
-PyDoc_STRVAR(Bus_request_name__doc__, "");
-static PyObject *
-Bus_request_name(Connection *self, PyObject *args)
-{
-    unsigned int flags = 0;
-    const char *bus_name;
-    int ret;
-    DBusError error;
-
-    TRACE(self);
-    DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
-    if (!PyArg_ParseTuple(args, "s|I:request_name", &bus_name, &flags)) {
-        return NULL;
-    }
-    if (!dbus_py_validate_bus_name(bus_name, 0, 1)) return NULL;
-
-    dbus_error_init(&error);
-    Py_BEGIN_ALLOW_THREADS
-    ret = dbus_bus_request_name(self->conn, bus_name, flags, &error);
-    Py_END_ALLOW_THREADS
-    if (ret == -1) return DBusPyException_ConsumeError(&error);
-
-    return PyInt_FromLong(ret);
-}
-
-PyDoc_STRVAR(Bus_release_name__doc__, "");
-static PyObject *
-Bus_release_name(Connection *self, PyObject *args)
-{
-    const char *bus_name;
-    int ret;
-    DBusError error;
-
-    TRACE(self);
-    DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
-    if (!PyArg_ParseTuple(args, "s:release_name", &bus_name)) return NULL;
-
-    dbus_error_init(&error);
-    Py_BEGIN_ALLOW_THREADS
-    ret = dbus_bus_release_name(self->conn, bus_name, &error);
-    Py_END_ALLOW_THREADS
-    if (ret == -1) return DBusPyException_ConsumeError(&error);
-
-    return PyInt_FromLong(ret);
-}
-
-PyDoc_STRVAR(Bus_name_has_owner__doc__,
-"name_has_owner(bus_name) -> bool\n\n"
-"Return True if and only if the given bus name has an owner on this bus.\n");
-static PyObject *
-Bus_name_has_owner(Connection *self, PyObject *args)
-{
-    const char *bus_name;
-    int ret;
-    DBusError error;
-
-    TRACE(self);
-    DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
-    if (!PyArg_ParseTuple(args, "s:name_has_owner", &bus_name)) return NULL;
-    dbus_error_init(&error);
-    Py_BEGIN_ALLOW_THREADS
-    ret = dbus_bus_name_has_owner(self->conn, bus_name, &error);
-    Py_END_ALLOW_THREADS
-    if (dbus_error_is_set(&error)) {
-        return DBusPyException_ConsumeError(&error);
-    }
-    return PyBool_FromLong(ret);
-}
-
-PyDoc_STRVAR(Bus_add_match_string__doc__,
-"add_match_string(rule)\n\n"
-"Arrange for this application to receive messages on the bus that match\n"
-"the given rule. This version will block and raises DBusException on error.\n");
-static PyObject *
-Bus_add_match_string(Connection *self, PyObject *args)
-{
-    const char *rule;
-    DBusError error;
-
-    TRACE(self);
-    DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
-    if (!PyArg_ParseTuple(args, "s:add_match", &rule)) return NULL;
-    dbus_error_init(&error);
-    Py_BEGIN_ALLOW_THREADS
-    dbus_bus_add_match(self->conn, rule, &error);
-    Py_END_ALLOW_THREADS
-    if (dbus_error_is_set(&error)) {
-        return DBusPyException_ConsumeError(&error);
-    }
-    Py_RETURN_NONE;
-}
-
-PyDoc_STRVAR(Bus_add_match_string_non_blocking__doc__,
-"add_match_string_non_blocking(rule)\n\n"
-"Arrange for this application to receive messages on the bus that match\n"
-"the given rule. This version does not block, but any errors will be\n"
-"ignored.\n");
-static PyObject *
-Bus_add_match_string_non_blocking(Connection *self, PyObject *args)
-{
-    const char *rule;
-
-    DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
-    if (!PyArg_ParseTuple(args, "s:add_match", &rule)) return NULL;
-    Py_BEGIN_ALLOW_THREADS
-    dbus_bus_add_match(self->conn, rule, NULL);
-    Py_END_ALLOW_THREADS
-    Py_RETURN_NONE;
-}
-
-PyDoc_STRVAR(Bus_remove_match_string__doc__,
-"remove_match_string(rule)\n\n"
-"Remove the given match rule; if it has been added more than once,\n"
-"remove one of the identical copies, leaving the others active.\n"
-"This version blocks, and raises DBusException on error.\n");
-static PyObject *
-Bus_remove_match_string(Connection *self, PyObject *args)
-{
-    const char *rule;
-    DBusError error;
-
-    TRACE(self);
-    DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
-    if (!PyArg_ParseTuple(args, "s:remove_match", &rule)) return NULL;
-    dbus_error_init(&error);
-    Py_BEGIN_ALLOW_THREADS
-    dbus_bus_remove_match(self->conn, rule, &error);
-    Py_END_ALLOW_THREADS
-    if (dbus_error_is_set(&error)) {
-        return DBusPyException_ConsumeError(&error);
-    }
-    Py_RETURN_NONE;
-}
-
-PyDoc_STRVAR(Bus_remove_match_string_non_blocking__doc__,
-"remove_match_string_non_blocking(rule)\n\n"
-"Remove the given match rule; if it has been added more than once,\n"
-"remove one of the identical copies, leaving the others active.\n"
-"This version does not block, but causes any errors to be ignored.\n");
-static PyObject *
-Bus_remove_match_string_non_blocking(Connection *self, PyObject *args)
-{
-    const char *rule;
-
-    TRACE(self);
-    DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
-    if (!PyArg_ParseTuple(args, "s:remove_match", &rule)) return NULL;
-    Py_BEGIN_ALLOW_THREADS
-    dbus_bus_remove_match(self->conn, rule, NULL);
-    Py_END_ALLOW_THREADS
-    Py_RETURN_NONE;
-}
-
 /* Bus type object ================================================== */
 
 static struct PyMethodDef Bus_tp_methods[] = {
 #define ENTRY(name, flags) {#name, (PyCFunction)Bus_##name, flags, Bus_##name##__doc__},
     ENTRY(get_unique_name, METH_NOARGS)
-    ENTRY(get_unix_user, METH_VARARGS)
-    ENTRY(start_service_by_name, METH_VARARGS)
-    ENTRY(request_name, METH_VARARGS)
-    ENTRY(release_name, METH_VARARGS)
-    ENTRY(name_has_owner, METH_VARARGS)
-    ENTRY(add_match_string, METH_VARARGS)
-    ENTRY(add_match_string_non_blocking, METH_VARARGS)
-    ENTRY(remove_match_string, METH_VARARGS)
-    ENTRY(remove_match_string_non_blocking, METH_VARARGS)
 #undef ENTRY
     {NULL},
 };
diff --git a/dbus/Makefile.am b/dbus/Makefile.am
index 9059659..53efa89 100644
--- a/dbus/Makefile.am
+++ b/dbus/Makefile.am
@@ -1,6 +1,7 @@
 pythondbusdir = $(pythondir)/dbus
 
-nobase_pythondbus_PYTHON = dbus_bindings.py \
+nobase_pythondbus_PYTHON = bus.py \
+			   dbus_bindings.py \
 			   _dbus.py \
 			   _version.py \
 			   decorators.py \
diff --git a/dbus/_dbus.py b/dbus/_dbus.py
index a54338d..838b223 100644
--- a/dbus/_dbus.py
+++ b/dbus/_dbus.py
@@ -37,6 +37,7 @@ import sys
 import weakref
 from traceback import print_exc
 
+from dbus.bus import _BusDaemonMixin
 from dbus.proxies import ProxyObject, BUS_DAEMON_NAME, BUS_DAEMON_PATH, \
         BUS_DAEMON_IFACE
 
@@ -221,7 +222,7 @@ class SignalMatch(object):
                                         **self._args_match)
 
 
-class Bus(BusImplementation):
+class Bus(BusImplementation, _BusDaemonMixin):
     """A connection to a DBus daemon.
 
     One of three possible standard buses, the SESSION, SYSTEM,
@@ -533,6 +534,7 @@ class Bus(BusImplementation):
                         yield m
 
     def _remove_name_owner_changed_for_match(self, named_service, match):
+        # The signals lock must be held.
         notification = self._signal_sender_matches.get(named_service, False)
         if notification:
             try:
diff --git a/dbus/bus.py b/dbus/bus.py
new file mode 100644
index 0000000..20bc97f
--- /dev/null
+++ b/dbus/bus.py
@@ -0,0 +1,191 @@
+"""Bus mixin, for use within dbus-python only. See `_BusMixin`."""
+
+# Copyright (C) 2007 Collabora Ltd. <http://www.collabora.co.uk/>
+#
+# Licensed under the Academic Free License version 2.1
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+from dbus import UInt32, UTF8String
+from dbus.proxies import BUS_DAEMON_NAME, BUS_DAEMON_PATH, BUS_DAEMON_IFACE
+
+from _dbus_bindings import validate_interface_name, validate_member_name,\
+                           validate_bus_name, validate_object_path,\
+                           validate_error_name
+
+def _noop(*args, **kwargs):
+    """A universal no-op function"""
+
+class _BusDaemonMixin(object):
+    """This mixin must be mixed-in with something with a get_object method
+    (obviously, it's meant to be the dbus.Bus). It provides simple blocking
+    wrappers for various methods on the org.freedesktop.DBus bus-daemon
+    object, to reduce the amount of C code we need.
+    """
+
+    def get_unix_user(self, bus_name):
+        """Get the numeric uid of the process owning the given bus name.
+
+        :Parameters:
+            `bus_name` : str
+                A bus name, either unique or well-known
+        :Returns: a `dbus.UInt32`
+        """
+        validate_bus_name(bus_name)
+        return self.get_object(BUS_DAEMON_NAME,
+                BUS_DAEMON_PATH).GetConnectionUnixUser(bus_name,
+                        dbus_interface=BUS_DAEMON_IFACE)
+
+    def start_service_by_name(self, bus_name, flags=0):
+        """Start a service which will implement the given bus name on this Bus.
+
+        :Parameters:
+            `bus_name` : str
+                The well-known bus name to be activated.
+            `flags` : dbus.UInt32
+                Flags to pass to StartServiceByName (currently none are
+                defined)
+
+        :Returns: A tuple of 2 elements. The first is always True, the
+            second is either START_REPLY_SUCCESS or
+            START_REPLY_ALREADY_RUNNING.
+
+        :Raises DBusException: if the service could not be started.
+        """
+        validate_bus_name(bus_name)
+        ret = self.get_object(BUS_DAEMON_NAME,
+                BUS_DAEMON_PATH).StartServiceByName(bus_name, UInt32(flags),
+                        dbus_interface=BUS_DAEMON_IFACE)
+        return (True, ret)
+
+    # XXX: it might be nice to signal IN_QUEUE, EXISTS by exception,
+    # but this would not be backwards-compatible
+    def request_name(self, name, flags=0):
+        """Request a bus name.
+
+        :Parameters:
+            `name` : str
+                The well-known name to be requested
+            `flags` : dbus.UInt32
+                A bitwise-OR of 0 or more of the flags
+                `DBUS_NAME_FLAG_ALLOW_REPLACEMENT`,
+                `DBUS_NAME_FLAG_REPLACE_EXISTING`
+                and `DBUS_NAME_FLAG_DO_NOT_QUEUE`
+        :Returns: `DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER`,
+            `DBUS_REQUEST_NAME_REPLY_IN_QUEUE`,
+            `DBUS_REQUEST_NAME_REPLY_EXISTS` or
+            `DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER`
+        :Raises DBusException: if the bus daemon cannot be contacted or
+            returns an error.
+        """
+        validate_bus_name(name, allow_unique=False)
+        return self.get_object(BUS_DAEMON_NAME,
+                BUS_DAEMON_PATH).RequestName(name, UInt32(flags),
+                        dbus_interface=BUS_DAEMON_IFACE)
+
+    def release_name(self, name):
+        """Release a bus name.
+
+        :Parameters:
+            `name` : str
+                The well-known name to be released
+        :Returns: `DBUS_RELEASE_NAME_REPLY_RELEASED`,
+            `DBUS_RELEASE_NAME_REPLY_NON_EXISTENT`
+            or `DBUS_RELEASE_NAME_REPLY_NOT_OWNER`
+        :Raises DBusException: if the bus daemon cannot be contacted or
+            returns an error.
+        """
+        validate_bus_name(name, allow_unique=False)
+        return self.get_object(BUS_DAEMON_NAME,
+                BUS_DAEMON_PATH).ReleaseName(name,
+                        dbus_interface=BUS_DAEMON_IFACE)
+
+    def name_has_owner(self, bus_name):
+        """Return True iff the given bus name has an owner on this bus.
+
+        :Parameters:
+            `name` : str
+                The bus name to look up
+        :Returns: a `bool`
+        """
+        return bool(self.get_object(BUS_DAEMON_NAME,
+                BUS_DAEMON_PATH).NameHasOwner(bus_name,
+                        dbus_interface=BUS_DAEMON_IFACE))
+
+    # AddMatchString is not bound here
+    # RemoveMatchString either
+
+    def add_match_string(self, rule):
+        """Arrange for this application to receive messages on the bus that
+        match the given rule. This version will block.
+
+        :Parameters:
+            `rule` : str
+                The match rule
+        :Raises: `DBusException` on error.
+        """
+        self.get_object(BUS_DAEMON_NAME,
+                BUS_DAEMON_PATH).AddMatch(rule,
+                        dbus_interface=BUS_DAEMON_IFACE)
+
+    # FIXME: add an async success/error handler capability?
+    # FIXME: tell the bus daemon not to bother sending us a reply
+    # (and the same for remove_...)
+    def add_match_string_non_blocking(self, rule):
+        """Arrange for this application to receive messages on the bus that
+        match the given rule. This version will not block, but any errors
+        will be ignored.
+
+
+        :Parameters:
+            `rule` : str
+                The match rule
+        :Raises: `DBusException` on error.
+        """
+        self.get_object(BUS_DAEMON_NAME,
+                BUS_DAEMON_PATH).AddMatch(rule,
+                        dbus_interface=BUS_DAEMON_IFACE,
+                        reply_handler=_noop,
+                        error_handler=_noop)
+
+    def remove_match_string(self, rule):
+        """Arrange for this application to receive messages on the bus that
+        match the given rule. This version will block.
+
+        :Parameters:
+            `rule` : str
+                The match rule
+        :Raises: `DBusException` on error.
+        """
+        self.get_object(BUS_DAEMON_NAME,
+                BUS_DAEMON_PATH).RemoveMatch(rule,
+                        dbus_interface=BUS_DAEMON_IFACE)
+
+    def remove_match_string_non_blocking(self, rule):
+        """Arrange for this application to receive messages on the bus that
+        match the given rule. This version will not block, but any errors
+        will be ignored.
+
+
+        :Parameters:
+            `rule` : str
+                The match rule
+        :Raises: `DBusException` on error.
+        """
+        self.get_object(BUS_DAEMON_NAME,
+                BUS_DAEMON_PATH).RemoveMatch(rule,
+                        dbus_interface=BUS_DAEMON_IFACE,
+                        reply_handler=_noop,
+                        error_handler=_noop)


More information about the dbus mailing list