[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