[PATCH] Add wrapper for DBusServer.

Huang Peng shawn.p.huang at gmail.com
Tue Jun 24 20:33:44 PDT 2008


---
 Makefile.am                             |    1 +
 _dbus_bindings/Makefile.am              |    3 +
 _dbus_bindings/conn-methods.c           |    1 +
 _dbus_bindings/dbus_bindings-internal.h |    8 +
 _dbus_bindings/mainloop.c               |   18 ++
 _dbus_bindings/module.c                 |    2 +
 _dbus_bindings/server-internal.h        |   51 ++++
 _dbus_bindings/server-methods.c         |  215 ++++++++++++++++++
 _dbus_bindings/server.c                 |  377 +++++++++++++++++++++++++++++++
 dbus/lowlevel.py                        |    2 +
 dbus/server.py                          |   71 ++++++
 examples/Makefile.am                    |    5 +-
 examples/example-server-client.py       |   18 ++
 examples/example-server.py              |   61 +++++
 include/dbus-python.h                   |    1 +
 15 files changed, 833 insertions(+), 1 deletions(-)
 create mode 100644 _dbus_bindings/server-internal.h
 create mode 100644 _dbus_bindings/server-methods.c
 create mode 100644 _dbus_bindings/server.c
 create mode 100644 dbus/server.py
 create mode 100644 examples/example-server-client.py
 create mode 100644 examples/example-server.py

diff --git a/Makefile.am b/Makefile.am
index 8f594ad..40ac719 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -35,6 +35,7 @@ nobase_python_PYTHON = \
     dbus/mainloop/glib.py \
     dbus/proxies.py \
     dbus/service.py \
+    dbus/server.py \
     dbus/types.py
 
 check_py_sources = $(nobase_python_PYTHON)
diff --git a/_dbus_bindings/Makefile.am b/_dbus_bindings/Makefile.am
index c2e59c9..a747e18 100644
--- a/_dbus_bindings/Makefile.am
+++ b/_dbus_bindings/Makefile.am
@@ -7,6 +7,9 @@ _dbus_bindings_la_SOURCES = \
 			    abstract.c \
 			    bus.c \
 			    bytes.c \
+			    server.c \
+			    server-internal.h \
+			    server-methods.c \
 			    conn.c \
 			    conn-internal.h \
 			    conn-methods.c \
