From 6963fa9cfa5be7af19b93c71d8a26c149cf2c88c Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 30 Apr 2007 11:20:53 +0100 Subject: [PATCH] Implement o.fd.DBus method wrappers in Python instead of C. This reduces the need to have _dbus_bindings.BusImplementation and makes peer-to-peer connections easier to implement. --- _dbus_bindings/bus.c | 235 --------------------------------------------------- dbus/Makefile.am | 3 +- dbus/_dbus.py | 4 +- dbus/bus.py | 191 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 196 insertions(+), 237 deletions(-) create mode 100644 dbus/bus.py 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. +# +# 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) -- 2.11.4.GIT