1 /* Implementation of the _dbus_bindings Connection type, a Python wrapper
2 * for DBusConnection. See also conn-methods-impl.h.
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 /* Connection definition ============================================ */
28 PyDoc_STRVAR(Connection_tp_doc
,
29 "A D-Bus connection.\n\n"
30 "Connection(address: str, mainloop=None) -> Connection\n"
33 typedef struct Connection
{
36 /* A list of filter callbacks. */
38 /* A dict mapping object paths to one of:
39 * - tuples (unregister_callback or None, message_callback)
40 * - None (meaning unregistration from libdbus is in progress and nobody
41 * should touch this entry til we're finished)
43 PyObject
*object_paths
;
48 static PyTypeObject ConnectionType
;
50 static inline int Connection_Check(PyObject
*o
)
52 return PyObject_TypeCheck(o
, &ConnectionType
);
55 /* Helpers ========================================================== */
57 static PyObject
*Connection_ExistingFromDBusConnection(DBusConnection
*);
58 static PyObject
*Connection_GetObjectPathHandlers(Connection
*, PyObject
*);
59 static DBusHandlerResult
Connection_HandleMessage(Connection
*, Message
*,
63 _object_path_unregister(DBusConnection
*conn
, void *user_data
)
65 PyGILState_STATE gil
= PyGILState_Ensure();
66 PyObject
*tuple
= NULL
;
67 Connection
*conn_obj
= NULL
;
70 conn_obj
= (Connection
*)Connection_ExistingFromDBusConnection(conn
);
71 if (!conn_obj
) goto out
;
73 DBG("Connection at %p unregistering object path %s",
74 conn_obj
, PyString_AS_STRING((PyObject
*)user_data
));
75 tuple
= Connection_GetObjectPathHandlers(conn_obj
, (PyObject
*)user_data
);
77 if (tuple
== Py_None
) goto out
;
79 DBG("%s", "... yes we have handlers for that object path");
81 /* 0'th item is the unregisterer (if that's a word) */
82 callable
= PyTuple_GetItem(tuple
, 0);
83 if (callable
&& callable
!= Py_None
) {
84 DBG("%s", "... and we even have an unregisterer");
85 /* any return from the unregisterer is ignored */
86 Py_XDECREF(PyObject_CallFunctionObjArgs(callable
, conn_obj
, NULL
));
91 /* the user_data (a Python str) is no longer ref'd by the DBusConnection */
92 Py_XDECREF((PyObject
*)user_data
);
93 if (PyErr_Occurred()) {
96 PyGILState_Release(gil
);
99 static DBusHandlerResult
100 _object_path_message(DBusConnection
*conn
, DBusMessage
*message
,
103 DBusHandlerResult ret
;
104 PyGILState_STATE gil
= PyGILState_Ensure();
105 Connection
*conn_obj
= NULL
;
106 PyObject
*tuple
= NULL
;
108 PyObject
*callable
; /* borrowed */
110 dbus_message_ref(message
);
111 msg_obj
= (Message
*)Message_ConsumeDBusMessage(message
);
113 ret
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
117 conn_obj
= (Connection
*)Connection_ExistingFromDBusConnection(conn
);
119 ret
= DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
123 DBG("Connection at %p messaging object path %s",
124 conn_obj
, PyString_AS_STRING((PyObject
*)user_data
));
125 DBG_DUMP_MESSAGE(message
);
126 tuple
= Connection_GetObjectPathHandlers(conn_obj
, (PyObject
*)user_data
);
127 if (!tuple
|| tuple
== Py_None
) {
128 ret
= DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
132 DBG("%s", "... yes we have handlers for that object path");
134 /* 1st item (0-based) is the message callback */
135 callable
= PyTuple_GetItem(tuple
, 1);
137 DBG("%s", "... error getting message handler from tuple");
138 ret
= DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
140 else if (callable
== Py_None
) {
141 /* there was actually no handler after all */
142 DBG("%s", "... but those handlers don't do messages");
143 ret
= DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
146 DBG("%s", "... and we have a message handler for that object path");
147 ret
= Connection_HandleMessage(conn_obj
, msg_obj
, callable
);
152 Py_XDECREF(conn_obj
);
154 if (PyErr_Occurred()) {
157 PyGILState_Release(gil
);
161 static const DBusObjectPathVTable _object_path_vtable
= {
162 _object_path_unregister
,
163 _object_path_message
,
166 static DBusHandlerResult
167 _filter_message(DBusConnection
*conn
, DBusMessage
*message
, void *user_data
)
169 DBusHandlerResult ret
;
170 PyGILState_STATE gil
= PyGILState_Ensure();
171 Connection
*conn_obj
= NULL
;
172 PyObject
*callable
= NULL
;
174 #ifndef DBUS_PYTHON_DISABLE_CHECKS
178 dbus_message_ref(message
);
179 msg_obj
= (Message
*)Message_ConsumeDBusMessage(message
);
181 DBG("%s", "OOM while trying to construct Message");
182 ret
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
186 conn_obj
= (Connection
*)Connection_ExistingFromDBusConnection(conn
);
188 DBG("%s", "failed to traverse DBusConnection -> Connection weakref");
189 ret
= DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
193 /* The user_data is a pointer to a Python object. To avoid
194 * cross-library reference cycles, the DBusConnection isn't allowed
195 * to reference it. However, as long as the Connection is still
196 * alive, its ->filters list owns a reference to the same Python
197 * object, so the object should also still be alive.
199 * To ensure that this works, be careful whenever manipulating the
200 * filters list! (always put things in the list *before* giving
201 * them to libdbus, etc.)
203 #ifdef DBUS_PYTHON_DISABLE_CHECKS
204 callable
= (PyObject
*)user_data
;
206 size
= PyList_GET_SIZE(conn_obj
->filters
);
207 for (i
= 0; i
< size
; i
++) {
208 callable
= PyList_GET_ITEM(conn_obj
->filters
, i
);
209 if (callable
== user_data
) {
218 DBG("... filter %p has vanished from ->filters, so not calling it",
220 ret
= DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
225 ret
= Connection_HandleMessage(conn_obj
, msg_obj
, callable
);
228 Py_XDECREF(conn_obj
);
229 Py_XDECREF(callable
);
230 PyGILState_Release(gil
);
234 /* D-Bus Connection user data slot, containing an owned reference to either
235 * the Connection, or a weakref to the Connection.
237 static dbus_int32_t _connection_python_slot
;
239 /* C API for main-loop hooks ======================================== */
241 /* Return a borrowed reference to the DBusConnection which underlies this
243 static DBusConnection
*
244 Connection_BorrowDBusConnection(PyObject
*self
)
248 if (!Connection_Check(self
)) {
249 PyErr_SetString(PyExc_TypeError
, "A dbus.Connection is required");
252 dbc
= ((Connection
*)self
)->conn
;
254 PyErr_SetString(PyExc_RuntimeError
, "Connection is in an invalid "
255 "state: no DBusConnection");
261 /* Internal C API =================================================== */
263 /* Pass a message through a handler. */
264 static DBusHandlerResult
265 Connection_HandleMessage(Connection
*conn
, Message
*msg
, PyObject
*callable
)
267 PyObject
*obj
= PyObject_CallFunctionObjArgs(callable
, conn
, msg
,
269 if (obj
== Py_None
) {
270 DBG("%p: OK, handler %p returned None", conn
, callable
);
272 return DBUS_HANDLER_RESULT_HANDLED
;
274 else if (obj
== Py_NotImplemented
) {
275 DBG("%p: handler %p returned NotImplemented, continuing",
278 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
281 if (PyErr_ExceptionMatches(PyExc_MemoryError
)) {
282 DBG_EXC("%p: handler %p caused OOM", conn
, callable
);
284 return DBUS_HANDLER_RESULT_NEED_MEMORY
;
286 DBG_EXC("%p: handler %p raised exception", conn
, callable
);
287 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
290 long i
= PyInt_AsLong(obj
);
291 DBG("%p: handler %p returned %ld", conn
, callable
, i
);
293 if (i
== -1 && PyErr_Occurred()) {
294 PyErr_SetString(PyExc_TypeError
, "Return from D-Bus message "
295 "handler callback should be None, "
296 "NotImplemented or integer");
297 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
299 else if (i
== DBUS_HANDLER_RESULT_HANDLED
||
300 i
== DBUS_HANDLER_RESULT_NOT_YET_HANDLED
||
301 i
== DBUS_HANDLER_RESULT_NEED_MEMORY
) {
305 PyErr_Format(PyExc_ValueError
, "Integer return from "
306 "D-Bus message handler callback should "
307 "be a DBUS_HANDLER_RESULT_... constant, "
309 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
314 /* On KeyError or if unregistration is in progress, return None. */
316 Connection_GetObjectPathHandlers(Connection
*self
, PyObject
*path
)
318 PyObject
*callbacks
= PyDict_GetItem(self
->object_paths
, path
);
320 if (PyErr_ExceptionMatches(PyExc_KeyError
)) {
325 Py_INCREF(callbacks
);
329 /* Return a new reference to a Python Connection or subclass corresponding
330 * to the DBusConnection conn. For use in callbacks.
332 * Raises AssertionError if the DBusConnection does not have a Connection.
335 Connection_ExistingFromDBusConnection(DBusConnection
*conn
)
337 PyObject
*self
, *ref
;
339 Py_BEGIN_ALLOW_THREADS
340 ref
= (PyObject
*)dbus_connection_get_data(conn
,
341 _connection_python_slot
);
344 self
= PyWeakref_GetObject(ref
); /* still a borrowed ref */
345 if (self
&& self
!= Py_None
&& Connection_Check(self
)) {
351 PyErr_SetString(PyExc_AssertionError
,
352 "D-Bus connection does not have a Connection "
353 "instance associated with it");
357 /* Return a new reference to a Python Connection or subclass (given by cls)
358 * corresponding to the DBusConnection conn, which must have been newly
359 * created. For use by the Connection and Bus constructors.
361 * Raises AssertionError if the DBusConnection already has a Connection.
364 Connection_NewConsumingDBusConnection(PyTypeObject
*cls
,
365 DBusConnection
*conn
,
368 Connection
*self
= NULL
;
372 Py_BEGIN_ALLOW_THREADS
373 ref
= (PyObject
*)dbus_connection_get_data(conn
,
374 _connection_python_slot
);
377 self
= (Connection
*)PyWeakref_GetObject(ref
);
379 if (self
&& (PyObject
*)self
!= Py_None
) {
381 PyErr_SetString(PyExc_AssertionError
,
382 "Newly created D-Bus connection already has a "
383 "Connection instance associated with it");
389 if (!mainloop
|| mainloop
== Py_None
) {
390 mainloop
= default_main_loop
;
391 if (!mainloop
|| mainloop
== Py_None
) {
392 PyErr_SetString(PyExc_ValueError
,
393 "D-Bus connections must be attached to a main "
394 "loop by passing mainloop=... to the constructor "
395 "or calling dbus.Bus.set_default_main_loop(...)");
399 /* Make sure there's a ref to the main loop (in case someone changes the
403 DBG("Constructing Connection from DBusConnection at %p", conn
);
405 self
= (Connection
*)(cls
->tp_alloc(cls
, 0));
409 self
->filters
= PyList_New(0);
410 if (!self
->filters
) goto err
;
411 self
->object_paths
= PyDict_New();
412 if (!self
->object_paths
) goto err
;
414 ref
= PyWeakref_NewRef((PyObject
*)self
, NULL
);
417 Py_BEGIN_ALLOW_THREADS
418 ok
= dbus_connection_set_data(conn
, _connection_python_slot
,
420 (DBusFreeFunction
)Glue_TakeGILAndXDecref
);
430 if (!dbus_python_set_up_connection((PyObject
*)self
, mainloop
)) {
436 return (PyObject
*)self
;
439 DBG("Failed to construct Connection from DBusConnection at %p", conn
);
440 Py_XDECREF(mainloop
);
444 Py_BEGIN_ALLOW_THREADS
445 dbus_connection_close(conn
);
446 dbus_connection_unref(conn
);
452 /* Connection type-methods ========================================== */
454 /* "Constructor" (the real constructor is Connection_NewFromDBusConnection,
455 * to which this delegates). */
457 Connection_tp_new(PyTypeObject
*cls
, PyObject
*args
, PyObject
*kwargs
)
459 DBusConnection
*conn
;
462 PyObject
*self
, *mainloop
= NULL
;
463 static char *argnames
[] = {"address", "mainloop", NULL
};
465 if (!PyArg_ParseTupleAndKeywords(args
, kwargs
, "s|O", argnames
,
466 &address
, &mainloop
)) {
470 dbus_error_init(&error
);
472 /* We always open a private connection (at the libdbus level). Sharing
473 * is done in Python, to keep things simple. */
474 Py_BEGIN_ALLOW_THREADS
475 conn
= dbus_connection_open_private(address
, &error
);
479 DBusException_ConsumeError(&error
);
482 self
= Connection_NewConsumingDBusConnection(cls
, conn
, mainloop
);
488 static void Connection_tp_dealloc(Connection
*self
)
490 DBusConnection
*conn
= self
->conn
;
491 PyObject
*filters
= self
->filters
;
492 PyObject
*object_paths
= self
->object_paths
;
494 DBG("Deallocating Connection at %p (DBusConnection at %p)", self
, conn
);
496 self
->filters
= NULL
;
498 self
->object_paths
= NULL
;
499 Py_XDECREF(object_paths
);
501 /* make sure to do this last to preserve the invariant that
502 * self->conn is always non-NULL for any referenced Connection
503 * (until the filters and object paths were freed, we might have been
504 * in a reference cycle!)
508 Py_BEGIN_ALLOW_THREADS
509 dbus_connection_close(conn
);
512 dbus_connection_unref(conn
);
515 (self
->ob_type
->tp_free
)((PyObject
*)self
);
518 /* Connection_tp_methods */
519 #include "conn-methods-impl.h"
521 /* Connection type object =========================================== */
523 static PyTypeObject ConnectionType
= {
524 PyObject_HEAD_INIT(NULL
)
526 "_dbus_bindings.Connection", /*tp_name*/
527 sizeof(Connection
), /*tp_basicsize*/
530 (destructor
)Connection_tp_dealloc
,
537 0, /*tp_as_sequence*/
545 Py_TPFLAGS_DEFAULT
| Py_TPFLAGS_HAVE_WEAKREFS
| Py_TPFLAGS_BASETYPE
,
546 Connection_tp_doc
, /*tp_doc*/
549 0, /*tp_richcompare*/
550 offsetof(Connection
, weaklist
), /*tp_weaklistoffset*/
553 Connection_tp_methods
, /*tp_methods*/
563 Connection_tp_new
, /*tp_new*/
568 static inline dbus_bool_t
569 init_conn_types(void)
571 default_main_loop
= NULL
;
573 /* Get a slot to store our weakref on DBus Connections */
574 _connection_python_slot
= -1;
575 if (!dbus_connection_allocate_data_slot(&_connection_python_slot
))
577 if (PyType_Ready(&ConnectionType
) < 0)
582 static inline dbus_bool_t
583 insert_conn_types(PyObject
*this_module
)
585 if (PyModule_AddObject(this_module
, "Connection",
586 (PyObject
*)&ConnectionType
) < 0) return FALSE
;
590 /* vim:set ft=c cino< sw=4 sts=4 et: */