From 9774cdade2306b9bb641162a14645510fc822c86 Mon Sep 17 00:00:00 2001 From: Mathias Hasselmann Date: Thu, 31 Jan 2008 23:26:30 +0100 Subject: Initial support for DBusServer class (#14322). --- _dbus_bindings/server.c | 535 ++++++++++++++++++++++++++++++++++++++++++++++++ dbus/server.py | 38 ++++ test/test-server.py | 74 +++++++ 3 files changed, 647 insertions(+) create mode 100644 _dbus_bindings/server.c create mode 100644 dbus/server.py create mode 100755 test/test-server.py diff --git a/_dbus_bindings/server.c b/_dbus_bindings/server.c new file mode 100644 index 0000000..ec92b3b --- /dev/null +++ b/_dbus_bindings/server.c @@ -0,0 +1,535 @@ +/* Implementation of the _dbus_bindings Server type, a Python wrapper + * for DBusServer. + * + * Copyright (C) 2008 Openismus GmbH + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "dbus_bindings-internal.h" + +/* Server definition ================================================ */ + +typedef struct { + PyObject_HEAD + DBusServer *server; + + /* Weak-references list to make server weakly referenceable */ + PyObject *weaklist; + + PyObject *mainloop; +} Server; + +PyDoc_STRVAR(Server_tp_doc, +"A D-Bus server.\n" +"\n" +"::\n" +"\n" +" Server(address, mainloop=None, auth_mechanisms=None) -> Server\n" +); + +/* D-Bus Server user data slot, containing an owned reference to either + * the Server, or a weakref to the Server. + */ +static dbus_int32_t _server_python_slot; + +/* C API for main-loop hooks ======================================== */ + +/* Return a borrowed reference to the DBusServer which underlies this + * Server. */ +DBusServer * +DBusPyServer_BorrowDBusServer(PyObject *self) +{ + DBusServer *dbs; + + TRACE(self); + if (!DBusPyServer_Check(self)) { + PyErr_SetString(PyExc_TypeError, "A dbus.Server is required"); + return NULL; + } + dbs = ((Server *)self)->server; + if (!dbs) { + PyErr_SetString(PyExc_RuntimeError, "Server is in an invalid " + "state: no DBusServer"); + return NULL; + } + return dbs; +} + +/* Internal C API =================================================== */ + +static void +DBusPyServer_set_auth_mechanisms(Server *self, + PyObject *auth_mechanisms) +{ + const char **list = NULL; + + if (auth_mechanisms) { + Py_ssize_t length; + Py_ssize_t i; + + if (!PySequence_Check(auth_mechanisms)) { + PyErr_SetString(PyExc_TypeError, "Expecting sequence for auth_mechanisms parameter"); + return; + } + + length = PySequence_Fast_GET_SIZE(auth_mechanisms); + + Py_BEGIN_ALLOW_THREADS + list = PyMem_New(const char*, length + 1); + Py_END_ALLOW_THREADS + + for (i = 0; i < length; ++i) { + PyObject* am; + + am = PySequence_Fast_GET_ITEM(auth_mechanisms, i); + am = PyObject_Str(am); + + list[i] = PyString_AS_STRING(am); + } + } + + Py_BEGIN_ALLOW_THREADS + dbus_server_set_auth_mechanisms(self->server, list); + Py_END_ALLOW_THREADS + + PyMem_Free(list); +} + +/* Return a new reference to a Python Server or subclass corresponding + * to the DBusServer server. For use in callbacks. + * + * Raises AssertionError if the DBusServer does not have a Server. + */ +static PyObject * +DBusPyServer_ExistingFromDBusServer(DBusServer *server) +{ + PyObject *self, *ref; + + Py_BEGIN_ALLOW_THREADS + ref = (PyObject *)dbus_server_get_data(server, + _server_python_slot); + Py_END_ALLOW_THREADS + if (ref) { + DBG("(DBusServer *)%p has weak reference at %p", server, ref); + self = PyWeakref_GetObject(ref); /* still a borrowed ref */ + if (self && self != Py_None && DBusPyServer_Check(self)) { + DBG("(DBusServer *)%p has weak reference at %p pointing to %p", + server, ref, self); + TRACE(self); + Py_INCREF(self); + TRACE(self); + return self; + } + } + + PyErr_SetString(PyExc_AssertionError, + "D-Bus server does not have a Server " + "instance associated with it"); + return NULL; +} + +static void +DBusPyServer_new_connection_cb(DBusServer *server, + DBusConnection *conn, + void *data UNUSED) +{ + PyGILState_STATE gil = PyGILState_Ensure(); + PyObject *self = NULL, *conn_obj = NULL; + PyObject *method = NULL, *result = NULL; + +printf("%s:%d\n", __func__, __LINE__); + self = DBusPyServer_ExistingFromDBusServer(server); + if (!self) goto out; + TRACE(self); + + method = PyObject_GetAttrString(self, "_on_new_connection"); + TRACE(method); + +printf("%s:%d\n", __func__, __LINE__); + if (method) { + conn_obj = DBusPyConnection_NewConsumingDBusConnection(&DBusPyConnection_Type, + dbus_connection_ref(conn), + ((Server*) self)->mainloop); + + result = PyObject_CallFunctionObjArgs(method, conn_obj, NULL); + } + +out: +printf("%s:%d\n", __func__, __LINE__); + Py_XDECREF(result); + Py_XDECREF(method); + Py_XDECREF(conn_obj); + Py_XDECREF(self); + + if (PyErr_Occurred()) + PyErr_Print(); + + PyGILState_Release(gil); +} + +/* Return a new reference to a Python Server or subclass (given by cls) + * corresponding to the DBusServer server, which must have been newly + * created. For use by the Server constructor. + * + * Raises AssertionError if the DBusServer already has a Server. + */ +static PyObject * +DBusPyServer_NewDBusServer(PyTypeObject *cls, + DBusServer *server, + PyObject *mainloop, + PyObject *auth_mechanisms) +{ + Server *self = NULL; + PyObject *ref; + dbus_bool_t ok; + + DBG("%s(cls=%p, server=%p, mainloop=%p, auth_mechanisms=%p)", + __func__, cls, server, mainloop, auth_mechanisms); + DBUS_PY_RAISE_VIA_NULL_IF_FAIL(server); + + Py_BEGIN_ALLOW_THREADS + ref = (PyObject *)dbus_server_get_data(server, + _server_python_slot); + Py_END_ALLOW_THREADS + if (ref) { + self = (Server *)PyWeakref_GetObject(ref); + ref = NULL; + if (self && (PyObject *)self != Py_None) { + self = NULL; + PyErr_SetString(PyExc_AssertionError, + "Newly created D-Bus server already has a " + "Server instance associated with it"); + DBG("%s() fail - assertion failed, DBusPyServer has a DBusServer already", __func__); + DBG_WHEREAMI; + return NULL; + } + } + ref = NULL; + + /* Change mainloop from a borrowed reference to an owned reference */ + if (!mainloop || mainloop == Py_None) { + mainloop = dbus_py_get_default_main_loop(); + if (!mainloop) + goto err; + } + else { + Py_INCREF(mainloop); + } + + DBG("Constructing Server from DBusServer at %p", server); + + self = (Server *)(cls->tp_alloc(cls, 0)); + if (!self) goto err; + TRACE(self); + + DBG_WHEREAMI; + + self->mainloop = mainloop; + self->server = NULL; + + ref = PyWeakref_NewRef((PyObject *)self, NULL); + if (!ref) goto err; + DBG("Created weak ref %p to (Server *)%p for (DBusServer *)%p", + ref, self, server); + + Py_BEGIN_ALLOW_THREADS + ok = dbus_server_set_data(server, _server_python_slot, + (void *)ref, + (DBusFreeFunction)dbus_py_take_gil_and_xdecref); + Py_END_ALLOW_THREADS + + if (ok) { + DBG("Attached weak ref %p ((Server *)%p) to (DBusServer *)%p", + ref, self, server); + + ref = NULL; /* don't DECREF it - the DBusServer owns it now */ + } + else { + DBG("Failed to attached weak ref %p ((Server *)%p) to " + "(DBusServer *)%p - will dispose of it", ref, self, server); + PyErr_NoMemory(); + goto err; + } + + DBUS_PY_RAISE_VIA_GOTO_IF_FAIL(server, err); + self->server = server; + /* the DBusPyServer will close it now */ + server = NULL; + + if (self->mainloop != Py_None && + !dbus_py_set_up_server((PyObject *)self, self->mainloop)) + goto err; + + DBusPyServer_set_auth_mechanisms(self, auth_mechanisms); + + Py_BEGIN_ALLOW_THREADS + dbus_server_set_new_connection_function(self->server, + DBusPyServer_new_connection_cb, + NULL, NULL); + Py_END_ALLOW_THREADS + + DBG("%s() -> %p", __func__, self); + TRACE(self); + return (PyObject *)self; + +err: + DBG("Failed to construct Server from DBusServer at %p", server); +// Py_XDECREF(mainloop); +// Py_XDECREF(self); +// Py_XDECREF(ref); + if (server) { + Py_BEGIN_ALLOW_THREADS + dbus_server_disconnect(server); + dbus_server_unref(server); + Py_END_ALLOW_THREADS + } + DBG("%s() fail", __func__); + DBG_WHEREAMI; + return NULL; +} + +/* Server type-methods ============================================== */ + +static PyObject * +Server_tp_new(PyTypeObject *cls, PyObject *args, PyObject *kwargs) +{ + DBusServer *server; + const char *address; + DBusError error; + PyObject *self, *mainloop = NULL, *auth_mechanisms = NULL; + static char *argnames[] = {"address", "mainloop", "auth_mechanisms", NULL}; + +printf("%s:%d\n", __func__, __LINE__); + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|OO", argnames, + &address, &mainloop, &auth_mechanisms)) { + return NULL; + } + + dbus_error_init(&error); + + Py_BEGIN_ALLOW_THREADS + server = dbus_server_listen(address, &error); + Py_END_ALLOW_THREADS + + if (!server) { + DBusPyException_ConsumeError(&error); + return NULL; + } + + self = DBusPyServer_NewDBusServer(cls, server, mainloop, auth_mechanisms); + TRACE(self); + + return self; +} + +/* Destructor */ +static void Server_tp_dealloc(Server *self) +{ + DBusServer *server = self->server; + PyObject *et, *ev, *etb; + +printf("%s:%d\n", __func__, __LINE__); + /* avoid clobbering any pending exception */ + PyErr_Fetch(&et, &ev, &etb); + + if (self->weaklist) { + PyObject_ClearWeakRefs((PyObject *)self); + } + + TRACE(self); + DBG("Deallocating Server at %p (DBusServer at %p)", self, server); + DBG_WHEREAMI; + + if (server) { + DBG("Server at %p has a server, disconnecting it...", self); + Py_BEGIN_ALLOW_THREADS + dbus_server_disconnect(server); + Py_END_ALLOW_THREADS + } + + Py_DECREF(self->mainloop); + + /* make sure to do this last to preserve the invariant that + * self->server is always non-NULL for any referenced Server. + */ + DBG("Server at %p: nulling self->server", self); + self->server = NULL; + + if (server) { + DBG("Server at %p: unreffing server", self); + dbus_server_unref(server); + } + + DBG("Server at %p: freeing self", self); + PyErr_Restore(et, ev, etb); + (self->ob_type->tp_free)((PyObject *)self); +} + +PyDoc_STRVAR(Server_disconnect__doc__, +"disconnect()\n\n" +"Releases the server's address and stops listening for new clients.\n\n" +"If called more than once, only the first call has an effect."); +static PyObject * +Server_disconnect (Server *self, PyObject *args UNUSED) +{ + TRACE(self); + if (self->server) { + Py_BEGIN_ALLOW_THREADS + dbus_server_disconnect(self->server); + Py_END_ALLOW_THREADS + } + Py_RETURN_NONE; +} + +PyDoc_STRVAR(Server_get_address__doc__, +"get_address() -> String\n\n" +"Returns the address of the server."); +static PyObject * +Server_get_address(Server *self, PyObject *args UNUSED) +{ + const char *address; + + TRACE(self); + DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->server); + Py_BEGIN_ALLOW_THREADS + address = dbus_server_get_address(self->server); + Py_END_ALLOW_THREADS + + return PyString_FromString(address); +} + +PyDoc_STRVAR(Server_get_id__doc__, +"get_id() -> String\n\n" +"Returns the unique ID of the server."); +static PyObject * +Server_get_id(Server *self, PyObject *args UNUSED) +{ + const char *id; + + TRACE(self); + DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->server); + Py_BEGIN_ALLOW_THREADS + id = dbus_server_get_id(self->server); + Py_END_ALLOW_THREADS + + return PyString_FromString(id); +} + +PyDoc_STRVAR(Server_get_is_connected__doc__, +"get_is_connected() -> bool\n\n" +"Return true if this Server is still listening for new connections.\n"); +static PyObject * +Server_get_is_connected (Server *self, PyObject *args UNUSED) +{ + dbus_bool_t ret; + + TRACE(self); + DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->server); + Py_BEGIN_ALLOW_THREADS + ret = dbus_server_get_is_connected(self->server); + Py_END_ALLOW_THREADS + return PyBool_FromLong(ret); +} + +/* Server type object =============================================== */ + +struct PyMethodDef DBusPyServer_tp_methods[] = { +#define ENTRY(name, flags) {#name, (PyCFunction)Server_##name, flags, Server_##name##__doc__} + ENTRY(disconnect, METH_NOARGS), + ENTRY(get_address, METH_NOARGS), + ENTRY(get_id, METH_NOARGS), + ENTRY(get_is_connected, METH_NOARGS), + {NULL}, +#undef ENTRY +}; + +PyTypeObject DBusPyServer_Type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_dbus_bindings.Server",/*tp_name*/ + sizeof(Server), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)Server_tp_dealloc, + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_WEAKREFS | Py_TPFLAGS_BASETYPE, + Server_tp_doc, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + offsetof(Server, weaklist), /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + DBusPyServer_tp_methods,/*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ + 0, /*tp_base*/ + 0, /*tp_dict*/ + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + 0, /*tp_init*/ + 0, /*tp_alloc*/ + Server_tp_new, /*tp_new*/ + 0, /*tp_free*/ + 0, /*tp_is_gc*/ +}; + +dbus_bool_t +dbus_py_init_server_types(void) +{ + /* Get a slot to store our weakref on DBus Server */ +printf("%s:%d\n", __func__, __LINE__); + _server_python_slot = -1; + if (!dbus_server_allocate_data_slot(&_server_python_slot)) + return FALSE; +printf("%s:%d\n", __func__, __LINE__); + if (PyType_Ready(&DBusPyServer_Type) < 0) + return FALSE; +printf("%s:%d\n", __func__, __LINE__); + return TRUE; +} + +dbus_bool_t +dbus_py_insert_server_types(PyObject *this_module) +{ +printf("%s:%d\n", __func__, __LINE__); + if (PyModule_AddObject(this_module, "Server", + (PyObject *)&DBusPyServer_Type) < 0) return FALSE; +printf("%s:%d\n", __func__, __LINE__); + return TRUE; +} + +/* vim:set ft=c cino< sw=4 sts=4 et: */ diff --git a/dbus/server.py b/dbus/server.py new file mode 100644 index 0000000..c7a6184 --- /dev/null +++ b/dbus/server.py @@ -0,0 +1,38 @@ +# Copyright (C) 2008 Openismus GmbH +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation +# files (the "Software"), to deal in the Software without +# restriction, including without limitation the rights to use, copy, +# modify, merge, publish, distribute, sublicense, and/or sell copies +# of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. + +__all__ = ('Server', ) +__docformat__ = 'reStructuredText' + +from _dbus_bindings import Server as _Server + +class Server(_Server): + """An opaque object representing a server that listens for connections from + other applications. + + :Since: 0.82.5 + """ + + address = property(_Server.get_address) + id = property(_Server.get_id) + is_connected = property(_Server.get_is_connected) + diff --git a/test/test-server.py b/test/test-server.py new file mode 100755 index 0000000..2a9f824 --- /dev/null +++ b/test/test-server.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python + +from dbus.mainloop.glib import DBusGMainLoop + +import dbus +import dbus.bus +import dbus.service +import dbus.server + +import gobject +import os, sys + +class TestService(dbus.service.Object): + NAME = 'org.freedesktop.dbus.TestService' + PATH = '/org/freedesktop/dbus/TestService' + + def __init__(self, conn, path=PATH): + super(TestService, self).__init__(conn, path) + + @dbus.service.method(dbus_interface=NAME, + out_signature='s', + in_signature='s') + def reverse(self, text): + text = list(text) + text.reverse() + + return ''.join(text) + +class TestServer(dbus.server.Server): + def __init__(self, *args, **kwargs): + super(TestServer, self).__init__(*args, **kwargs) + self.__connections = list() + + def _on_new_connection(self, conn): + print 'new connection: %r' % conn + self.__connections.append(conn) + TestService(conn) + +pin, pout = os.pipe() +child = os.fork() + +if 0 == child: + DBusGMainLoop(set_as_default=True) + server = TestServer('unix:tmpdir=/tmp') + + os.write(pout, server.address) + os.close(pout) + os.close(pin) + + print 'server running: %s' % server.address + gobject.MainLoop().run() + + print 'server quit' + + print 'done?' + +else: + os.waitpid(child, os.WNOHANG) + os.close(pout) + + address = os.read(pin, 128) + os.close(pin) + + client = dbus.bus.Connection(address) + proxy = client.get_object(TestService.NAME, TestService.PATH) + object = dbus.Interface(proxy, TestService.NAME) + + while True: + line = sys.stdin.readline() + if not line: break + + text = line.strip() + print 'reverse(%s): %s' % (text, object.reverse(text)) + -- cgit v1.2.1 From ac83797b39b1d0eacaafcdf42772eab8ca91ce63 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 14 Jul 2008 15:39:02 +0100 Subject: DBusPyServer: refactor set_auth_mechanisms * save a malloc * return a boolean * don't crash if the sequence isn't a sequence * don't coerce items to strings too hard (we only want to accept str or unicode, accepting FooObject and trying to use it as an authentication method "" would be silly) --- _dbus_bindings/server.c | 52 +++++++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/_dbus_bindings/server.c b/_dbus_bindings/server.c index ec92b3b..d1d9c9d 100644 --- a/_dbus_bindings/server.c +++ b/_dbus_bindings/server.c @@ -76,42 +76,46 @@ DBusPyServer_BorrowDBusServer(PyObject *self) /* Internal C API =================================================== */ -static void -DBusPyServer_set_auth_mechanisms(Server *self, +static dbus_bool_t +DBusPyServer_set_auth_mechanisms(Server *self, PyObject *auth_mechanisms) { - const char **list = NULL; + PyObject *fast_seq; + Py_ssize_t length; + Py_ssize_t i; - if (auth_mechanisms) { - Py_ssize_t length; - Py_ssize_t i; + fast_seq = PySequence_Fast(auth_mechanisms, + "Expecting sequence for auth_mechanisms parameter"); - if (!PySequence_Check(auth_mechanisms)) { - PyErr_SetString(PyExc_TypeError, "Expecting sequence for auth_mechanisms parameter"); - return; - } + if (!fast_seq) + return FALSE; - length = PySequence_Fast_GET_SIZE(auth_mechanisms); + length = PySequence_Fast_GET_SIZE(fast_seq); - Py_BEGIN_ALLOW_THREADS - list = PyMem_New(const char*, length + 1); - Py_END_ALLOW_THREADS + /* scope for list */ + { + const char *list[length + 1]; for (i = 0; i < length; ++i) { - PyObject* am; + PyObject *am; am = PySequence_Fast_GET_ITEM(auth_mechanisms, i); - am = PyObject_Str(am); + /* this supports either str or unicode, raising TypeError + * on failure */ + list[i] = PyString_AsString(am); - list[i] = PyString_AS_STRING(am); + if (!list[i]) + return FALSE; } - } - Py_BEGIN_ALLOW_THREADS - dbus_server_set_auth_mechanisms(self->server, list); - Py_END_ALLOW_THREADS + list[length] = NULL; - PyMem_Free(list); + Py_BEGIN_ALLOW_THREADS + dbus_server_set_auth_mechanisms(self->server, list); + Py_END_ALLOW_THREADS + } + + return TRUE; } /* Return a new reference to a Python Server or subclass corresponding @@ -279,7 +283,9 @@ DBusPyServer_NewDBusServer(PyTypeObject *cls, !dbus_py_set_up_server((PyObject *)self, self->mainloop)) goto err; - DBusPyServer_set_auth_mechanisms(self, auth_mechanisms); + if (auth_mechanisms && auth_mechanisms != Py_None && + !DBusPyServer_set_auth_mechanisms(self, auth_mechanisms)) + goto err; Py_BEGIN_ALLOW_THREADS dbus_server_set_new_connection_function(self->server, -- cgit v1.2.1 From 1896381006ec54b1dfaf8bbcaaeab14b8a12ed32 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 14 Jul 2008 15:45:35 +0100 Subject: DBusPyServer: remove some stray debugging printfs --- _dbus_bindings/server.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/_dbus_bindings/server.c b/_dbus_bindings/server.c index d1d9c9d..0afa34d 100644 --- a/_dbus_bindings/server.c +++ b/_dbus_bindings/server.c @@ -160,7 +160,6 @@ DBusPyServer_new_connection_cb(DBusServer *server, PyObject *self = NULL, *conn_obj = NULL; PyObject *method = NULL, *result = NULL; -printf("%s:%d\n", __func__, __LINE__); self = DBusPyServer_ExistingFromDBusServer(server); if (!self) goto out; TRACE(self); @@ -168,7 +167,6 @@ printf("%s:%d\n", __func__, __LINE__); method = PyObject_GetAttrString(self, "_on_new_connection"); TRACE(method); -printf("%s:%d\n", __func__, __LINE__); if (method) { conn_obj = DBusPyConnection_NewConsumingDBusConnection(&DBusPyConnection_Type, dbus_connection_ref(conn), @@ -178,7 +176,6 @@ printf("%s:%d\n", __func__, __LINE__); } out: -printf("%s:%d\n", __func__, __LINE__); Py_XDECREF(result); Py_XDECREF(method); Py_XDECREF(conn_obj); -- cgit v1.2.1 From 37fd41a721a5417eeb72e90cbe3296f823ccc1b2 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 14 Jul 2008 16:08:26 +0100 Subject: Document that DBusPyServer_NewDBusServer consumes a reference to the server. Rename it to DBusPyServer_NewConsumingDBusServer to make this clearer. --- _dbus_bindings/server.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/_dbus_bindings/server.c b/_dbus_bindings/server.c index 0afa34d..6af462f 100644 --- a/_dbus_bindings/server.c +++ b/_dbus_bindings/server.c @@ -192,12 +192,15 @@ out: * created. For use by the Server constructor. * * Raises AssertionError if the DBusServer already has a Server. + * + * One reference to server is stolen - either the returned DBusPyServer + * claims it, or it's unreffed. */ static PyObject * -DBusPyServer_NewDBusServer(PyTypeObject *cls, - DBusServer *server, - PyObject *mainloop, - PyObject *auth_mechanisms) +DBusPyServer_NewConsumingDBusServer(PyTypeObject *cls, + DBusServer *server, + PyObject *mainloop, + PyObject *auth_mechanisms) { Server *self = NULL; PyObject *ref; @@ -338,7 +341,8 @@ printf("%s:%d\n", __func__, __LINE__); return NULL; } - self = DBusPyServer_NewDBusServer(cls, server, mainloop, auth_mechanisms); + self = DBusPyServer_NewConsumingDBusServer(cls, server, mainloop, + auth_mechanisms); TRACE(self); return self; -- cgit v1.2.1 From 1144c656fa9d956853fd8c03dc52937f60d3ee2e Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 14 Jul 2008 16:09:27 +0100 Subject: DBusPyServer: if there's no main loop, throw an exception The code starting at the "err" label assumes that an exception has been set already. --- _dbus_bindings/server.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/_dbus_bindings/server.c b/_dbus_bindings/server.c index 6af462f..3f32f43 100644 --- a/_dbus_bindings/server.c +++ b/_dbus_bindings/server.c @@ -232,8 +232,14 @@ DBusPyServer_NewConsumingDBusServer(PyTypeObject *cls, /* Change mainloop from a borrowed reference to an owned reference */ if (!mainloop || mainloop == Py_None) { mainloop = dbus_py_get_default_main_loop(); - if (!mainloop) + + if (!mainloop || mainloop == Py_None) { + PyErr_SetString(PyExc_RuntimeError, + "To run a D-Bus server, you need to either " + "pass mainloop=... to the constructor or cal " + "dbus.set_default_main_loop(...)"); goto err; + } } else { Py_INCREF(mainloop); -- cgit v1.2.1 From 0a888fb732074426d9b0dd6f487dffe5cee9df39 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 14 Jul 2008 16:09:57 +0100 Subject: DBusPyServer: Fix refcounting of the main loop --- _dbus_bindings/server.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/_dbus_bindings/server.c b/_dbus_bindings/server.c index 3f32f43..7a45449 100644 --- a/_dbus_bindings/server.c +++ b/_dbus_bindings/server.c @@ -253,9 +253,11 @@ DBusPyServer_NewConsumingDBusServer(PyTypeObject *cls, DBG_WHEREAMI; - self->mainloop = mainloop; self->server = NULL; + self->mainloop = mainloop; + mainloop = NULL; /* don't DECREF it - the DBusServer owns it now */ + ref = PyWeakref_NewRef((PyObject *)self, NULL); if (!ref) goto err; DBG("Created weak ref %p to (Server *)%p for (DBusServer *)%p", @@ -305,7 +307,7 @@ DBusPyServer_NewConsumingDBusServer(PyTypeObject *cls, err: DBG("Failed to construct Server from DBusServer at %p", server); -// Py_XDECREF(mainloop); + Py_XDECREF(mainloop); // Py_XDECREF(self); // Py_XDECREF(ref); if (server) { -- cgit v1.2.1 From 603d449610275db1e51816104630cce6d74ac9d4 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 14 Jul 2008 16:11:30 +0100 Subject: DBusPyServer: fix a typo --- _dbus_bindings/server.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_dbus_bindings/server.c b/_dbus_bindings/server.c index 7a45449..84d8a28 100644 --- a/_dbus_bindings/server.c +++ b/_dbus_bindings/server.c @@ -236,7 +236,7 @@ DBusPyServer_NewConsumingDBusServer(PyTypeObject *cls, if (!mainloop || mainloop == Py_None) { PyErr_SetString(PyExc_RuntimeError, "To run a D-Bus server, you need to either " - "pass mainloop=... to the constructor or cal " + "pass mainloop=... to the constructor or call " "dbus.set_default_main_loop(...)"); goto err; } -- cgit v1.2.1 From 6d77f23d062f047437dc8ce428d86a7ad9fa05b2 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 14 Jul 2008 16:12:27 +0100 Subject: DBusPyServer: correctly unref the weakref object on errors --- _dbus_bindings/server.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/_dbus_bindings/server.c b/_dbus_bindings/server.c index 84d8a28..8b59125 100644 --- a/_dbus_bindings/server.c +++ b/_dbus_bindings/server.c @@ -309,7 +309,8 @@ err: DBG("Failed to construct Server from DBusServer at %p", server); Py_XDECREF(mainloop); // Py_XDECREF(self); -// Py_XDECREF(ref); + Py_XDECREF(ref); + if (server) { Py_BEGIN_ALLOW_THREADS dbus_server_disconnect(server); -- cgit v1.2.1 From d9821035d58c4fe161a9ff0a2546198e2c1dbb49 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 14 Jul 2008 16:13:34 +0100 Subject: DBusPyServer: correctly unref the partially constructed DBusPyServer on errors --- _dbus_bindings/server.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/_dbus_bindings/server.c b/_dbus_bindings/server.c index 8b59125..ab89f59 100644 --- a/_dbus_bindings/server.c +++ b/_dbus_bindings/server.c @@ -308,7 +308,7 @@ DBusPyServer_NewConsumingDBusServer(PyTypeObject *cls, err: DBG("Failed to construct Server from DBusServer at %p", server); Py_XDECREF(mainloop); -// Py_XDECREF(self); + Py_XDECREF(self); Py_XDECREF(ref); if (server) { @@ -317,6 +317,7 @@ err: dbus_server_unref(server); Py_END_ALLOW_THREADS } + DBG("%s() fail", __func__); DBG_WHEREAMI; return NULL; -- cgit v1.2.1 From cf64e96607157f4e73c6dacaa34fb54fead26d1f Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 14 Jul 2008 16:14:48 +0100 Subject: DBusPyServer tp_dealloc: remove a stray debugging printf --- _dbus_bindings/server.c | 1 - 1 file changed, 1 deletion(-) diff --git a/_dbus_bindings/server.c b/_dbus_bindings/server.c index ab89f59..f9ede3c 100644 --- a/_dbus_bindings/server.c +++ b/_dbus_bindings/server.c @@ -364,7 +364,6 @@ static void Server_tp_dealloc(Server *self) DBusServer *server = self->server; PyObject *et, *ev, *etb; -printf("%s:%d\n", __func__, __LINE__); /* avoid clobbering any pending exception */ PyErr_Fetch(&et, &ev, &etb); -- cgit v1.2.1 From 05c19f23c63246322e4c9dfed2af47aaf77f7619 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 14 Jul 2008 16:15:43 +0100 Subject: DBusPyServer get_address, get_id: correct docs, these return str not String --- _dbus_bindings/server.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_dbus_bindings/server.c b/_dbus_bindings/server.c index f9ede3c..fbe1f4c 100644 --- a/_dbus_bindings/server.c +++ b/_dbus_bindings/server.c @@ -417,7 +417,7 @@ Server_disconnect (Server *self, PyObject *args UNUSED) } PyDoc_STRVAR(Server_get_address__doc__, -"get_address() -> String\n\n" +"get_address() -> str\n\n" "Returns the address of the server."); static PyObject * Server_get_address(Server *self, PyObject *args UNUSED) @@ -434,7 +434,7 @@ Server_get_address(Server *self, PyObject *args UNUSED) } PyDoc_STRVAR(Server_get_id__doc__, -"get_id() -> String\n\n" +"get_id() -> str\n\n" "Returns the unique ID of the server."); static PyObject * Server_get_id(Server *self, PyObject *args UNUSED) -- cgit v1.2.1 From 54a64a6571a6de18b87177ff1410e5d57f574463 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 14 Jul 2008 16:16:22 +0100 Subject: DBusPyServer initialization: remove some debugging printfs --- _dbus_bindings/server.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/_dbus_bindings/server.c b/_dbus_bindings/server.c index fbe1f4c..33dbaae 100644 --- a/_dbus_bindings/server.c +++ b/_dbus_bindings/server.c @@ -527,24 +527,22 @@ dbus_bool_t dbus_py_init_server_types(void) { /* Get a slot to store our weakref on DBus Server */ -printf("%s:%d\n", __func__, __LINE__); _server_python_slot = -1; if (!dbus_server_allocate_data_slot(&_server_python_slot)) return FALSE; -printf("%s:%d\n", __func__, __LINE__); + if (PyType_Ready(&DBusPyServer_Type) < 0) return FALSE; -printf("%s:%d\n", __func__, __LINE__); + return TRUE; } dbus_bool_t dbus_py_insert_server_types(PyObject *this_module) { -printf("%s:%d\n", __func__, __LINE__); if (PyModule_AddObject(this_module, "Server", (PyObject *)&DBusPyServer_Type) < 0) return FALSE; -printf("%s:%d\n", __func__, __LINE__); + return TRUE; } -- cgit v1.2.1 From 8e213001de0523bd7971fc9251c56635a9e943db Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 14 Jul 2008 16:40:32 +0100 Subject: test-server: import Connection from the right place --- test/test-server.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test-server.py b/test/test-server.py index 2a9f824..aef27c9 100755 --- a/test/test-server.py +++ b/test/test-server.py @@ -3,7 +3,7 @@ from dbus.mainloop.glib import DBusGMainLoop import dbus -import dbus.bus +import dbus.connection import dbus.service import dbus.server @@ -61,7 +61,7 @@ else: address = os.read(pin, 128) os.close(pin) - client = dbus.bus.Connection(address) + client = dbus.connection.Connection(address) proxy = client.get_object(TestService.NAME, TestService.PATH) object = dbus.Interface(proxy, TestService.NAME) -- cgit v1.2.1 From 87a86d7d6033cbb61271a84f20d5d25c4b4182c0 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 14 Jul 2008 16:41:28 +0100 Subject: DBusPyServer: construct a user-specified subtype of Connection --- _dbus_bindings/server.c | 32 +++++++++++++++++++++++++------- dbus/server.py | 20 ++++++++++++++++++++ 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/_dbus_bindings/server.c b/_dbus_bindings/server.c index 33dbaae..6f86417 100644 --- a/_dbus_bindings/server.c +++ b/_dbus_bindings/server.c @@ -2,6 +2,7 @@ * for DBusServer. * * Copyright (C) 2008 Openismus GmbH + * Copyright (C) 2008 Collabora Ltd. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -32,6 +33,9 @@ typedef struct { PyObject_HEAD DBusServer *server; + /* The Connection subtype for which this Server is a factory */ + PyObject *conn_class; + /* Weak-references list to make server weakly referenceable */ PyObject *weaklist; @@ -199,6 +203,7 @@ out: static PyObject * DBusPyServer_NewConsumingDBusServer(PyTypeObject *cls, DBusServer *server, + PyObject *conn_class, PyObject *mainloop, PyObject *auth_mechanisms) { @@ -255,6 +260,9 @@ DBusPyServer_NewConsumingDBusServer(PyTypeObject *cls, self->server = NULL; + Py_INCREF(conn_class); + self->conn_class = conn_class; + self->mainloop = mainloop; mainloop = NULL; /* don't DECREF it - the DBusServer owns it now */ @@ -331,12 +339,22 @@ Server_tp_new(PyTypeObject *cls, PyObject *args, PyObject *kwargs) DBusServer *server; const char *address; DBusError error; - PyObject *self, *mainloop = NULL, *auth_mechanisms = NULL; - static char *argnames[] = {"address", "mainloop", "auth_mechanisms", NULL}; + PyObject *self, *conn_class, *mainloop = NULL, *auth_mechanisms = NULL; + static char *argnames[] = { "address", "connection_class", "mainloop", + "auth_mechanisms", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|OO", argnames, + &address, &conn_class, &mainloop, &auth_mechanisms)) { + return NULL; + } -printf("%s:%d\n", __func__, __LINE__); - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|OO", argnames, - &address, &mainloop, &auth_mechanisms)) { + if (!PyType_Check(conn_class) || + !PyType_IsSubtype((PyTypeObject *) conn_class, &DBusPyConnection_Type)) { + /* strictly speaking, it can be any subtype of + * _dbus_bindings._Connection - but nobody else should be subtyping + * that, so let's keep this slightly inaccurate message */ + PyErr_SetString(PyExc_TypeError, "connection_class must be " + "dbus.connection.Connection or a subtype"); return NULL; } @@ -351,8 +369,8 @@ printf("%s:%d\n", __func__, __LINE__); return NULL; } - self = DBusPyServer_NewConsumingDBusServer(cls, server, mainloop, - auth_mechanisms); + self = DBusPyServer_NewConsumingDBusServer(cls, server, conn_class, + mainloop, auth_mechanisms); TRACE(self); return self; diff --git a/dbus/server.py b/dbus/server.py index c7a6184..632e7cd 100644 --- a/dbus/server.py +++ b/dbus/server.py @@ -1,4 +1,5 @@ # Copyright (C) 2008 Openismus GmbH +# Copyright (C) 2008 Collabora Ltd. # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation @@ -24,6 +25,7 @@ __all__ = ('Server', ) __docformat__ = 'reStructuredText' from _dbus_bindings import Server as _Server +from dbus.connection import Connection class Server(_Server): """An opaque object representing a server that listens for connections from @@ -32,6 +34,24 @@ class Server(_Server): :Since: 0.82.5 """ + def __new__(cls, address, connection_class=Connection, + mainloop=None, auth_mechanisms=None): + """Construct a new Server. + + :Parameters: + `address` : str + Listen on this address. + `connection_class` : type + When new connections come in, instantiate this subclass + of dbus.connection.Connection to represent them. + The default is Connection. + `mainloop` : dbus.mainloop.NativeMainLoop or None + The main loop with which to associate the new connections. + `auth_mechanisms` : sequence of str + Authentication mechanisms to allow. The default is to allow + any authentication mechanism supported by ``libdbus``. + """ + address = property(_Server.get_address) id = property(_Server.get_id) is_connected = property(_Server.get_is_connected) -- cgit v1.2.1 From 6dcd530f0b6aa158330726d7a5cca9d6dd96eafb Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 14 Jul 2008 16:43:15 +0100 Subject: Rename _dbus_bindings.Server to _dbus_bindings._Server --- _dbus_bindings/server.c | 9 +++++---- dbus/server.py | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/_dbus_bindings/server.c b/_dbus_bindings/server.c index 6f86417..048ef63 100644 --- a/_dbus_bindings/server.c +++ b/_dbus_bindings/server.c @@ -47,7 +47,8 @@ PyDoc_STRVAR(Server_tp_doc, "\n" "::\n" "\n" -" Server(address, mainloop=None, auth_mechanisms=None) -> Server\n" +" Server(address, connection_subtype, mainloop=None, auth_mechanisms=None)\n" +" -> Server\n" ); /* D-Bus Server user data slot, containing an owned reference to either @@ -66,7 +67,7 @@ DBusPyServer_BorrowDBusServer(PyObject *self) TRACE(self); if (!DBusPyServer_Check(self)) { - PyErr_SetString(PyExc_TypeError, "A dbus.Server is required"); + PyErr_SetString(PyExc_TypeError, "A dbus.server.Server is required"); return NULL; } dbs = ((Server *)self)->server; @@ -499,7 +500,7 @@ struct PyMethodDef DBusPyServer_tp_methods[] = { PyTypeObject DBusPyServer_Type = { PyObject_HEAD_INIT(NULL) 0, /*ob_size*/ - "_dbus_bindings.Server",/*tp_name*/ + "_dbus_bindings._Server",/*tp_name*/ sizeof(Server), /*tp_basicsize*/ 0, /*tp_itemsize*/ /* methods */ @@ -558,7 +559,7 @@ dbus_py_init_server_types(void) dbus_bool_t dbus_py_insert_server_types(PyObject *this_module) { - if (PyModule_AddObject(this_module, "Server", + if (PyModule_AddObject(this_module, "_Server", (PyObject *)&DBusPyServer_Type) < 0) return FALSE; return TRUE; diff --git a/dbus/server.py b/dbus/server.py index 632e7cd..3e45b3c 100644 --- a/dbus/server.py +++ b/dbus/server.py @@ -24,7 +24,7 @@ __all__ = ('Server', ) __docformat__ = 'reStructuredText' -from _dbus_bindings import Server as _Server +from _dbus_bindings import _Server from dbus.connection import Connection class Server(_Server): -- cgit v1.2.1 From c731758bd9d5dfcfe2a9e3176596bb43777ea334 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 14 Jul 2008 16:54:16 +0100 Subject: Hook DBusServer into the build system. Based on parts of the patch by Huang Peng --- Makefile.am | 1 + _dbus_bindings/Makefile.am | 1 + _dbus_bindings/dbus_bindings-internal.h | 8 ++++++++ _dbus_bindings/mainloop.c | 19 +++++++++++++++++++ _dbus_bindings/module.c | 2 ++ 5 files changed, 31 insertions(+) diff --git a/Makefile.am b/Makefile.am index 8f594ad..cd029e8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -34,6 +34,7 @@ nobase_python_PYTHON = \ dbus/mainloop/__init__.py \ dbus/mainloop/glib.py \ dbus/proxies.py \ + dbus/server.py \ dbus/service.py \ dbus/types.py diff --git a/_dbus_bindings/Makefile.am b/_dbus_bindings/Makefile.am index c2e59c9..e9e2421 100644 --- a/_dbus_bindings/Makefile.am +++ b/_dbus_bindings/Makefile.am @@ -24,6 +24,7 @@ _dbus_bindings_la_SOURCES = \ message-internal.h \ module.c \ pending-call.c \ + server.c \ signature.c \ string.c \ types-internal.h \ diff --git a/_dbus_bindings/dbus_bindings-internal.h b/_dbus_bindings/dbus_bindings-internal.h index 1dfd6b2..6b801c6 100644 --- a/_dbus_bindings/dbus_bindings-internal.h +++ b/_dbus_bindings/dbus_bindings-internal.h @@ -140,11 +140,19 @@ extern dbus_bool_t dbus_py_insert_pending_call(PyObject *this_module); /* mainloop.c */ extern dbus_bool_t dbus_py_set_up_connection(PyObject *conn, PyObject *mainloop); +extern dbus_bool_t dbus_py_set_up_server(PyObject *server, + PyObject *mainloop); extern PyObject *dbus_py_get_default_main_loop(void); extern dbus_bool_t dbus_py_check_mainloop_sanity(PyObject *); extern dbus_bool_t dbus_py_init_mainloop(void); extern dbus_bool_t dbus_py_insert_mainloop_types(PyObject *); +/* server.c */ +extern PyTypeObject DBusPyServer_Type; +DEFINE_CHECK(DBusPyServer) +extern dbus_bool_t dbus_py_init_server_types(void); +extern dbus_bool_t dbus_py_insert_server_types(PyObject *this_module); + /* validation.c */ dbus_bool_t dbus_py_validate_bus_name(const char *name, dbus_bool_t may_be_unique, diff --git a/_dbus_bindings/mainloop.c b/_dbus_bindings/mainloop.c index 3b56ade..1733410 100644 --- a/_dbus_bindings/mainloop.c +++ b/_dbus_bindings/mainloop.c @@ -1,6 +1,7 @@ /* Implementation of main-loop integration for dbus-python. * * Copyright (C) 2006 Collabora Ltd. + * Copyright (C) 2008 Huang Peng * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -131,6 +132,24 @@ dbus_py_set_up_connection(PyObject *conn, PyObject *mainloop) return FALSE; } +dbus_bool_t +dbus_py_set_up_server(PyObject *server, PyObject *mainloop) +{ + if (NativeMainLoop_Check(mainloop)) { + /* Native mainloops are allowed to do arbitrary strange things */ + NativeMainLoop *nml = (NativeMainLoop *)mainloop; + DBusServer *dbs = DBusPyServer_BorrowDBusServer(server); + + if (!dbs) { + return FALSE; + } + return (nml->set_up_server_cb)(dbs, nml->data); + } + PyErr_SetString(PyExc_TypeError, + "A dbus.mainloop.NativeMainLoop instance is required"); + return FALSE; +} + /* C API ============================================================ */ PyObject * diff --git a/_dbus_bindings/module.c b/_dbus_bindings/module.c index ddeb1f0..a7e0820 100644 --- a/_dbus_bindings/module.c +++ b/_dbus_bindings/module.c @@ -268,6 +268,7 @@ init_dbus_bindings(void) if (!dbus_py_init_pending_call()) return; if (!dbus_py_init_mainloop()) return; if (!dbus_py_init_conn_types()) return; + if (!dbus_py_init_server_types()) return; this_module = Py_InitModule3("_dbus_bindings", module_functions, module_doc); if (!this_module) return; @@ -283,6 +284,7 @@ init_dbus_bindings(void) if (!dbus_py_insert_pending_call(this_module)) return; if (!dbus_py_insert_mainloop_types(this_module)) return; if (!dbus_py_insert_conn_types(this_module)) return; + if (!dbus_py_insert_server_types(this_module)) return; if (PyModule_AddStringConstant(this_module, "BUS_DAEMON_NAME", DBUS_SERVICE_DBUS) < 0) return; -- cgit v1.2.1 From b9925dc51a1fe6d07c263f7f956005b44ca95b29 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 14 Jul 2008 16:56:02 +0100 Subject: Add some whitespace to make reStructuredText happy --- dbus/_dbus.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dbus/_dbus.py b/dbus/_dbus.py index 18747c0..60e7933 100644 --- a/dbus/_dbus.py +++ b/dbus/_dbus.py @@ -76,8 +76,10 @@ class Bus(BusConnection): `private` : bool If true, never return an existing shared instance, but instead return a private connection. + :Deprecated: since 0.82.3. Use dbus.bus.BusConnection for private connections. + `mainloop` : dbus.mainloop.NativeMainLoop The main loop to use. The default is to use the default main loop if one has been set up, or raise an exception -- cgit v1.2.1 From 2a646b1a5ac0ac15923e657058703f146f12a452 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 14 Jul 2008 17:18:45 +0100 Subject: Fix thinko in dbus.server: actually instantiate a _Server --- dbus/server.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dbus/server.py b/dbus/server.py index 3e45b3c..fd2da87 100644 --- a/dbus/server.py +++ b/dbus/server.py @@ -51,6 +51,8 @@ class Server(_Server): Authentication mechanisms to allow. The default is to allow any authentication mechanism supported by ``libdbus``. """ + return super(Server, cls).__new__(address, connection_class, + mainloop, auth_mechanisms) address = property(_Server.get_address) id = property(_Server.get_id) -- cgit v1.2.1 From 689e366ec0192920bcc961a0c44e89898d9bd8b1 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 14 Jul 2008 17:19:56 +0100 Subject: Fix *another* thinko in dbus.server --- dbus/server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbus/server.py b/dbus/server.py index fd2da87..6b7eb50 100644 --- a/dbus/server.py +++ b/dbus/server.py @@ -51,7 +51,7 @@ class Server(_Server): Authentication mechanisms to allow. The default is to allow any authentication mechanism supported by ``libdbus``. """ - return super(Server, cls).__new__(address, connection_class, + return super(Server, cls).__new__(cls, address, connection_class, mainloop, auth_mechanisms) address = property(_Server.get_address) -- cgit v1.2.1 From ee4c9f80cd6a6e839eeebd2e592e4793e9bb61a8 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 14 Jul 2008 17:24:35 +0100 Subject: dbus.server.Server: implement a stub version of _on_new_connection --- dbus/server.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/dbus/server.py b/dbus/server.py index 6b7eb50..2cf11c8 100644 --- a/dbus/server.py +++ b/dbus/server.py @@ -31,6 +31,9 @@ class Server(_Server): """An opaque object representing a server that listens for connections from other applications. + This class is not useful to instantiate directly: you must subclass it and + provide an implementation of the _on_new_connection method. + :Since: 0.82.5 """ @@ -54,6 +57,20 @@ class Server(_Server): return super(Server, cls).__new__(cls, address, connection_class, mainloop, auth_mechanisms) + def _on_new_connection(self, conn): + """Respond to the creation of a new Connection. + + :Parameters: + `conn` : dbus.connection.Connection + A D-Bus connection. + + The type of this parameter is whatever was passed + to the Server constructor as the ``connection_class``. + """ + raise NotImplementedError('Subclasses of Server must implement ' + '_on_new_connection') + + address = property(_Server.get_address) id = property(_Server.get_id) is_connected = property(_Server.get_is_connected) -- cgit v1.2.1 From a7e29e3dbab9651761fc9a7c3ec7fbc27c711286 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 14 Jul 2008 17:30:43 +0100 Subject: Actually create objects of class Server->conn_class --- _dbus_bindings/server.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/_dbus_bindings/server.c b/_dbus_bindings/server.c index 048ef63..a7295a6 100644 --- a/_dbus_bindings/server.c +++ b/_dbus_bindings/server.c @@ -173,9 +173,10 @@ DBusPyServer_new_connection_cb(DBusServer *server, TRACE(method); if (method) { - conn_obj = DBusPyConnection_NewConsumingDBusConnection(&DBusPyConnection_Type, - dbus_connection_ref(conn), - ((Server*) self)->mainloop); + conn_obj = DBusPyConnection_NewConsumingDBusConnection( + ((Server *) self)->conn_class, + dbus_connection_ref(conn), + ((Server*) self)->mainloop); result = PyObject_CallFunctionObjArgs(method, conn_obj, NULL); } -- cgit v1.2.1 From 565d2e88c54d3f2e6dab4ae0ed3202d3a26bfd2c Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 14 Jul 2008 18:44:15 +0100 Subject: Add Connection.call_on_disconnection --- dbus/connection.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/dbus/connection.py b/dbus/connection.py index 3a551ea..e2ec3b2 100644 --- a/dbus/connection.py +++ b/dbus/connection.py @@ -246,6 +246,8 @@ class Connection(_Connection): if not hasattr(self, '_dbus_Connection_initialized'): self._dbus_Connection_initialized = 1 + self.__call_on_disconnection = [] + self._signal_recipients_by_object_path = {} """Map from object path to dict mapping dbus_interface to dict mapping member to list of SignalMatch objects.""" @@ -508,6 +510,19 @@ class Connection(_Connection): for match in self._iter_easy_matches(path, dbus_interface, signal_name): match.maybe_handle_message(message) + + if (dbus_interface == LOCAL_IFACE and + path == LOCAL_PATH and + signal_name == 'Disconnected'): + for cb in self.__call_on_disconnection: + try: + cb(self) + except Exception, e: + # basicConfig is a no-op if logging is already configured + logging.basicConfig() + _logger.error('Exception in handler for Disconnected ' + 'signal:', exc_info=1) + return HANDLER_RESULT_NOT_YET_HANDLED def call_async(self, bus_name, object_path, dbus_interface, method, @@ -612,3 +627,12 @@ class Connection(_Connection): return args_list[0] else: return tuple(args_list) + + def call_on_disconnection(self, callable): + """Arrange for `callable` to be called with one argument (this + Connection object) when the Connection becomes + disconnected. + + :Since: 0.83.0 + """ + self.__call_on_disconnection.append(callable) -- cgit v1.2.1 From 9d9322f9faf49f7ac8b4c3048f1333a9d7cabb48 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Tue, 15 Jul 2008 18:54:54 +0100 Subject: Add LibDBusConnection in _dbus_bindings --- _dbus_bindings/Makefile.am | 1 + _dbus_bindings/conn-internal.h | 5 ++ _dbus_bindings/dbus_bindings-internal.h | 16 ++++- _dbus_bindings/libdbusconn.c | 124 ++++++++++++++++++++++++++++++++ 4 files changed, 143 insertions(+), 3 deletions(-) create mode 100644 _dbus_bindings/libdbusconn.c diff --git a/_dbus_bindings/Makefile.am b/_dbus_bindings/Makefile.am index e9e2421..c6cd1ee 100644 --- a/_dbus_bindings/Makefile.am +++ b/_dbus_bindings/Makefile.am @@ -17,6 +17,7 @@ _dbus_bindings_la_SOURCES = \ float.c \ generic.c \ int.c \ + libdbusconn.c \ mainloop.c \ message-append.c \ message.c \ diff --git a/_dbus_bindings/conn-internal.h b/_dbus_bindings/conn-internal.h index 4f83c39..f4c7a80 100644 --- a/_dbus_bindings/conn-internal.h +++ b/_dbus_bindings/conn-internal.h @@ -46,6 +46,11 @@ typedef struct { dbus_bool_t has_mainloop; } Connection; +typedef struct { + PyObject_HEAD + DBusConnection *conn; +} DBusPyLibDBusConnection; + extern struct PyMethodDef DBusPyConnection_tp_methods[]; extern DBusHandlerResult DBusPyConnection_HandleMessage(Connection *, PyObject *, diff --git a/_dbus_bindings/dbus_bindings-internal.h b/_dbus_bindings/dbus_bindings-internal.h index 6b801c6..efc9f40 100644 --- a/_dbus_bindings/dbus_bindings-internal.h +++ b/_dbus_bindings/dbus_bindings-internal.h @@ -52,17 +52,27 @@ typedef int Py_ssize_t; static inline int type##_Check (PyObject *o) \ { \ return (PyObject_TypeCheck (o, &type##_Type)); \ +} \ +static inline int type##_CheckExact (PyObject *o) \ +{ \ + return ((o)->ob_type == &type##_Type); \ } +PyMODINIT_FUNC init_dbus_bindings(void); + /* conn.c */ extern PyTypeObject DBusPyConnection_Type; DEFINE_CHECK(DBusPyConnection) -extern PyObject *DBusPyConnection_NewConsumingDBusConnection(PyTypeObject *, - DBusConnection *, - PyObject *); extern dbus_bool_t dbus_py_init_conn_types(void); extern dbus_bool_t dbus_py_insert_conn_types(PyObject *this_module); +/* libdbusconn.c */ +extern PyTypeObject DBusPyLibDBusConnection_Type; +DEFINE_CHECK(DBusPyLibDBusConnection) +PyObject *DBusPyLibDBusConnection_New(DBusConnection *conn); +extern dbus_bool_t dbus_py_init_libdbus_conn_types(void); +extern dbus_bool_t dbus_py_insert_libdbus_conn_types(PyObject *this_module); + /* bus.c */ extern dbus_bool_t dbus_py_init_bus_types(void); extern dbus_bool_t dbus_py_insert_bus_types(PyObject *this_module); diff --git a/_dbus_bindings/libdbusconn.c b/_dbus_bindings/libdbusconn.c new file mode 100644 index 0000000..9bd8def --- /dev/null +++ b/_dbus_bindings/libdbusconn.c @@ -0,0 +1,124 @@ +/* An extremely thin wrapper around a libdbus Connection, for use by + * Server. + * + * Copyright (C) 2008 Collabora Ltd. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "dbus_bindings-internal.h" +#include "conn-internal.h" + +PyDoc_STRVAR(DBusPyLibDBusConnection_tp_doc, +"A reference to a ``DBusConnection`` from ``libdbus``, which might not\n" +"have been attached to a `dbus.connection.Connection` yet.\n" +"\n" +"Cannot be instantiated from Python. The only use of this object is to\n" +"pass it to the ``dbus.connection.Connection`` constructor instead of an\n" +"address.\n" +); + +/** Create a DBusPyLibDBusConnection from a DBusConnection. + */ +PyObject * +DBusPyLibDBusConnection_New(DBusConnection *conn) +{ + DBusPyLibDBusConnection *self = NULL; + + DBUS_PY_RAISE_VIA_NULL_IF_FAIL(conn); + + self = (DBusPyLibDBusConnection *)(DBusPyLibDBusConnection_Type.tp_alloc( + &DBusPyLibDBusConnection_Type, 0)); + + if (!self) + return NULL; + + self->conn = dbus_connection_ref (conn); + + return (PyObject *)self; +} + +/* Destructor */ +static void +DBusPyLibDBusConnection_tp_dealloc(Connection *self) +{ + DBusConnection *conn = self->conn; + PyObject *et, *ev, *etb; + + /* avoid clobbering any pending exception */ + PyErr_Fetch(&et, &ev, &etb); + + self->conn = NULL; + + if (conn) { + dbus_connection_unref(conn); + } + + PyErr_Restore(et, ev, etb); + (self->ob_type->tp_free)((PyObject *) self); +} + +PyTypeObject DBusPyLibDBusConnection_Type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_dbus_bindings._LibDBusConnection", + sizeof(DBusPyLibDBusConnection), + 0, /*tp_itemsize*/ + /* methods */ + (destructor)DBusPyLibDBusConnection_tp_dealloc, + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, + DBusPyLibDBusConnection_tp_doc, +}; + +dbus_bool_t +dbus_py_init_libdbus_conn_types(void) +{ + if (PyType_Ready(&DBusPyLibDBusConnection_Type) < 0) + return FALSE; + + return TRUE; +} + +dbus_bool_t +dbus_py_insert_libdbus_conn_types(PyObject *this_module) +{ + if (PyModule_AddObject(this_module, "_LibDBusConnection", + (PyObject *)&DBusPyLibDBusConnection_Type) < 0) + return FALSE; + + return TRUE; +} + +/* vim:set ft=c cino< sw=4 sts=4 et: */ -- cgit v1.2.1 From 9d53f6c5179c590089bd6560e266dda538202f93 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Tue, 15 Jul 2008 18:59:33 +0100 Subject: [trivial] un-extern Struct_tp_getattro --- _dbus_bindings/containers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_dbus_bindings/containers.c b/_dbus_bindings/containers.c index e80098f..319ebe1 100644 --- a/_dbus_bindings/containers.c +++ b/_dbus_bindings/containers.c @@ -642,7 +642,7 @@ Struct_tp_dealloc(PyObject *self) (PyTuple_Type.tp_dealloc)(self); } -PyObject * +static PyObject * Struct_tp_getattro(PyObject *obj, PyObject *name) { PyObject *key, *value; -- cgit v1.2.1 From 0f0193180b7853cfea486d0aef1b60d4fa067ed2 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Tue, 15 Jul 2008 19:13:55 +0100 Subject: _dbus_bindings._Connection: sort out constructor Accept a LibDBusConnection for the address (sic) parameter, so we can construct a Connection for a DBusConnection that already exists. The way all this works right now is a bit unfortunate, with hindsight, but most of it is fixable like this. --- _dbus_bindings/conn.c | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/_dbus_bindings/conn.c b/_dbus_bindings/conn.c index ddc570a..483d4ef 100644 --- a/_dbus_bindings/conn.c +++ b/_dbus_bindings/conn.c @@ -1,7 +1,7 @@ /* Implementation of the _dbus_bindings Connection type, a Python wrapper * for DBusConnection. See also conn-methods.c. * - * Copyright (C) 2006 Collabora Ltd. + * Copyright (C) 2006-2008 Collabora Ltd. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -293,34 +293,48 @@ err: /* Connection type-methods ========================================== */ -/* "Constructor" (the real constructor is Connection_NewFromDBusConnection, - * to which this delegates). */ +/* Constructor */ static PyObject * Connection_tp_new(PyTypeObject *cls, PyObject *args, PyObject *kwargs) { DBusConnection *conn; const char *address; + PyObject *address_or_conn; DBusError error; PyObject *self, *mainloop = NULL; static char *argnames[] = {"address", "mainloop", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|O", argnames, - &address, &mainloop)) { + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O", argnames, + &address_or_conn, &mainloop)) { return NULL; } - dbus_error_init(&error); + if (DBusPyLibDBusConnection_CheckExact(address_or_conn)) { + DBusPyLibDBusConnection *wrapper = + (DBusPyLibDBusConnection *) address_or_conn; - /* We always open a private connection (at the libdbus level). Sharing - * is done in Python, to keep things simple. */ - Py_BEGIN_ALLOW_THREADS - conn = dbus_connection_open_private(address, &error); - Py_END_ALLOW_THREADS + DBUS_PY_RAISE_VIA_NULL_IF_FAIL(wrapper->conn); + + conn = dbus_connection_ref (wrapper->conn); + } + else if ((address = PyString_AsString(address_or_conn)) != NULL) { + dbus_error_init(&error); + + /* We always open a private connection (at the libdbus level). Sharing + * is done in Python, to keep things simple. */ + Py_BEGIN_ALLOW_THREADS + conn = dbus_connection_open_private(address, &error); + Py_END_ALLOW_THREADS - if (!conn) { - DBusPyException_ConsumeError(&error); + if (!conn) { + DBusPyException_ConsumeError(&error); + return NULL; + } + } + else { return NULL; } + self = DBusPyConnection_NewConsumingDBusConnection(cls, conn, mainloop); TRACE(self); -- cgit v1.2.1 From bdc76e63da5ca9e017cfbea6c1ce1b0e21ebf706 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Tue, 15 Jul 2008 19:15:42 +0100 Subject: Refactor DBusPyConnection_NewForBus and make it go via DbusPyConnection_Type.tp_new Now that we have the LibDBusConnection temporary objects, we don't have to do strange sideways inheritance, we can just chain up to the superclass constructor. --- _dbus_bindings/bus.c | 85 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 59 insertions(+), 26 deletions(-) diff --git a/_dbus_bindings/bus.c b/_dbus_bindings/bus.c index 5d64a6a..7ab0d95 100644 --- a/_dbus_bindings/bus.c +++ b/_dbus_bindings/bus.c @@ -33,8 +33,6 @@ DBusPyConnection_NewForBus(PyTypeObject *cls, PyObject *args, PyObject *kwargs) DBusConnection *conn; DBusError error; Connection *self; - dbus_bool_t ret; - long type; static char *argnames[] = {"address_or_type", "mainloop", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OO", argnames, @@ -45,6 +43,8 @@ DBusPyConnection_NewForBus(PyTypeObject *cls, PyObject *args, PyObject *kwargs) dbus_error_init(&error); if (first && PyString_Check(first)) { + dbus_bool_t ret; + /* It's a custom address. First connect to it, then register. */ self = (Connection *)(DBusPyConnection_Type.tp_new)(cls, args, kwargs); @@ -62,37 +62,70 @@ DBusPyConnection_NewForBus(PyTypeObject *cls, PyObject *args, PyObject *kwargs) return (PyObject *)self; } + else if (!first || PyInt_Check(first)) { + long type; + PyObject *libdbusconn; + PyObject *new_args; + PyObject *new_kwargs; + + /* If the first argument isn't a string, it must be an integer + representing one of the well-known bus types. The default is + DBUS_BUS_SESSION. */ + + if (first) { + type = PyInt_AsLong(first); + + if (type != DBUS_BUS_SESSION && type != DBUS_BUS_SYSTEM + && type != DBUS_BUS_STARTER) { + PyErr_Format(PyExc_ValueError, "Unknown bus type %ld", type); + return NULL; + } + } + else { + type = DBUS_BUS_SESSION; + } - /* If the first argument isn't a string, it must be an integer - representing one of the well-known bus types. */ + Py_BEGIN_ALLOW_THREADS + conn = dbus_bus_get_private(type, &error); + Py_END_ALLOW_THREADS - if (first && !PyInt_Check(first)) { - PyErr_SetString(PyExc_TypeError, "A string address or an integer " - "bus type is required"); - return NULL; - } - if (first) { - type = PyInt_AsLong(first); - } - else { - type = DBUS_BUS_SESSION; - } + if (!conn) { + DBusPyException_ConsumeError(&error); + return NULL; + } - if (type != DBUS_BUS_SESSION && type != DBUS_BUS_SYSTEM - && type != DBUS_BUS_STARTER) { - PyErr_Format(PyExc_ValueError, "Unknown bus type %d", (int)type); - return NULL; - } + libdbusconn = DBusPyLibDBusConnection_New (conn); + dbus_connection_unref (conn); - Py_BEGIN_ALLOW_THREADS - conn = dbus_bus_get_private(type, &error); - Py_END_ALLOW_THREADS + if (!libdbusconn) + return NULL; + + new_args = PyTuple_Pack(2, libdbusconn, mainloop ? mainloop : Py_None); + Py_DECREF(libdbusconn); + + if (!new_args) { + return NULL; + } + + new_kwargs = PyDict_New(); - if (!conn) { - DBusPyException_ConsumeError(&error); + if (!new_kwargs) { + Py_DECREF(new_args); + return NULL; + } + + self = (Connection *)(DBusPyConnection_Type.tp_new)(cls, new_args, + new_kwargs); + Py_DECREF(new_args); + Py_DECREF(new_kwargs); + + return (PyObject *)self; /* whether NULL or not */ + } + else { + PyErr_SetString(PyExc_TypeError, "A string address or an integer " + "bus type is required"); return NULL; } - return DBusPyConnection_NewConsumingDBusConnection(cls, conn, mainloop); } PyObject * -- cgit v1.2.1 From 2f7e3865c5cae60769b98dac163802e224345fb9 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Tue, 15 Jul 2008 19:21:51 +0100 Subject: DbusPyServer: construct connections by calling the type, so the object will be fully initialized --- _dbus_bindings/server.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/_dbus_bindings/server.c b/_dbus_bindings/server.c index a7295a6..7fc4f70 100644 --- a/_dbus_bindings/server.c +++ b/_dbus_bindings/server.c @@ -162,8 +162,8 @@ DBusPyServer_new_connection_cb(DBusServer *server, void *data UNUSED) { PyGILState_STATE gil = PyGILState_Ensure(); - PyObject *self = NULL, *conn_obj = NULL; - PyObject *method = NULL, *result = NULL; + PyObject *self = NULL; + PyObject *method = NULL; self = DBusPyServer_ExistingFromDBusServer(server); if (!self) goto out; @@ -173,18 +173,30 @@ DBusPyServer_new_connection_cb(DBusServer *server, TRACE(method); if (method) { - conn_obj = DBusPyConnection_NewConsumingDBusConnection( - ((Server *) self)->conn_class, - dbus_connection_ref(conn), - ((Server*) self)->mainloop); + PyObject *conn_class = ((Server *)self)->conn_class; + PyObject *wrapper = DBusPyLibDBusConnection_New(conn); + PyObject *conn_obj; + PyObject *result; + + if (!wrapper) + goto out; + + conn_obj = PyObject_CallFunctionObjArgs((PyObject *)conn_class, + wrapper, ((Server*) self)->mainloop, NULL); + Py_DECREF(wrapper); + + if (!conn_obj) + goto out; result = PyObject_CallFunctionObjArgs(method, conn_obj, NULL); + Py_XDECREF (conn_obj); + + /* discard result if not NULL, and fall through regardless */ + Py_XDECREF(result); } out: - Py_XDECREF(result); Py_XDECREF(method); - Py_XDECREF(conn_obj); Py_XDECREF(self); if (PyErr_Occurred()) -- cgit v1.2.1 From 88a08077393d4c6f091109ccc819c6ec4664ba71 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Tue, 15 Jul 2008 19:22:21 +0100 Subject: Make DBusPyConnection_NewConsumingDBusConnection static now nobody else needs to call it --- _dbus_bindings/conn.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_dbus_bindings/conn.c b/_dbus_bindings/conn.c index 483d4ef..c30f167 100644 --- a/_dbus_bindings/conn.c +++ b/_dbus_bindings/conn.c @@ -180,7 +180,7 @@ DBusPyConnection_ExistingFromDBusConnection(DBusConnection *conn) * * Raises AssertionError if the DBusConnection already has a Connection. */ -PyObject * +static PyObject * DBusPyConnection_NewConsumingDBusConnection(PyTypeObject *cls, DBusConnection *conn, PyObject *mainloop) -- cgit v1.2.1 From 07196538f58c069313eeda2c496278f8289b9437 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Tue, 15 Jul 2008 19:22:33 +0100 Subject: Add some more compiler warning flags if supported --- configure.ac | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/configure.ac b/configure.ac index 7bfa3f9..a9bd749 100644 --- a/configure.ac +++ b/configure.ac @@ -148,6 +148,11 @@ JH_ADD_CFLAG([-Wall]) JH_ADD_CFLAG([-Wextra]) JH_ADD_CFLAG([-Wno-missing-field-initializers]) JH_ADD_CFLAG([-Wdeclaration-after-statement]) +JH_ADD_CFLAG([-Wshadow]) +JH_ADD_CFLAG([-Wstrict-prototypes]) +JH_ADD_CFLAG([-Wmissing-prototypes]) +JH_ADD_CFLAG([-Wmissing-declarations]) +JH_ADD_CFLAG([-Wdeprecated-declarations]) JH_ADD_CFLAG([-std=c9x]) JH_ADD_CFLAG([-fno-strict-aliasing]) -- cgit v1.2.1 From 810b67cd6d30e4af73067090c7fe1ae14952ce00 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Tue, 15 Jul 2008 19:30:33 +0100 Subject: Initialize LibDBusConnection correctly --- _dbus_bindings/module.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/_dbus_bindings/module.c b/_dbus_bindings/module.c index a7e0820..bf5fb0d 100644 --- a/_dbus_bindings/module.c +++ b/_dbus_bindings/module.c @@ -267,6 +267,7 @@ init_dbus_bindings(void) if (!dbus_py_init_message_types()) return; if (!dbus_py_init_pending_call()) return; if (!dbus_py_init_mainloop()) return; + if (!dbus_py_init_libdbus_conn_types()) return; if (!dbus_py_init_conn_types()) return; if (!dbus_py_init_server_types()) return; @@ -283,6 +284,7 @@ init_dbus_bindings(void) if (!dbus_py_insert_message_types(this_module)) return; if (!dbus_py_insert_pending_call(this_module)) return; if (!dbus_py_insert_mainloop_types(this_module)) return; + if (!dbus_py_insert_libdbus_conn_types(this_module)) return; if (!dbus_py_insert_conn_types(this_module)) return; if (!dbus_py_insert_server_types(this_module)) return; -- cgit v1.2.1 From a7725c9d7589773de7c068f11ca63b95f99ccfcc Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Tue, 15 Jul 2008 19:33:01 +0100 Subject: Alter dbus.server.Server API to have pseudo-signals By either appending to a list of callbacks, or subclassing and providing a method, you can be notified when connections are added or removed. Inspired by the DBusServer patch from Huang Peng. --- dbus/server.py | 48 ++++++++++++++++++++++++++++++++++++++++++++---- test/test-server.py | 24 +++++++++++++----------- 2 files changed, 57 insertions(+), 15 deletions(-) diff --git a/dbus/server.py b/dbus/server.py index 2cf11c8..d690e1a 100644 --- a/dbus/server.py +++ b/dbus/server.py @@ -32,7 +32,8 @@ class Server(_Server): other applications. This class is not useful to instantiate directly: you must subclass it and - provide an implementation of the _on_new_connection method. + either extend the method connection_added, or append to the + list on_connection_added. :Since: 0.82.5 """ @@ -57,19 +58,58 @@ class Server(_Server): return super(Server, cls).__new__(cls, address, connection_class, mainloop, auth_mechanisms) + def __init__(self, *args, **kwargs): + + self.__connections = {} + + self.on_connection_added = [] + """A list of callbacks to invoke when a connection is added. + They receive two arguments: this Server and the new Connection.""" + + self.on_connection_removed = [] + """A list of callbacks to invoke when a connection becomes + disconnected. They receive two arguments: this Server and the removed + Connection.""" + + # This method name is hard-coded in _dbus_bindings._Server. + # This is not public API. def _on_new_connection(self, conn): + conn.call_on_disconnection(self.connection_removed) + self.connection_added(conn) + + def connection_added(self, conn): """Respond to the creation of a new Connection. + This base-class implementation just invokes the callbacks in + the on_connection_added attribute. + :Parameters: `conn` : dbus.connection.Connection - A D-Bus connection. + A D-Bus connection which has just been added. The type of this parameter is whatever was passed to the Server constructor as the ``connection_class``. """ - raise NotImplementedError('Subclasses of Server must implement ' - '_on_new_connection') + if self.on_connection_added: + for cb in self.on_connection_added: + cb(conn) + + def connection_removed(self, conn): + """Respond to the disconnection of a Connection. + This base-class implementation just invokes the callbacks in + the on_connection_removed attribute. + + :Parameters: + `conn` : dbus.connection.Connection + A D-Bus connection which has just become disconnected. + + The type of this parameter is whatever was passed + to the Server constructor as the ``connection_class``. + """ + if self.on_connection_removed: + for cb in self.on_connection_removed: + cb(conn) address = property(_Server.get_address) id = property(_Server.get_id) diff --git a/test/test-server.py b/test/test-server.py index aef27c9..b909d84 100755 --- a/test/test-server.py +++ b/test/test-server.py @@ -26,22 +26,23 @@ class TestService(dbus.service.Object): return ''.join(text) -class TestServer(dbus.server.Server): - def __init__(self, *args, **kwargs): - super(TestServer, self).__init__(*args, **kwargs) - self.__connections = list() - - def _on_new_connection(self, conn): - print 'new connection: %r' % conn - self.__connections.append(conn) - TestService(conn) - pin, pout = os.pipe() child = os.fork() if 0 == child: DBusGMainLoop(set_as_default=True) - server = TestServer('unix:tmpdir=/tmp') + server = dbus.server.Server('unix:tmpdir=/tmp') + + def new_connection(conn): + print "new connection, %r" % conn + TestService(conn) + + def connection_gone(conn): + print "goodbye, %r" % conn + + # Instantiate a TestService every time a connection is created + server.on_connection_added.append(new_connection) + server.on_connection_removed.append(connection_gone) os.write(pout, server.address) os.close(pout) @@ -72,3 +73,4 @@ else: text = line.strip() print 'reverse(%s): %s' % (text, object.reverse(text)) + client.close() -- cgit v1.2.1 From b962965f8c30d785ade69dd6a60924b42d6a1c8d Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Thu, 17 Jul 2008 12:17:31 +0100 Subject: Update NEWS. Let's call the next release 0.83 since it's a feature release --- NEWS | 8 +++++++- dbus/server.py | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index a866177..c0a63a8 100644 --- a/NEWS +++ b/NEWS @@ -1,14 +1,20 @@ -D-Bus Python Bindings 0.82.5 (unreleased) +D-Bus Python Bindings 0.83.0 (unreleased) ========================================= Features: +* add bindings for DBusServer (thanks to Mathias Hasselmann, Huang Peng; + fd.o #14322, #15514) + Fixes: * don't emit spurious Error messages if libdbus gives object-path handlers a message that isn't a method call (most likely because of binding to a locally emitted signal, as in fd.o #14199) +* Make multiple filters added by Connection.add_message_filter work + (fd.o #15547, thanks to Huang Peng) + D-Bus Python Bindings 0.82.4 (2007-12-10) ========================================= diff --git a/dbus/server.py b/dbus/server.py index d690e1a..1988101 100644 --- a/dbus/server.py +++ b/dbus/server.py @@ -35,7 +35,7 @@ class Server(_Server): either extend the method connection_added, or append to the list on_connection_added. - :Since: 0.82.5 + :Since: 0.83 """ def __new__(cls, address, connection_class=Connection, -- cgit v1.2.1 From d1ded84e774c4aaad9bf02842e1898580dd599ea Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Thu, 17 Jul 2008 13:00:15 +0100 Subject: Omit the remote traceback from certain D-Bus errors Specifically, DBusException and its subclasses no longer have the remote traceback by default (although subclasses can turn it back on again by setting include_traceback = True, and the various "programmer error" subclasses of DBusException do have this set). Hopefully this will stop people thinking it's a dbus-python or telepathy-python bug when a D-Bus API like Telepathy deliberately raises an error (and so dbus-python or telepathy-python is visible in the traceback). --- dbus/exceptions.py | 33 +++++++++++++++++++++++++++++++++ dbus/service.py | 5 ++++- test/test-client.py | 37 +++++++++++++++++++++++++++++++++++++ test/test-service.py | 22 ++++++++++++++++++++++ 4 files changed, 96 insertions(+), 1 deletion(-) diff --git a/dbus/exceptions.py b/dbus/exceptions.py index 6a0fbaf..8d84a29 100644 --- a/dbus/exceptions.py +++ b/dbus/exceptions.py @@ -28,6 +28,17 @@ __all__ = ('DBusException', 'MissingErrorHandlerException', 'NameExistsException') class DBusException(Exception): + + include_traceback = False + """If True, tracebacks will be included in the exception message sent to + D-Bus clients. + + Exceptions that are not DBusException subclasses always behave + as though this is True. Set this to True on DBusException subclasses + that represent a programming error, and leave it False on subclasses that + represent an expected failure condition (e.g. a network server not + responding).""" + def __init__(self, *args, **kwargs): name = kwargs.pop('name', None) if name is not None or getattr(self, '_dbus_error_name', None) is None: @@ -44,30 +55,52 @@ class DBusException(Exception): else: return s + def get_dbus_message(self): + s = Exception.__str__(self) + return s.decode('utf-8', 'replace') + def get_dbus_name(self): return self._dbus_error_name class MissingErrorHandlerException(DBusException): + + include_traceback = True + def __init__(self): DBusException.__init__(self, "error_handler not defined: if you define a reply_handler you must also define an error_handler") class MissingReplyHandlerException(DBusException): + + include_traceback = True + def __init__(self): DBusException.__init__(self, "reply_handler not defined: if you define an error_handler you must also define a reply_handler") class ValidationException(DBusException): + + include_traceback = True + def __init__(self, msg=''): DBusException.__init__(self, "Error validating string: %s"%msg) class IntrospectionParserException(DBusException): + + include_traceback = True + def __init__(self, msg=''): DBusException.__init__(self, "Error parsing introspect data: %s"%msg) class UnknownMethodException(DBusException): + + include_traceback = True _dbus_error_name = 'org.freedesktop.DBus.Error.UnknownMethod' + def __init__(self, method): DBusException.__init__(self, "Unknown method: %s"%method) class NameExistsException(DBusException): + + include_traceback = True + def __init__(self, name): DBusException.__init__(self, "Bus name already exists: %s"%name) diff --git a/dbus/service.py b/dbus/service.py index 55bb04e..b92d840 100644 --- a/dbus/service.py +++ b/dbus/service.py @@ -277,7 +277,10 @@ def _method_reply_error(connection, message, exception): name = 'org.freedesktop.DBus.Python.%s.%s' % (exception.__module__, exception.__class__.__name__) et, ev, etb = sys.exc_info() - if ev is exception: + if isinstance(exception, DBusException) and not exception.include_traceback: + # We don't actually want the traceback anyway + contents = exception.get_dbus_message() + elif ev is exception: # The exception was actually thrown, so we can get a traceback contents = ''.join(traceback.format_exception(et, ev, etb)) else: diff --git a/test/test-client.py b/test/test-client.py index 5b977da..753d892 100755 --- a/test/test-client.py +++ b/test/test-client.py @@ -508,6 +508,43 @@ class TestDBusBindings(unittest.TestCase): self.assertRaises(dbus.DBusException, lambda: self.iface.AsyncWait500ms(timeout=0.25)) + def testExceptions(self): + #self.assertRaises(dbus.DBusException, + # lambda: self.iface.RaiseValueError) + #self.assertRaises(dbus.DBusException, + # lambda: self.iface.RaiseDBusExceptionNoTraceback) + #self.assertRaises(dbus.DBusException, + # lambda: self.iface.RaiseDBusExceptionWithTraceback) + + try: + self.iface.RaiseValueError() + except Exception, e: + self.assert_(isinstance(e, dbus.DBusException), e.__class__) + self.assert_('.ValueError: Traceback ' in str(e), + 'Wanted a traceback but got:\n"""%s"""' % str(e)) + else: + raise AssertionError('Wanted an exception') + + try: + self.iface.RaiseDBusExceptionNoTraceback() + except Exception, e: + self.assert_(isinstance(e, dbus.DBusException), e.__class__) + self.assertEquals(str(e), + 'com.example.Networking.ServerError: ' + 'Server not responding') + else: + raise AssertionError('Wanted an exception') + + try: + self.iface.RaiseDBusExceptionWithTraceback() + except Exception, e: + self.assert_(isinstance(e, dbus.DBusException), e.__class__) + self.assert_(str(e).startswith('com.example.Misc.RealityFailure: ' + 'Traceback '), + 'Wanted a traceback but got:\n%s' % str(e)) + else: + raise AssertionError('Wanted an exception') + """ Remove this for now class TestDBusPythonToGLibBindings(unittest.TestCase): def setUp(self): diff --git a/test/test-service.py b/test/test-service.py index 74829d4..51865eb 100755 --- a/test/test-service.py +++ b/test/test-service.py @@ -305,6 +305,28 @@ class TestObject(dbus.service.Object, TestInterface): return False gobject.timeout_add(500, return_from_async_wait) + @dbus.service.method(IFACE, in_signature='', out_signature='') + def RaiseValueError(self): + raise ValueError('Wrong!') + + @dbus.service.method(IFACE, in_signature='', out_signature='') + def RaiseDBusExceptionNoTraceback(self): + class ServerError(dbus.DBusException): + """Exception representing a normal "environmental" error""" + include_traceback = False + _dbus_error_name = 'com.example.Networking.ServerError' + + raise ServerError('Server not responding') + + @dbus.service.method(IFACE, in_signature='', out_signature='') + def RaiseDBusExceptionWithTraceback(self): + class RealityFailure(dbus.DBusException): + """Exception representing a programming error""" + include_traceback = True + _dbus_error_name = 'com.example.Misc.RealityFailure' + + raise RealityFailure('Botched invariant') + @dbus.service.method(IFACE, in_signature='', out_signature='', async_callbacks=('return_cb', 'raise_cb')) def AsyncRaise(self, return_cb, raise_cb): -- cgit v1.2.1 From 8c2ef87d94525af4b1e7f21e18b0a07b30ab425b Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Thu, 17 Jul 2008 13:06:06 +0100 Subject: Update NEWS again --- NEWS | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/NEWS b/NEWS index c0a63a8..56c48b5 100644 --- a/NEWS +++ b/NEWS @@ -3,17 +3,22 @@ D-Bus Python Bindings 0.83.0 (unreleased) Features: -* add bindings for DBusServer (thanks to Mathias Hasselmann, Huang Peng; - fd.o #14322, #15514) +* Add bindings for DBusServer (thanks to Mathias Hasselmann, Huang Peng; + fd.o #14322, #15514). + +* Omit the service's traceback from certain D-Bus errors: specifically, those + that were probably deliberately raised as part of an API. Subclasses + of DBusException that indicate programmer error can turn the traceback + back on if it seems likely to be useful. Fixes: -* don't emit spurious Error messages if libdbus gives object-path handlers +* Don't emit spurious Error messages if libdbus gives object-path handlers a message that isn't a method call (most likely because of binding to a - locally emitted signal, as in fd.o #14199) + locally emitted signal, as in fd.o #14199). * Make multiple filters added by Connection.add_message_filter work - (fd.o #15547, thanks to Huang Peng) + (fd.o #15547, thanks to Huang Peng). D-Bus Python Bindings 0.82.4 (2007-12-10) ========================================= -- cgit v1.2.1