summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon McVittie <simon.mcvittie@collabora.co.uk>2008-07-17 13:10:53 +0100
committerSimon McVittie <simon.mcvittie@collabora.co.uk>2008-07-17 13:10:53 +0100
commit1cbb5a302724041dab107a8634cbea6b0fe1bc99 (patch)
tree62c97727b71d13a8f96cb6c13f5b1be20954138a
parent5235d4028fc863fe81d8aa7ccbe2d5f0830fd392 (diff)
parent8c2ef87d94525af4b1e7f21e18b0a07b30ab425b (diff)
downloaddbus-python-1cbb5a302724041dab107a8634cbea6b0fe1bc99.tar.gz
Merge branch 'master' into purity
Conflicts: NEWS _dbus_bindings/containers.c dbus/connection.py
-rw-r--r--Makefile.am1
-rw-r--r--NEWS17
-rw-r--r--_dbus_bindings/Makefile.am2
-rw-r--r--_dbus_bindings/bus.c85
-rw-r--r--_dbus_bindings/conn-internal.h5
-rw-r--r--_dbus_bindings/conn.c42
-rw-r--r--_dbus_bindings/dbus_bindings-internal.h24
-rw-r--r--_dbus_bindings/libdbusconn.c124
-rw-r--r--_dbus_bindings/mainloop.c19
-rw-r--r--_dbus_bindings/module.c4
-rw-r--r--_dbus_bindings/server.c581
-rw-r--r--configure.ac5
-rw-r--r--dbus/_dbus.py2
-rw-r--r--dbus/connection.py24
-rw-r--r--dbus/exceptions.py33
-rw-r--r--dbus/server.py117
-rw-r--r--dbus/service.py5
-rwxr-xr-xtest/test-client.py37
-rwxr-xr-xtest/test-server.py76
-rwxr-xr-xtest/test-service.py22
20 files changed, 1178 insertions, 47 deletions
diff --git a/Makefile.am b/Makefile.am
index 565633f..bb91e4e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -35,6 +35,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/NEWS b/NEWS
index de6b6fd..294ec69 100644
--- a/NEWS
+++ b/NEWS
@@ -14,16 +14,27 @@ Roadmap:
* Implement message serialization and deserialization in pure Python
(may need to be conditional on having dbus >= 1.1)
-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).
+
+* 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).
D-Bus Python Bindings 0.82.4 (2007-12-10)
=========================================
diff --git a/_dbus_bindings/Makefile.am b/_dbus_bindings/Makefile.am
index c2e59c9..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 \
@@ -24,6 +25,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/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 *
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/conn.c b/_dbus_bindings/conn.c
index ddc570a..c30f167 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. <http://www.collabora.co.uk/>
+ * Copyright (C) 2006-2008 Collabora Ltd. <http://www.collabora.co.uk/>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
@@ -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)
@@ -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);
diff --git a/_dbus_bindings/dbus_bindings-internal.h b/_dbus_bindings/dbus_bindings-internal.h
index 6ffbc37..c9a0d69 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);
@@ -173,11 +183,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/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. <http://www.collabora.co.uk/>
+ *
+ * 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: */
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. <http://www.collabora.co.uk/>
+ * Copyright (C) 2008 Huang Peng <phuang@redhat.com>
*
* 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 8dd756c..9c274b7 100644
--- a/_dbus_bindings/module.c
+++ b/_dbus_bindings/module.c
@@ -288,7 +288,9 @@ 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;
this_module = Py_InitModule3("_dbus_bindings", module_functions, module_doc);
if (!this_module) return;
@@ -303,7 +305,9 @@ 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;
if (PyModule_AddStringConstant(this_module, "BUS_DAEMON_NAME",
DBUS_SERVICE_DBUS) < 0) return;
diff --git a/_dbus_bindings/server.c b/_dbus_bindings/server.c
new file mode 100644
index 0000000..7fc4f70
--- /dev/null
+++ b/_dbus_bindings/server.c
@@ -0,0 +1,581 @@
+/* Implementation of the _dbus_bindings Server type, a Python wrapper
+ * for DBusServer.
+ *
+ * Copyright (C) 2008 Openismus GmbH <http://openismus.com/>
+ * Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * 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;
+
+ /* The Connection subtype for which this Server is a factory */
+ PyObject *conn_class;
+
+ /* 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, connection_subtype, mainloop=None, auth_mechanisms=None)\n"
+" -> 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.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 dbus_bool_t
+DBusPyServer_set_auth_mechanisms(Server *self,
+ PyObject *auth_mechanisms)
+{
+ PyObject *fast_seq;
+ Py_ssize_t length;
+ Py_ssize_t i;
+
+ fast_seq = PySequence_Fast(auth_mechanisms,
+ "Expecting sequence for auth_mechanisms parameter");
+
+ if (!fast_seq)
+ return FALSE;
+
+ length = PySequence_Fast_GET_SIZE(fast_seq);
+
+ /* scope for list */
+ {
+ const char *list[length + 1];
+
+ for (i = 0; i < length; ++i) {
+ PyObject *am;
+
+ am = PySequence_Fast_GET_ITEM(auth_mechanisms, i);
+ /* this supports either str or unicode, raising TypeError
+ * on failure */
+ list[i] = PyString_AsString(am);
+
+ if (!list[i])
+ return FALSE;
+ }
+
+ list[length] = NULL;
+
+ 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
+ * 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;
+ PyObject *method = NULL;
+
+ self = DBusPyServer_ExistingFromDBusServer(server);
+ if (!self) goto out;
+ TRACE(self);
+
+ method = PyObject_GetAttrString(self, "_on_new_connection");
+ TRACE(method);
+
+ if (method) {
+ 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(method);
+ 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.
+ *
+ * One reference to server is stolen - either the returned DBusPyServer
+ * claims it, or it's unreffed.
+ */
+static PyObject *
+DBusPyServer_NewConsumingDBusServer(PyTypeObject *cls,
+ DBusServer *server,
+ PyObject *conn_class,
+ 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 || mainloop == Py_None) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "To run a D-Bus server, you need to either "
+ "pass mainloop=... to the constructor or call "
+ "dbus.set_default_main_loop(...)");
+ 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->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 */
+
+ 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;
+
+ 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,
+ 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, *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;
+ }
+
+ 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;
+ }
+
+ 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_NewConsumingDBusServer(cls, server, conn_class,
+ mainloop, auth_mechanisms);
+ TRACE(self);
+
+ return self;
+}
+
+/* Destructor */
+static void Server_tp_dealloc(Server *self)
+{
+ DBusServer *server = self->server;
+ PyObject *et, *ev, *etb;
+
+ /* 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() -> str\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() -> str\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 */
+ _server_python_slot = -1;
+ if (!dbus_server_allocate_data_slot(&_server_python_slot))
+ return FALSE;
+
+ if (PyType_Ready(&DBusPyServer_Type) < 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+dbus_bool_t
+dbus_py_insert_server_types(PyObject *this_module)
+{
+ if (PyModule_AddObject(this_module, "_Server",
+ (PyObject *)&DBusPyServer_Type) < 0) return FALSE;
+
+ return TRUE;
+}
+
+/* vim:set ft=c cino< sw=4 sts=4 et: */
diff --git a/configure.ac b/configure.ac
index 614c034..b8f4e53 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])
diff --git a/dbus/_dbus.py b/dbus/_dbus.py
index 2b1c20f..c8ce770 100644
--- a/dbus/_dbus.py
+++ b/dbus/_dbus.py
@@ -77,8 +77,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
diff --git a/dbus/connection.py b/dbus/connection.py
index ca61c5a..aecf623 100644
--- a/dbus/connection.py
+++ b/dbus/connection.py
@@ -253,6 +253,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."""
@@ -822,6 +824,19 @@ class Connection(_Connection):
path = message.get_path()
signal_name = message.get_member()
+ 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)
+
for match in self._iter_easy_matches(path, dbus_interface,
signal_name):
try:
@@ -865,3 +880,12 @@ class Connection(_Connection):
def add_message_filter(self):
self._filters.append(filter)
+
+ 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)
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/server.py b/dbus/server.py
new file mode 100644
index 0000000..1988101
--- /dev/null
+++ b/dbus/server.py
@@ -0,0 +1,117 @@
+# Copyright (C) 2008 Openismus GmbH <http://openismus.com/>
+# Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+#
+# 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
+from dbus.connection import Connection
+
+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
+ either extend the method connection_added, or append to the
+ list on_connection_added.
+
+ :Since: 0.83
+ """
+
+ 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``.
+ """
+ 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 which has just been added.
+
+ The type of this parameter is whatever was passed
+ to the Server constructor as the ``connection_class``.
+ """
+ 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)
+ is_connected = property(_Server.get_is_connected)
+
diff --git a/dbus/service.py b/dbus/service.py
index 141d1a3..acaa85d 100644
--- a/dbus/service.py
+++ b/dbus/service.py
@@ -278,7 +278,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-server.py b/test/test-server.py
new file mode 100755
index 0000000..b909d84
--- /dev/null
+++ b/test/test-server.py
@@ -0,0 +1,76 @@
+#!/usr/bin/env python
+
+from dbus.mainloop.glib import DBusGMainLoop
+
+import dbus
+import dbus.connection
+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)
+
+pin, pout = os.pipe()
+child = os.fork()
+
+if 0 == child:
+ DBusGMainLoop(set_as_default=True)
+ 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)
+ 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.connection.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))
+
+ client.close()
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):