diff --git a/_dbus_bindings/conn-methods.c b/_dbus_bindings/conn-methods.c
index 6fadc31..81c4514 100644
--- a/_dbus_bindings/conn-methods.c
+++ b/_dbus_bindings/conn-methods.c
@@ -179,6 +179,7 @@ _filter_message(DBusConnection *conn, DBusMessage *message, void *user_data)
         callable = PyList_GET_ITEM(conn_obj->filters, i);
         if (callable == user_data) {
             Py_INCREF(callable);
+            break;
         }
         else {
             callable = NULL;
diff --git a/_dbus_bindings/dbus_bindings-internal.h b/_dbus_bindings/dbus_bindings-internal.h
index 1dfd6b2..ea3f2aa 100644
--- a/_dbus_bindings/dbus_bindings-internal.h
+++ b/_dbus_bindings/dbus_bindings-internal.h
@@ -54,6 +54,12 @@ static inline int type##_Check (PyObject *o) \
     return (PyObject_TypeCheck (o, &type##_Type)); \
 }
 
+/* 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);
+
 /* conn.c */
 extern PyTypeObject DBusPyConnection_Type;
 DEFINE_CHECK(DBusPyConnection)
@@ -138,6 +144,8 @@ extern dbus_bool_t dbus_py_init_pending_call(void);
 extern dbus_bool_t dbus_py_insert_pending_call(PyObject *this_module);
 
 /* mainloop.c */
+extern dbus_bool_t dbus_py_set_up_server(PyObject *server,
+                                         PyObject *mainloop);
 extern dbus_bool_t dbus_py_set_up_connection(PyObject *conn,
                                              PyObject *mainloop);
 extern PyObject *dbus_py_get_default_main_loop(void);
diff --git a/_dbus_bindings/mainloop.c b/_dbus_bindings/mainloop.c
index 3b56ade..c4659fc 100644
--- a/_dbus_bindings/mainloop.c
+++ b/_dbus_bindings/mainloop.c
@@ -114,6 +114,24 @@ dbus_py_check_mainloop_sanity(PyObject *mainloop)
 }
 
 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;
+}
+
+dbus_bool_t
 dbus_py_set_up_connection(PyObject *conn, PyObject *mainloop)
 {
     if (NativeMainLoop_Check(mainloop)) {
diff --git a/_dbus_bindings/module.c b/_dbus_bindings/module.c
index ddeb1f0..de5966b 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_server_types()) return;
     if (!dbus_py_init_conn_types()) return;
 
     this_module = Py_InitModule3("_dbus_bindings", module_functions, module_doc);
@@ -282,6 +283,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_server_types(this_module)) return;
     if (!dbus_py_insert_conn_types(this_module)) return;
 
     if (PyModule_AddStringConstant(this_module, "BUS_DAEMON_NAME",
diff --git a/_dbus_bindings/server-internal.h b/_dbus_bindings/server-internal.h
new file mode 100644
index 0000000..cde1b1f
--- /dev/null
+++ b/_dbus_bindings/server-internal.h
@@ -0,0 +1,51 @@
+/* _dbus_bindings internal API. For use within _dbus_bindings only.
+ *
+ * Copyright (C) 2008 Huang Peng <phuang at redhat.com> .
+ *
+ * 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.
+ */
+
+#ifndef DBUS_BINDINGS_SERVER_H
+#define DBUS_BINDINGS_SERVER_H
+
+#include "dbus_bindings-internal.h"
+
+typedef struct {
+    PyObject_HEAD
+    DBusServer *server;
+
+	PyObject *new_connection_callback;
+	PyTypeObject *new_connection_type;
+    /* Weak-references list to make Connections weakly referenceable */
+    PyObject *weaklist;
+
+    PyObject *mainloop;
+} Server;
+
+extern struct PyMethodDef DBusPyServer_tp_methods[];
+extern DBusHandlerResult DBusPyServer_HandleMessage(Server *,
+                                                    PyObject *,
+                                                    PyObject *);
+extern PyObject *DBusPyServer_ExistingFromDBusServer(DBusServer *);
+
+extern PyObject *DBusPyServer_GetAddress(Server *, PyObject *);
+
+#endif
diff --git a/_dbus_bindings/server-methods.c b/_dbus_bindings/server-methods.c
new file mode 100644
index 0000000..1e10d06
--- /dev/null
+++ b/_dbus_bindings/server-methods.c
@@ -0,0 +1,215 @@
+/* Implementation of normal Python-accessible methods on the _dbus_bindings
+ * Connection type; separated out to keep the file size manageable.
+ *
+ * Copyright (C) 2008 Huang Peng <phuang at redhat.com> .
+ *
+ * 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 "server-internal.h"
+
+PyDoc_STRVAR(Server_disconnect__doc__,
+"disconnect()\n\n"
+"Releases the server's address and stops listening for new clients.\n");
+static PyObject *
+Server_disconnect (Server *self, PyObject *args UNUSED)
+{
+    TRACE(self);
+    DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->server);
+    Py_BEGIN_ALLOW_THREADS
+    dbus_server_disconnect(self->server);
+    Py_END_ALLOW_THREADS
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+
+
+PyDoc_STRVAR(Server_get_address__doc__,
+"get_address() -> String\n\n"
+"Return the address of the server.\n");
+static PyObject *
+Server_get_address (Server *self, PyObject *args UNUSED)
+{
+    char *address;
+    PyObject *ret;
+
+    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
+    ret = PyString_FromString(address);
+    dbus_free (address);
+    return ret;
+}
+
+PyDoc_STRVAR(Server_get_id__doc__,
+"get_id() -> String\n\n"
+"Return the id of the server.\n");
+static PyObject *
+Server_get_id (Server *self, PyObject *args UNUSED)
+{
+    char *id;
+    PyObject *ret;
+
+    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
+
+    ret = PyString_FromString(id);
+    dbus_free (id);
+
+    return ret;
+}
+
+PyDoc_STRVAR(Server_get_is_connected__doc__,
+"get_is_connected() -> bool\n\n"
+"Return True if server is istill listening new connections.\n");
+static PyObject *
+Server_get_is_connected (Server *self, PyObject *args UNUSED)
+{
+    PyObject *ret;
+
+    TRACE(self);
+    DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->server);
+    Py_BEGIN_ALLOW_THREADS
+    if (dbus_server_get_is_connected(self->server))
+        ret = Py_True;
+    else
+        ret = Py_False;
+    Py_END_ALLOW_THREADS
+
+   Py_INCREF(ret);
+
+    return ret;
+}
+
+
+PyDoc_STRVAR(Server_set_new_connection_function__doc__,
+"set_new_connection_function()\n\n"
+"Set a function for handling new connections.\n");
+static PyObject *
+Server_set_new_connection_function (Server *self, PyObject *args)
+{
+
+    PyObject *callback;
+    PyTypeObject *conn_type = NULL;
+
+    if (!PyArg_ParseTuple(args, "O|O:set_new_connection_function",
+                          &callback, &conn_type)) {
+        return NULL;
+    }
+
+    if (callback != Py_None && ! PyCallable_Check (callback)) {
+        PyErr_SetString (PyExc_TypeError,
+                            "The first argument must be a callable object");
+        return NULL;
+    }
+
+    if (!conn_type || (PyObject *)conn_type == Py_None) {
+        conn_type = &DBusPyConnection_Type;
+    }
+
+    if (!PyType_IsSubtype (conn_type, &DBusPyConnection_Type)) {
+        PyErr_SetString (PyExc_TypeError,
+                            "The second argument must be None or Subclass of _dbus_bindings_.Connection");
+        return NULL;
+    }
+
+    TRACE(self);
+    DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->server);
+    Py_BEGIN_ALLOW_THREADS
+    Py_XDECREF (self->new_connection_callback);
+    if (callback != Py_None) {
+        Py_INCREF (callback);
+        self->new_connection_callback = callback;
+    }
+    else
+        self->new_connection_callback = NULL;
+
+    Py_XDECREF (self->new_connection_type);
+    Py_INCREF (conn_type);
+    self->new_connection_type = conn_type;
+
+    Py_END_ALLOW_THREADS
+
+    Py_INCREF (Py_None);
+    return Py_None;
+}
+
+
+PyDoc_STRVAR(Server_set_auth_mechanisms__doc__,
+"set_auth_mechanisms (...)\n\n"
+"Set the authentication mechanisms that this server offers to clients.\n");
+static PyObject *
+Server_set_auth_mechanisms (Server *self, PyObject *args)
+{
+
+    int len, i;
+    const char ** _mechanisms = NULL;
+
+    len = PyTuple_Size (args);
+
+    if (len > 0) {
+        _mechanisms = (const char **)malloc (sizeof (char *) * (len + 1));
+        for (i = 0; i < len; i++) {
+            PyObject *mechanism = PyTuple_GetItem (args, i);
+            if (!PyString_Check (mechanism)) {
+                free (_mechanisms);
+                PyErr_SetString(PyExc_TypeError,
+                    "arguments must be strings");
+                return NULL;
+            }
+            _mechanisms[i] = PyString_AS_STRING (mechanism);
+        }
+        _mechanisms[len] = NULL;
+    }
+
+    TRACE(self);
+    DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->server);
+    Py_BEGIN_ALLOW_THREADS
+    dbus_server_set_auth_mechanisms (self->server, _mechanisms);
+    Py_END_ALLOW_THREADS
+
+    if (_mechanisms != NULL)
+        free (_mechanisms);
+    Py_INCREF (Py_None);
+    return Py_None;
+}
+
+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),
+    ENTRY(set_new_connection_function, METH_VARARGS),
+    ENTRY(set_auth_mechanisms, METH_VARARGS),
+    {NULL},
+#undef ENTRY
+};
+
+/* vim:set ft=c cino< sw=4 sts=4 et: */
diff --git a/_dbus_bindings/server.c b/_dbus_bindings/server.c
new file mode 100644
index 0000000..3de7e07
--- /dev/null
+++ b/_dbus_bindings/server.c
@@ -0,0 +1,377 @@
+/* Implementation of the _dbus_bindings Server type, a Python wrapper
+ * for DBusServer. See also server-methods.c.
+ *
+ * Copyright (C) 2008 Huang Peng <phuang at redhat.com> .
+ *
+ * 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 "server-internal.h"
+
+/* Server definition ============================================ */
+
+PyDoc_STRVAR(Server_tp_doc,
+"A D-Bus server.\n"
+"\n"
+"::\n"
+"\n"
+"   Server(address, mainloop=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 =================================================== */
+
+/* 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.
+ */
+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;
+}
+
+/* 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 and Bus constructors.
+ *
+ * Raises AssertionError if the DBusServer already has a Server.
+ */
+
+static void
+Server_new_connection_callback (DBusServer *server, DBusConnection *connection, void * data UNUSED)
+{
+    PyObject *conn = NULL;
+    PyObject *result = NULL;
+    PyObject *args = NULL;
+    Server *self = NULL;
+
+    self = (Server *)DBusPyServer_ExistingFromDBusServer(server);
+    if (self == NULL)
+        goto error;
+
+    if (self->new_connection_callback) {
+        conn = DBusPyConnection_NewConsumingDBusConnection(self->new_connection_type, connection, self->mainloop);
+        if (conn == NULL)
+            goto error;
+        dbus_connection_ref (connection);
+
+        args = Py_BuildValue ("(OO)", self, conn);
+        if (args == NULL)
+            goto error;
+        result = PyObject_Call (self->new_connection_callback, args, NULL);
+        if (result == NULL)
+            goto error;
+
+    }
+    goto out;
+error:
+    PyErr_Print ();
+out:
+    Py_XDECREF (conn);
+    Py_XDECREF (result);
+    Py_XDECREF (args);
+    Py_XDECREF ((PyObject *)self);
+}
+
+/* Server type-methods ========================================== */
+
+/* "Constructor" (the real constructor is Server_NewFromDBusServer,
+ * to which this delegates). */
+static PyObject *
+Server_tp_new(PyTypeObject *cls, PyObject *args UNUSED, PyObject *kwargs UNUSED)
+{
+    PyObject *self;
+    self = cls->tp_alloc (cls, 0);
+    return (PyObject *)self;
+
+}
+
+
+static int
+Server_tp_init(Server *self, PyObject *args, PyObject *kwargs)
+{
+    PyObject *mainloop = NULL;
+    DBusServer *server = NULL;
+    char *address = NULL;
+
+    PyObject *ref = NULL;
+    DBusError error;
+    dbus_bool_t ok;
+
+    static char *argnames[] = {"address", "mainloop", NULL};
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|O", argnames,
+                                     &address, &mainloop)) {
+        return -1;
+    }
+
+    if (!mainloop || mainloop == Py_None) {
+        mainloop = dbus_py_get_default_main_loop();
+        if (!mainloop) {
+            PyErr_SetString (PyExc_Exception,
+                                "Can not get default mainloop.");
+            goto err;
+        }
+    }
+    else {
+        Py_INCREF (mainloop);
+    }
+
+
+    dbus_error_init(&error);
+
+    Py_BEGIN_ALLOW_THREADS
+    server = dbus_server_listen(address, &error);
+    Py_END_ALLOW_THREADS
+
+    if (!server) {
+        DBusPyException_ConsumeError(&error);
+        goto err;
+    }
+
+    dbus_server_set_new_connection_function (server, Server_new_connection_callback, NULL, NULL);
+
+    Py_INCREF (mainloop);
+    self->mainloop = mainloop;
+    self->server = server;
+    self->new_connection_callback = NULL;
+    self->new_connection_type = 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 (!dbus_py_set_up_server((PyObject *)self, mainloop)) {
+        goto err;
+    }
+
+    Py_DECREF(mainloop);
+
+    DBG("%s() -> %p", __func__, self);
+    TRACE(self);
+    return 0;
+
+err:
+    DBG("Failed to construct Server.");
+    Py_XDECREF(mainloop);
+    Py_XDECREF(ref);
+    if (server) {
+        Py_BEGIN_ALLOW_THREADS
+        dbus_server_unref(server);
+        Py_END_ALLOW_THREADS
+    }
+    DBG("%s() fail", __func__);
+    DBG_WHEREAMI;
+    return -1;
+}
+
+
+/* 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;
+
+    DBG("Server at %p: deleting callbacks", self);
+
+    if (server) {
+        /* Might trigger callbacks if we're unlucky... */
+        DBG("Server at %p has a server, closing it...", self);
+        Py_BEGIN_ALLOW_THREADS
+        Py_END_ALLOW_THREADS
+    }
+
+    /* make sure to do this last to preserve the invariant that
+     * self->server is always non-NULL for any referenced Server
+     * (until the filters and object paths were freed, we might have been
+     * in a reference cycle!)
+     */
+    DBG("Server at %p: nulling self->server", self);
+    self->server = server;
+
+    if (server) {
+        DBG("Server at %p: unreffing server", self);
+        dbus_server_unref(server);
+    }
+
+    Py_XDECREF (self->mainloop);
+
+    DBG("Server at %p: freeing self", self);
+    PyErr_Restore(et, ev, etb);
+    (self->ob_type->tp_free)((PyObject *)self);
+}
+
+/* Server type object =========================================== */
+
+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*/
+    (initproc) Server_tp_init,
+                            /*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 Servers */
+    _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/dbus/lowlevel.py b/dbus/lowlevel.py
index eeabcbd..38f763c 100644
--- a/dbus/lowlevel.py
+++ b/dbus/lowlevel.py
@@ -24,9 +24,11 @@
 
 __all__ = ('PendingCall', 'Message', 'MethodCallMessage',
            'MethodReturnMessage', 'ErrorMessage', 'SignalMessage',
+           'Server',
            'HANDLER_RESULT_HANDLED', 'HANDLER_RESULT_NOT_YET_HANDLED')
 
 from _dbus_bindings import PendingCall, Message, MethodCallMessage, \
                            MethodReturnMessage, ErrorMessage, SignalMessage, \
