Remove from EXTRA_DIST files we'd already be distributing
[dbus-python-phuang.git] / _dbus_bindings / conn.c
blob4c623ced03d3f1322173babd612c4aa68ce67efa
1 /* Implementation of the _dbus_bindings Connection type, a Python wrapper
2  * for DBusConnection. See also conn-methods.c.
3  *
4  * Copyright (C) 2006 Collabora Ltd. <http://www.collabora.co.uk/>
5  *
6  * Licensed under the Academic Free License version 2.1
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  *
24  */
26 #include "dbus_bindings-internal.h"
27 #include "conn-internal.h"
29 /* Connection definition ============================================ */
31 PyDoc_STRVAR(Connection_tp_doc,
32 "A D-Bus connection.\n"
33 "\n"
34 "::\n"
35 "\n"
36 "   Connection(address, mainloop=None) -> Connection\n"
39 /* D-Bus Connection user data slot, containing an owned reference to either
40  * the Connection, or a weakref to the Connection.
41  */
42 static dbus_int32_t _connection_python_slot;
44 /* C API for main-loop hooks ======================================== */
46 /* Return a borrowed reference to the DBusConnection which underlies this
47  * Connection. */
48 DBusConnection *
49 DBusPyConnection_BorrowDBusConnection(PyObject *self)
51     DBusConnection *dbc;
53     TRACE(self);
54     if (!DBusPyConnection_Check(self)) {
55         PyErr_SetString(PyExc_TypeError, "A dbus.Connection is required");
56         return NULL;
57     }
58     dbc = ((Connection *)self)->conn;
59     if (!dbc) {
60         PyErr_SetString(PyExc_RuntimeError, "Connection is in an invalid "
61                         "state: no DBusConnection");
62         return NULL;
63     }
64     return dbc;
67 /* Internal C API =================================================== */
69 /* Pass a message through a handler. */
70 DBusHandlerResult
71 DBusPyConnection_HandleMessage(Connection *conn,
72                                PyObject *msg,
73                                PyObject *callable)
75     PyObject *obj;
77     TRACE(conn);
78     obj = PyObject_CallFunctionObjArgs(callable, conn, msg,
79                                                  NULL);
80     if (obj == Py_None) {
81         DBG("%p: OK, handler %p returned None", conn, callable);
82         Py_DECREF(obj);
83         return DBUS_HANDLER_RESULT_HANDLED;
84     }
85     else if (obj == Py_NotImplemented) {
86         DBG("%p: handler %p returned NotImplemented, continuing",
87             conn, callable);
88         Py_DECREF(obj);
89         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
90     }
91     else if (!obj) {
92         if (PyErr_ExceptionMatches(PyExc_MemoryError)) {
93             DBG_EXC("%p: handler %p caused OOM", conn, callable);
94             PyErr_Clear();
95             return DBUS_HANDLER_RESULT_NEED_MEMORY;
96         }
97         DBG_EXC("%p: handler %p raised exception", conn, callable);
98         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
99     }
100     else {
101         long i = PyInt_AsLong(obj);
102         DBG("%p: handler %p returned %ld", conn, callable, i);
103         Py_DECREF(obj);
104         if (i == -1 && PyErr_Occurred()) {
105             PyErr_SetString(PyExc_TypeError, "Return from D-Bus message "
106                             "handler callback should be None, "
107                             "NotImplemented or integer");
108             return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
109         }
110         else if (i == DBUS_HANDLER_RESULT_HANDLED ||
111             i == DBUS_HANDLER_RESULT_NOT_YET_HANDLED ||
112             i == DBUS_HANDLER_RESULT_NEED_MEMORY) {
113             return i;
114         }
115         else {
116             PyErr_Format(PyExc_ValueError, "Integer return from "
117                         "D-Bus message handler callback should "
118                         "be a DBUS_HANDLER_RESULT_... constant, "
119                         "not %d", (int)i);
120             return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
121         }
122     }
125 /* On KeyError or if unregistration is in progress, return None. */
126 PyObject *
127 DBusPyConnection_GetObjectPathHandlers(PyObject *self, PyObject *path)
129     PyObject *callbacks;
131     TRACE(self);
132     callbacks = PyDict_GetItem(((Connection *)self)->object_paths, path);
133     if (!callbacks) {
134         if (PyErr_ExceptionMatches(PyExc_KeyError)) {
135             PyErr_Clear();
136             Py_RETURN_NONE;
137         }
138     }
139     Py_INCREF(callbacks);
140     return callbacks;
143 /* Return a new reference to a Python Connection or subclass corresponding
144  * to the DBusConnection conn. For use in callbacks.
146  * Raises AssertionError if the DBusConnection does not have a Connection.
147  */
148 PyObject *
149 DBusPyConnection_ExistingFromDBusConnection(DBusConnection *conn)
151     PyObject *self, *ref;
153     Py_BEGIN_ALLOW_THREADS
154     ref = (PyObject *)dbus_connection_get_data(conn,
155                                                _connection_python_slot);
156     Py_END_ALLOW_THREADS
157     if (ref) {
158         DBG("(DBusConnection *)%p has weak reference at %p", conn, ref);
159         self = PyWeakref_GetObject(ref);   /* still a borrowed ref */
160         if (self && self != Py_None && DBusPyConnection_Check(self)) {
161             DBG("(DBusConnection *)%p has weak reference at %p pointing to %p",
162                 conn, ref, self);
163             TRACE(self);
164             Py_INCREF(self);
165             TRACE(self);
166             return self;
167         }
168     }
170     PyErr_SetString(PyExc_AssertionError,
171                     "D-Bus connection does not have a Connection "
172                     "instance associated with it");
173     return NULL;
176 /* Return a new reference to a Python Connection or subclass (given by cls)
177  * corresponding to the DBusConnection conn, which must have been newly
178  * created. For use by the Connection and Bus constructors.
180  * Raises AssertionError if the DBusConnection already has a Connection.
181  */
182 PyObject *
183 DBusPyConnection_NewConsumingDBusConnection(PyTypeObject *cls,
184                                             DBusConnection *conn,
185                                             PyObject *mainloop)
187     Connection *self = NULL;
188     PyObject *ref;
189     dbus_bool_t ok;
191     DBG("%s(cls=%p, conn=%p, mainloop=%p)", __func__, cls, conn, mainloop);
192     DBUS_PY_RAISE_VIA_NULL_IF_FAIL(conn);
194     Py_BEGIN_ALLOW_THREADS
195     ref = (PyObject *)dbus_connection_get_data(conn,
196                                                _connection_python_slot);
197     Py_END_ALLOW_THREADS
198     if (ref) {
199         self = (Connection *)PyWeakref_GetObject(ref);
200         ref = NULL;
201         if (self && (PyObject *)self != Py_None) {
202             self = NULL;
203             PyErr_SetString(PyExc_AssertionError,
204                             "Newly created D-Bus connection already has a "
205                             "Connection instance associated with it");
206             DBG("%s() fail - assertion failed, DBusPyConn has a DBusConn already", __func__);
207             DBG_WHEREAMI;
208             return NULL;
209         }
210     }
211     ref = NULL;
213     /* Change mainloop from a borrowed reference to an owned reference */
214     if (!mainloop || mainloop == Py_None) {
215         mainloop = dbus_py_get_default_main_loop();
216         if (!mainloop)
217             goto err;
218     }
219     else {
220         Py_INCREF(mainloop);
221     }
223     DBG("Constructing Connection from DBusConnection at %p", conn);
225     self = (Connection *)(cls->tp_alloc(cls, 0));
226     if (!self) goto err;
227     TRACE(self);
229     DBG_WHEREAMI;
231     self->has_mainloop = (mainloop != Py_None);
232     self->conn = NULL;
233     self->filters = PyList_New(0);
234     if (!self->filters) goto err;
235     self->object_paths = PyDict_New();
236     if (!self->object_paths) goto err;
238     ref = PyWeakref_NewRef((PyObject *)self, NULL);
239     if (!ref) goto err;
240     DBG("Created weak ref %p to (Connection *)%p for (DBusConnection *)%p",
241         ref, self, conn);
243     Py_BEGIN_ALLOW_THREADS
244     ok = dbus_connection_set_data(conn, _connection_python_slot,
245                                   (void *)ref,
246                                   (DBusFreeFunction)dbus_py_take_gil_and_xdecref);
247     Py_END_ALLOW_THREADS
249     if (ok) {
250         DBG("Attached weak ref %p ((Connection *)%p) to (DBusConnection *)%p",
251             ref, self, conn);
252         ref = NULL;     /* don't DECREF it - the DBusConnection owns it now */
253     }
254     else {
255         DBG("Failed to attached weak ref %p ((Connection *)%p) to "
256             "(DBusConnection *)%p - will dispose of it", ref, self, conn);
257         PyErr_NoMemory();
258         goto err;
259     }
261     DBUS_PY_RAISE_VIA_GOTO_IF_FAIL(conn, err);
262     self->conn = conn;
263     /* the DBusPyConnection will close it now */
264     conn = NULL;
266     if (self->has_mainloop
267         && !dbus_py_set_up_connection((PyObject *)self, mainloop)) {
268         goto err;
269     }
271     Py_DECREF(mainloop);
273     DBG("%s() -> %p", __func__, self);
274     TRACE(self);
275     return (PyObject *)self;
277 err:
278     DBG("Failed to construct Connection from DBusConnection at %p", conn);
279     Py_XDECREF(mainloop);
280     Py_XDECREF(self);
281     Py_XDECREF(ref);
282     if (conn) {
283         Py_BEGIN_ALLOW_THREADS
284         dbus_connection_close(conn);
285         dbus_connection_unref(conn);
286         Py_END_ALLOW_THREADS
287     }
288     DBG("%s() fail", __func__);
289     DBG_WHEREAMI;
290     return NULL;
293 /* Connection type-methods ========================================== */
295 /* "Constructor" (the real constructor is Connection_NewFromDBusConnection,
296  * to which this delegates). */
297 static PyObject *
298 Connection_tp_new(PyTypeObject *cls, PyObject *args, PyObject *kwargs)
300     DBusConnection *conn;
301     const char *address;
302     DBusError error;
303     PyObject *self, *mainloop = NULL;
304     static char *argnames[] = {"address", "mainloop", NULL};
306     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|O", argnames,
307                                      &address, &mainloop)) {
308         return NULL;
309     }
311     dbus_error_init(&error);
313     /* We always open a private connection (at the libdbus level). Sharing
314      * is done in Python, to keep things simple. */
315     Py_BEGIN_ALLOW_THREADS
316     conn = dbus_connection_open_private(address, &error);
317     Py_END_ALLOW_THREADS
319     if (!conn) {
320         DBusPyException_ConsumeError(&error);
321         return NULL;
322     }
323     self = DBusPyConnection_NewConsumingDBusConnection(cls, conn, mainloop);
324     TRACE(self);
326     return self;
329 /* Destructor */
330 static void Connection_tp_dealloc(Connection *self)
332     DBusConnection *conn = self->conn;
333     PyObject *filters = self->filters;
334     PyObject *object_paths = self->object_paths;
336     if (self->weaklist) {
337         PyObject_ClearWeakRefs((PyObject *)self);
338     }
340     TRACE(self);
341     DBG("Deallocating Connection at %p (DBusConnection at %p)", self, conn);
342     DBG_WHEREAMI;
344     DBG("Connection at %p: deleting callbacks", self);
345     self->filters = NULL;
346     Py_XDECREF(filters);
347     self->object_paths = NULL;
348     Py_XDECREF(object_paths);
350     if (conn) {
351         /* Might trigger callbacks if we're unlucky... */
352         DBG("Connection at %p has a conn, closing it...", self);
353         Py_BEGIN_ALLOW_THREADS
354         dbus_connection_close(conn);
355         Py_END_ALLOW_THREADS
356     }
358     /* make sure to do this last to preserve the invariant that 
359      * self->conn is always non-NULL for any referenced Connection
360      * (until the filters and object paths were freed, we might have been
361      * in a reference cycle!)
362      */
363     DBG("Connection at %p: nulling self->conn", self);
364     self->conn = NULL;
366     if (conn) {
367         DBG("Connection at %p: unreffing conn", self);
368         dbus_connection_unref(conn);
369     }
371     DBG("Connection at %p: freeing self", self);
372     (self->ob_type->tp_free)((PyObject *)self);
375 /* Connection type object =========================================== */
377 PyTypeObject DBusPyConnection_Type = {
378     PyObject_HEAD_INIT(NULL)
379     0,                      /*ob_size*/
380     "_dbus_bindings.Connection", /*tp_name*/
381     sizeof(Connection),     /*tp_basicsize*/
382     0,                      /*tp_itemsize*/
383     /* methods */
384     (destructor)Connection_tp_dealloc,
385     0,                      /*tp_print*/
386     0,                      /*tp_getattr*/
387     0,                      /*tp_setattr*/
388     0,                      /*tp_compare*/
389     0,                      /*tp_repr*/
390     0,                      /*tp_as_number*/
391     0,                      /*tp_as_sequence*/
392     0,                      /*tp_as_mapping*/
393     0,                      /*tp_hash*/
394     0,                      /*tp_call*/
395     0,                      /*tp_str*/
396     0,                      /*tp_getattro*/
397     0,                      /*tp_setattro*/
398     0,                      /*tp_as_buffer*/
399     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_WEAKREFS | Py_TPFLAGS_BASETYPE,
400     Connection_tp_doc,      /*tp_doc*/
401     0,                      /*tp_traverse*/
402     0,                      /*tp_clear*/
403     0,                      /*tp_richcompare*/
404     offsetof(Connection, weaklist),   /*tp_weaklistoffset*/
405     0,                      /*tp_iter*/
406     0,                      /*tp_iternext*/
407     DBusPyConnection_tp_methods,  /*tp_methods*/
408     0,                      /*tp_members*/
409     0,                      /*tp_getset*/
410     0,                      /*tp_base*/
411     0,                      /*tp_dict*/
412     0,                      /*tp_descr_get*/
413     0,                      /*tp_descr_set*/
414     0,                      /*tp_dictoffset*/
415     0,                      /*tp_init*/
416     0,                      /*tp_alloc*/
417     Connection_tp_new,      /*tp_new*/
418     0,                      /*tp_free*/
419     0,                      /*tp_is_gc*/
422 dbus_bool_t
423 dbus_py_init_conn_types(void)
425     /* Get a slot to store our weakref on DBus Connections */
426     _connection_python_slot = -1;
427     if (!dbus_connection_allocate_data_slot(&_connection_python_slot))
428         return FALSE;
429     if (PyType_Ready(&DBusPyConnection_Type) < 0)
430         return FALSE;
431     return TRUE;
434 dbus_bool_t
435 dbus_py_insert_conn_types(PyObject *this_module)
437     if (PyModule_AddObject(this_module, "Connection",
438                            (PyObject *)&DBusPyConnection_Type) < 0) return FALSE;
439     return TRUE;
442 /* vim:set ft=c cino< sw=4 sts=4 et: */