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.
4 * Copyright (C) 2006 Collabora Ltd. <http://www.collabora.co.uk/>
6 * Licensed under the Academic Free License version 2.1
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.
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.
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
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.
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;
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;
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;
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;
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;
97 DBG_EXC("%p: handler %p raised exception", conn, callable);
98 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
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;
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;
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;
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;
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.
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;
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.
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;
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;
219 else {
220 Py_INCREF(mainloop);
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 */
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;
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;
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
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;
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;
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);
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
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!)
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);
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: */