+                           Server, \
                            HANDLER_RESULT_HANDLED, \
                            HANDLER_RESULT_NOT_YET_HANDLED
diff --git a/dbus/server.py b/dbus/server.py
new file mode 100644
index 0000000..d31567a
--- /dev/null
+++ b/dbus/server.py
@@ -0,0 +1,71 @@
+# Copyright (C) 2008 Huang Peng <phuang at redhat.com>
+#
+# 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.
+
+import _dbus_bindings
+import dbus.connection
+import dbus.lowlevel
+
+class Server (_dbus_bindings.Server):
+	def __init__ (self, address):
+		_dbus_bindings.Server.__init__ (self, address)
+		self._connections = []
+		self._objects = {}
+		self.set_new_connection_function (self._new_connection_cb, dbus.connection.Connection)
+
+	def register_object (self, obj, path):
+		if path in self._objects:
+			raise Exception ("%s has been registered", path)
+		self._objects[path] = obj
+
+		for conn in self._connections:
+			obj.add_to_connection (conn, path)
+
+	def _new_connection_cb (self, server, new_connection):
+		self._connections.append (new_connection)
+		new_connection.add_message_filter (self._message_filter_cb)
+
+		# add all objects to the new connection
+		for path, obj in self._objects.items ():
+			obj.add_to_connection (new_connection, path)
+
+		self.new_connection (server, new_connection)
+
+	def _message_filter_cb (self, connection, message):
+		if message.get_interface() == "org.freedesktop.DBus.Local" and \
+			message.get_member() == "Disconnected":
+			# remove all object from this connection
+			for path, obj in self._objects.items ():
+				obj.remove_from_connection (connection, path)
+
+			self.remove_connection (connection)
+			self._connections.remove (connection)
+			return dbus.lowlevel.HANDLER_RESULT_HANDLED
+
+		return dbus.lowlevel.HANDLER_RESULT_NOT_YET_HANDLED
+
+	def new_connection (self, server, connection):
+		pass
+
+	def remove_connection (self, server, connection):
+		pass
+
+
diff --git a/examples/Makefile.am b/examples/Makefile.am
index 316b4bb..866aa04 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -4,5 +4,8 @@ EXTRA_DIST = \
 	     example-service.py \
 	     example-signal-emitter.py \
 	     example-signal-recipient.py \
