Mention relicensing in NEWS
[dbus-python-phuang.git] / _dbus_bindings / conn.c
blobddc570abed51a84c7bb0d1493935adb8f84a676e
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 * Permission is hereby granted, free of charge, to any person
7 * obtaining a copy of this software and associated documentation
8 * files (the "Software"), to deal in the Software without
9 * restriction, including without limitation the rights to use, copy,
10 * modify, merge, publish, distribute, sublicense, and/or sell copies
11 * of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
27 #include "dbus_bindings-internal.h"
28 #include "conn-internal.h"
30 /* Connection definition ============================================ */
32 PyDoc_STRVAR(Connection_tp_doc,
33 "A D-Bus connection.\n"
34 "\n"
35 "::\n"
36 "\n"
37 " Connection(address, mainloop=None) -> Connection\n"
40 /* D-Bus Connection user data slot, containing an owned reference to either
41 * the Connection, or a weakref to the Connection.
43 static dbus_int32_t _connection_python_slot;
45 /* C API for main-loop hooks ======================================== */
47 /* Return a borrowed reference to the DBusConnection which underlies this
48 * Connection. */
49 DBusConnection *
50 DBusPyConnection_BorrowDBusConnection(PyObject *self)
52 DBusConnection *dbc;
54 TRACE(self);
55 if (!DBusPyConnection_Check(self)) {
56 PyErr_SetString(PyExc_TypeError, "A dbus.Connection is required");
57 return NULL;
59 dbc = ((Connection *)self)->conn;
60 if (!dbc) {
61 PyErr_SetString(PyExc_RuntimeError, "Connection is in an invalid "
62 "state: no DBusConnection");
63 return NULL;
65 return dbc;
68 /* Internal C API =================================================== */
70 /* Pass a message through a handler. */
71 DBusHandlerResult
72 DBusPyConnection_HandleMessage(Connection *conn,
73 PyObject *msg,
74 PyObject *callable)
76 PyObject *obj;
78 TRACE(conn);
79 obj = PyObject_CallFunctionObjArgs(callable, conn, msg,
80 NULL);
81 if (obj == Py_None) {
82 DBG("%p: OK, handler %p returned None", conn, callable);
83 Py_DECREF(obj);
84 return DBUS_HANDLER_RESULT_HANDLED;
86 else if (obj == Py_NotImplemented) {
87 DBG("%p: handler %p returned NotImplemented, continuing",
88 conn, callable);
89 Py_DECREF(obj);
90 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
92 else if (!obj) {
93 if (PyErr_ExceptionMatches(PyExc_MemoryError)) {
94 DBG_EXC("%p: handler %p caused OOM", conn, callable);
95 PyErr_Clear();
96 return DBUS_HANDLER_RESULT_NEED_MEMORY;
98 DBG_EXC("%p: handler %p raised exception", conn, callable);
99 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
101 else {
102 long i = PyInt_AsLong(obj);
103 DBG("%p: handler %p returned %ld", conn, callable, i);
104 Py_DECREF(obj);
105 if (i == -1 && PyErr_Occurred()) {
106 PyErr_SetString(PyExc_TypeError, "Return from D-Bus message "
107 "handler callback should be None, "
108 "NotImplemented or integer");
109 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
111 else if (i == DBUS_HANDLER_RESULT_HANDLED ||
112 i == DBUS_HANDLER_RESULT_NOT_YET_HANDLED ||
113 i == DBUS_HANDLER_RESULT_NEED_MEMORY) {
114 return i;
116 else {
117 PyErr_Format(PyExc_ValueError, "Integer return from "
118 "D-Bus message handler callback should "
119 "be a DBUS_HANDLER_RESULT_... constant, "
120 "not %d", (int)i);
121 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
126 /* On KeyError or if unregistration is in progress, return None. */
127 PyObject *
128 DBusPyConnection_GetObjectPathHandlers(PyObject *self, PyObject *path)
130 PyObject *callbacks;
132 TRACE(self);
133 callbacks = PyDict_GetItem(((Connection *)self)->object_paths, path);
134 if (!callbacks) {
135 if (PyErr_ExceptionMatches(PyExc_KeyError)) {
136 PyErr_Clear();
137 Py_RETURN_NONE;
140 Py_INCREF(callbacks);
141 return callbacks;
144 /* Return a new reference to a Python Connection or subclass corresponding
145 * to the DBusConnection conn. For use in callbacks.
147 * Raises AssertionError if the DBusConnection does not have a Connection.
149 PyObject *
150 DBusPyConnection_ExistingFromDBusConnection(DBusConnection *conn)
152 PyObject *self, *ref;
154 Py_BEGIN_ALLOW_THREADS
155 ref = (PyObject *)dbus_connection_get_data(conn,
156 _connection_python_slot);
157 Py_END_ALLOW_THREADS
158 if (ref) {
159 DBG("(DBusConnection *)%p has weak reference at %p", conn, ref);
160 self = PyWeakref_GetObject(ref); /* still a borrowed ref */
161 if (self && self != Py_None && DBusPyConnection_Check(self)) {
162 DBG("(DBusConnection *)%p has weak reference at %p pointing to %p",
163 conn, ref, self);
164 TRACE(self);
165 Py_INCREF(self);
166 TRACE(self);
167 return self;
171 PyErr_SetString(PyExc_AssertionError,
172 "D-Bus connection does not have a Connection "
173 "instance associated with it");
174 return NULL;
177 /* Return a new reference to a Python Connection or subclass (given by cls)
178 * corresponding to the DBusConnection conn, which must have been newly
179 * created. For use by the Connection and Bus constructors.
181 * Raises AssertionError if the DBusConnection already has a Connection.
183 PyObject *
184 DBusPyConnection_NewConsumingDBusConnection(PyTypeObject *cls,
185 DBusConnection *conn,
186 PyObject *mainloop)
188 Connection *self = NULL;
189 PyObject *ref;
190 dbus_bool_t ok;
192 DBG("%s(cls=%p, conn=%p, mainloop=%p)", __func__, cls, conn, mainloop);
193 DBUS_PY_RAISE_VIA_NULL_IF_FAIL(conn);
195 Py_BEGIN_ALLOW_THREADS
196 ref = (PyObject *)dbus_connection_get_data(conn,
197 _connection_python_slot);
198 Py_END_ALLOW_THREADS
199 if (ref) {
200 self = (Connection *)PyWeakref_GetObject(ref);
201 ref = NULL;
202 if (self && (PyObject *)self != Py_None) {
203 self = NULL;
204 PyErr_SetString(PyExc_AssertionError,
205 "Newly created D-Bus connection already has a "
206 "Connection instance associated with it");
207 DBG("%s() fail - assertion failed, DBusPyConn has a DBusConn already", __func__);
208 DBG_WHEREAMI;
209 return NULL;
212 ref = NULL;
214 /* Change mainloop from a borrowed reference to an owned reference */
215 if (!mainloop || mainloop == Py_None) {
216 mainloop = dbus_py_get_default_main_loop();
217 if (!mainloop)
218 goto err;
220 else {
221 Py_INCREF(mainloop);
224 DBG("Constructing Connection from DBusConnection at %p", conn);
226 self = (Connection *)(cls->tp_alloc(cls, 0));
227 if (!self) goto err;
228 TRACE(self);
230 DBG_WHEREAMI;
232 self->has_mainloop = (mainloop != Py_None);
233 self->conn = NULL;
234 self->filters = PyList_New(0);
235 if (!self->filters) goto err;
236 self->object_paths = PyDict_New();
237 if (!self->object_paths) goto err;
239 ref = PyWeakref_NewRef((PyObject *)self, NULL);
240 if (!ref) goto err;
241 DBG("Created weak ref %p to (Connection *)%p for (DBusConnection *)%p",
242 ref, self, conn);
244 Py_BEGIN_ALLOW_THREADS
245 ok = dbus_connection_set_data(conn, _connection_python_slot,
246 (void *)ref,
247 (DBusFreeFunction)dbus_py_take_gil_and_xdecref);
248 Py_END_ALLOW_THREADS
250 if (ok) {
251 DBG("Attached weak ref %p ((Connection *)%p) to (DBusConnection *)%p",
252 ref, self, conn);
253 ref = NULL; /* don't DECREF it - the DBusConnection owns it now */
255 else {
256 DBG("Failed to attached weak ref %p ((Connection *)%p) to "
257 "(DBusConnection *)%p - will dispose of it", ref, self, conn);
258 PyErr_NoMemory();
259 goto err;
262 DBUS_PY_RAISE_VIA_GOTO_IF_FAIL(conn, err);
263 self->conn = conn;
264 /* the DBusPyConnection will close it now */
265 conn = NULL;
267 if (self->has_mainloop
268 && !dbus_py_set_up_connection((PyObject *)self, mainloop)) {
269 goto err;
272 Py_DECREF(mainloop);
274 DBG("%s() -> %p", __func__, self);
275 TRACE(self);
276 return (PyObject *)self;
278 err:
279 DBG("Failed to construct Connection from DBusConnection at %p", conn);
280 Py_XDECREF(mainloop);
281 Py_XDECREF(self);
282 Py_XDECREF(ref);
283 if (conn) {
284 Py_BEGIN_ALLOW_THREADS
285 dbus_connection_close(conn);
286 dbus_connection_unref(conn);
287 Py_END_ALLOW_THREADS
289 DBG("%s() fail", __func__);
290 DBG_WHEREAMI;
291 return NULL;
294 /* Connection type-methods ========================================== */
296 /* "Constructor" (the real constructor is Connection_NewFromDBusConnection,
297 * to which this delegates). */
298 static PyObject *
299 Connection_tp_new(PyTypeObject *cls, PyObject *args, PyObject *kwargs)
301 DBusConnection *conn;
302 const char *address;
303 DBusError error;
304 PyObject *self, *mainloop = NULL;
305 static char *argnames[] = {"address", "mainloop", NULL};
307 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|O", argnames,
308 &address, &mainloop)) {
309 return NULL;
312 dbus_error_init(&error);
314 /* We always open a private connection (at the libdbus level). Sharing
315 * is done in Python, to keep things simple. */
316 Py_BEGIN_ALLOW_THREADS
317 conn = dbus_connection_open_private(address, &error);
318 Py_END_ALLOW_THREADS
320 if (!conn) {
321 DBusPyException_ConsumeError(&error);
322 return NULL;
324 self = DBusPyConnection_NewConsumingDBusConnection(cls, conn, mainloop);
325 TRACE(self);
327 return self;
330 /* Destructor */
331 static void Connection_tp_dealloc(Connection *self)
333 DBusConnection *conn = self->conn;
334 PyObject *et, *ev, *etb;
335 PyObject *filters = self->filters;
336 PyObject *object_paths = self->object_paths;
338 /* avoid clobbering any pending exception */
339 PyErr_Fetch(&et, &ev, &etb);
341 if (self->weaklist) {
342 PyObject_ClearWeakRefs((PyObject *)self);
345 TRACE(self);
346 DBG("Deallocating Connection at %p (DBusConnection at %p)", self, conn);
347 DBG_WHEREAMI;
349 DBG("Connection at %p: deleting callbacks", self);
350 self->filters = NULL;
351 Py_XDECREF(filters);
352 self->object_paths = NULL;
353 Py_XDECREF(object_paths);
355 if (conn) {
356 /* Might trigger callbacks if we're unlucky... */
357 DBG("Connection at %p has a conn, closing it...", self);
358 Py_BEGIN_ALLOW_THREADS
359 dbus_connection_close(conn);
360 Py_END_ALLOW_THREADS
363 /* make sure to do this last to preserve the invariant that
364 * self->conn is always non-NULL for any referenced Connection
365 * (until the filters and object paths were freed, we might have been
366 * in a reference cycle!)
368 DBG("Connection at %p: nulling self->conn", self);
369 self->conn = NULL;
371 if (conn) {
372 DBG("Connection at %p: unreffing conn", self);
373 dbus_connection_unref(conn);
376 DBG("Connection at %p: freeing self", self);
377 PyErr_Restore(et, ev, etb);
378 (self->ob_type->tp_free)((PyObject *)self);
381 /* Connection type object =========================================== */
383 PyTypeObject DBusPyConnection_Type = {
384 PyObject_HEAD_INIT(NULL)
385 0, /*ob_size*/
386 "_dbus_bindings.Connection", /*tp_name*/
387 sizeof(Connection), /*tp_basicsize*/
388 0, /*tp_itemsize*/
389 /* methods */
390 (destructor)Connection_tp_dealloc,
391 0, /*tp_print*/
392 0, /*tp_getattr*/
393 0, /*tp_setattr*/
394 0, /*tp_compare*/
395 0, /*tp_repr*/
396 0, /*tp_as_number*/
397 0, /*tp_as_sequence*/
398 0, /*tp_as_mapping*/
399 0, /*tp_hash*/
400 0, /*tp_call*/
401 0, /*tp_str*/
402 0, /*tp_getattro*/
403 0, /*tp_setattro*/
404 0, /*tp_as_buffer*/
405 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_WEAKREFS | Py_TPFLAGS_BASETYPE,
406 Connection_tp_doc, /*tp_doc*/
407 0, /*tp_traverse*/
408 0, /*tp_clear*/
409 0, /*tp_richcompare*/
410 offsetof(Connection, weaklist), /*tp_weaklistoffset*/
411 0, /*tp_iter*/
412 0, /*tp_iternext*/
413 DBusPyConnection_tp_methods, /*tp_methods*/
414 0, /*tp_members*/
415 0, /*tp_getset*/
416 0, /*tp_base*/
417 0, /*tp_dict*/
418 0, /*tp_descr_get*/
419 0, /*tp_descr_set*/
420 0, /*tp_dictoffset*/
421 0, /*tp_init*/
422 0, /*tp_alloc*/
423 Connection_tp_new, /*tp_new*/
424 0, /*tp_free*/
425 0, /*tp_is_gc*/
428 dbus_bool_t
429 dbus_py_init_conn_types(void)
431 /* Get a slot to store our weakref on DBus Connections */
432 _connection_python_slot = -1;
433 if (!dbus_connection_allocate_data_slot(&_connection_python_slot))
434 return FALSE;
435 if (PyType_Ready(&DBusPyConnection_Type) < 0)
436 return FALSE;
437 return TRUE;
440 dbus_bool_t
441 dbus_py_insert_conn_types(PyObject *this_module)
443 if (PyModule_AddObject(this_module, "Connection",
444 (PyObject *)&DBusPyConnection_Type) < 0) return FALSE;
445 return TRUE;
448 /* vim:set ft=c cino< sw=4 sts=4 et: */