-	     list-system-services.py
+	     list-system-services.py \
+	     example-server.py \
+	     example-server-client.py
+
 # miss out the gconf examples for now - they don't work
diff --git a/examples/example-server-client.py b/examples/example-server-client.py
new file mode 100644
index 0000000..693fb46
--- /dev/null
+++ b/examples/example-server-client.py
@@ -0,0 +1,18 @@
+#!/usr/bin/env python
+import dbus
+import dbus.mainloop.glib
+import dbus.connection
+import gobject
+import time
+
+def main():
+	loop = gobject.MainLoop ()
+	conn = dbus.connection.Connection ("unix:abstract=/tmp/example-server")
+	obj = conn.get_object ("no.need.name", "/org/freedesktop/ExampleObject")
+	print obj.HelloWorld ("[%s] Hello World" % time.time())
+	time.sleep (1)
+	print obj.Exit ()
+
+if __name__ == "__main__":
+	dbus.mainloop.glib.DBusGMainLoop (set_as_default=True)
+	main ()
diff --git a/examples/example-server.py b/examples/example-server.py
new file mode 100644
index 0000000..1668426
--- /dev/null
+++ b/examples/example-server.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python
+import dbus
+import dbus.server
+import dbus.lowlevel
+import dbus.service
+import dbus.mainloop.glib
+import sys
+import gobject
+
+class ExampleObject (dbus.service.Object):
+	SUPPORTS_MULTIPLE_CONNECTIONS = True
+
+	@dbus.service.method (dbus_interface="org.freedesktop.ExampleObject", in_signature="s", out_signature="s")
+	def HelloWorld (self, message):
+		print message
+		return "Reply : %s" % message
+
+	@dbus.service.method (dbus_interface="org.freedesktop.ExampleObject", out_signature="s", async_callbacks=("reply_cb", "error_cb"))
+	def Exit (self, reply_cb, error_cb):
+		reply_cb ("Exit")
+		sys.exit ()
+
+
+class ExampleServer (dbus.server.Server):
+	def __init__ (self):
+		dbus.server.Server.__init__ (self, "unix:abstract=/tmp/example-server")
+		self.register_object (ExampleObject (), "/org/freedesktop/ExampleObject")
+
+	def new_connection (self, server, connection):
+		connection.add_message_filter (self.message_filter_cb)
+		print "New connection"
+
+	def remove_connection (self, connection):
+		print "Remove connection"
+
+	def message_filter_cb (self, connection, message):
+		if message.is_signal (dbus.LOCAL_IFACE, "Disconnected"):
+			print "A connection was closed"
+			return dbus.lowlevel.HANDLER_RESULT_NOT_YET_HANDLED
+
+		print "Got a Message (%s) : " % message.__class__.__name__
+		print "\t From:      %s" % message.get_sender ()
+		print "\t To:        %s" % message.get_destination ()
+		print "\t Interface: %s" % message.get_interface ()
+		print "\t Path:      %s" % message.get_path ()
+		print "\t Member:    %s" % message.get_member ()
+		print "\t Arguments:"
+		i = 0
+		for arg in message.get_args_list():
+			print "\t\t Arg[%d] : %s" % (i, arg)
+			i = i + 1
+
+		return dbus.lowlevel.HANDLER_RESULT_NOT_YET_HANDLED
+
+if __name__ == "__main__":
+	dbus.mainloop.glib.DBusGMainLoop (set_as_default = True)
+	loop = gobject.MainLoop ()
+	bus = ExampleServer ()
+	print "ADDRESS=\"%s\"" % bus.get_address ()
+	loop.run ()
+
diff --git a/include/dbus-python.h b/include/dbus-python.h
index ff83e71..b1dd7ff 100644
--- a/include/dbus-python.h
+++ b/include/dbus-python.h
@@ -46,6 +46,7 @@ typedef void (*_dbus_py_free_func)(void *);
 #ifdef INSIDE_DBUS_PYTHON_BINDINGS
 
 extern DBusConnection *DBusPyConnection_BorrowDBusConnection(PyObject *);
+extern DBusServer *DBusPyServer_BorrowDBusServer(PyObject *);
 extern PyObject *DBusPyNativeMainLoop_New4(_dbus_py_conn_setup_func,
                                            _dbus_py_srv_setup_func,
                                            _dbus_py_free_func,
-- 
1.5.5.1


--=-G8pkZTbyXA573HyrZ4Zc--



More information about the dbus mailing list