_dbus_bindings: split out conn, conn-methods into separate translation units
[dbus-python-phuang.git] / _dbus_bindings / conn.c
blobed79096d7630b5b74371cd511db5c0efa05bcd31
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 #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\n"
33 "Connection(address: str, mainloop=None) -> Connection\n"
36 /* D-Bus Connection user data slot, containing an owned reference to either
37 * the Connection, or a weakref to the Connection.
39 static dbus_int32_t _connection_python_slot;
41 /* C API for main-loop hooks ======================================== */
43 /* Return a borrowed reference to the DBusConnection which underlies this
44 * Connection. */
45 DBusConnection *
46 DBusPyConnection_BorrowDBusConnection(PyObject *self)
48 DBusConnection *dbc;
50 if (!DBusPyConnection_Check(self)) {
51 PyErr_SetString(PyExc_TypeError, "A dbus.Connection is required");
52 return NULL;
54 dbc = ((Connection *)self)->conn;
55 if (!dbc) {
56 PyErr_SetString(PyExc_RuntimeError, "Connection is in an invalid "
57 "state: no DBusConnection");
58 return NULL;
60 return dbc;
63 /* Internal C API =================================================== */
65 /* Pass a message through a handler. */
66 DBusHandlerResult
67 DBusPyConnection_HandleMessage(Connection *conn,
68 PyObject *msg,
69 PyObject *callable)
71 PyObject *obj = PyObject_CallFunctionObjArgs(callable, conn, msg,
72 NULL);
73 if (obj == Py_None) {
74 DBG("%p: OK, handler %p returned None", conn, callable);
75 Py_DECREF(obj);
76 return DBUS_HANDLER_RESULT_HANDLED;
78 else if (obj == Py_NotImplemented) {
79 DBG("%p: handler %p returned NotImplemented, continuing",
80 conn, callable);
81 Py_DECREF(obj);
82 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
84 else if (!obj) {
85 if (PyErr_ExceptionMatches(PyExc_MemoryError)) {
86 DBG_EXC("%p: handler %p caused OOM", conn, callable);
87 PyErr_Clear();
88 return DBUS_HANDLER_RESULT_NEED_MEMORY;
90 DBG_EXC("%p: handler %p raised exception", conn, callable);
91 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
93 else {
94 long i = PyInt_AsLong(obj);
95 DBG("%p: handler %p returned %ld", conn, callable, i);
96 Py_DECREF(obj);
97 if (i == -1 && PyErr_Occurred()) {
98 PyErr_SetString(PyExc_TypeError, "Return from D-Bus message "
99 "handler callback should be None, "
100 "NotImplemented or integer");
101 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
103 else if (i == DBUS_HANDLER_RESULT_HANDLED ||
104 i == DBUS_HANDLER_RESULT_NOT_YET_HANDLED ||
105 i == DBUS_HANDLER_RESULT_NEED_MEMORY) {
106 return i;
108 else {
109 PyErr_Format(PyExc_ValueError, "Integer return from "
110 "D-Bus message handler callback should "
111 "be a DBUS_HANDLER_RESULT_... constant, "
112 "not %d", (int)i);
113 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
118 /* On KeyError or if unregistration is in progress, return None. */
119 PyObject *
120 DBusPyConnection_GetObjectPathHandlers(PyObject *self, PyObject *path)
122 PyObject *callbacks = PyDict_GetItem(((Connection *)self)->object_paths,
123 path);
124 if (!callbacks) {
125 if (PyErr_ExceptionMatches(PyExc_KeyError)) {
126 PyErr_Clear();
127 Py_RETURN_NONE;
130 Py_INCREF(callbacks);
131 return callbacks;
134 /* Return a new reference to a Python Connection or subclass corresponding
135 * to the DBusConnection conn. For use in callbacks.
137 * Raises AssertionError if the DBusConnection does not have a Connection.
139 PyObject *
140 DBusPyConnection_ExistingFromDBusConnection(DBusConnection *conn)
142 PyObject *self, *ref;
144 Py_BEGIN_ALLOW_THREADS
145 ref = (PyObject *)dbus_connection_get_data(conn,
146 _connection_python_slot);
147 Py_END_ALLOW_THREADS
148 if (ref) {
149 self = PyWeakref_GetObject(ref); /* still a borrowed ref */
150 if (self && self != Py_None && DBusPyConnection_Check(self)) {
151 Py_INCREF(self);
152 return self;
156 PyErr_SetString(PyExc_AssertionError,
157 "D-Bus connection does not have a Connection "
158 "instance associated with it");
159 return NULL;
162 /* Return a new reference to a Python Connection or subclass (given by cls)
163 * corresponding to the DBusConnection conn, which must have been newly
164 * created. For use by the Connection and Bus constructors.
166 * Raises AssertionError if the DBusConnection already has a Connection.
168 PyObject *
169 DBusPyConnection_NewConsumingDBusConnection(PyTypeObject *cls,
170 DBusConnection *conn,
171 PyObject *mainloop)
173 Connection *self = NULL;
174 PyObject *ref;
175 dbus_bool_t ok;
177 Py_BEGIN_ALLOW_THREADS
178 ref = (PyObject *)dbus_connection_get_data(conn,
179 _connection_python_slot);
180 Py_END_ALLOW_THREADS
181 if (ref) {
182 self = (Connection *)PyWeakref_GetObject(ref);
183 ref = NULL;
184 if (self && (PyObject *)self != Py_None) {
185 self = NULL;
186 PyErr_SetString(PyExc_AssertionError,
187 "Newly created D-Bus connection already has a "
188 "Connection instance associated with it");
189 return NULL;
192 ref = NULL;
194 if (!mainloop || mainloop == Py_None) {
195 mainloop = dbus_py_get_default_main_loop();
196 if (!mainloop || mainloop == Py_None) {
197 PyErr_SetString(PyExc_ValueError,
198 "D-Bus connections must be attached to a main "
199 "loop by passing mainloop=... to the constructor "
200 "or calling dbus.Bus.set_default_main_loop(...)");
201 goto err;
204 else {
205 Py_INCREF(mainloop);
208 DBG("Constructing Connection from DBusConnection at %p", conn);
210 self = (Connection *)(cls->tp_alloc(cls, 0));
211 if (!self) goto err;
213 self->conn = NULL;
214 self->filters = PyList_New(0);
215 if (!self->filters) goto err;
216 self->object_paths = PyDict_New();
217 if (!self->object_paths) goto err;
219 ref = PyWeakref_NewRef((PyObject *)self, NULL);
220 if (!ref) goto err;
222 Py_BEGIN_ALLOW_THREADS
223 ok = dbus_connection_set_data(conn, _connection_python_slot,
224 (void *)ref,
225 (DBusFreeFunction)dbus_py_take_gil_and_xdecref);
226 Py_END_ALLOW_THREADS
228 if (!ok) {
229 PyErr_NoMemory();
230 goto err;
233 self->conn = conn;
235 if (!dbus_py_set_up_connection((PyObject *)self, mainloop)) {
236 goto err;
239 Py_DECREF(mainloop);
241 return (PyObject *)self;
243 err:
244 DBG("Failed to construct Connection from DBusConnection at %p", conn);
245 Py_XDECREF(mainloop);
246 Py_XDECREF(self);
247 Py_XDECREF(ref);
248 if (conn) {
249 Py_BEGIN_ALLOW_THREADS
250 dbus_connection_close(conn);
251 dbus_connection_unref(conn);
252 Py_END_ALLOW_THREADS
254 return NULL;
257 /* Connection type-methods ========================================== */
259 /* "Constructor" (the real constructor is Connection_NewFromDBusConnection,
260 * to which this delegates). */
261 static PyObject *
262 Connection_tp_new(PyTypeObject *cls, PyObject *args, PyObject *kwargs)
264 DBusConnection *conn;
265 const char *address;
266 DBusError error;
267 PyObject *self, *mainloop = NULL;
268 static char *argnames[] = {"address", "mainloop", NULL};
270 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|O", argnames,
271 &address, &mainloop)) {
272 return NULL;
275 dbus_error_init(&error);
277 /* We always open a private connection (at the libdbus level). Sharing
278 * is done in Python, to keep things simple. */
279 Py_BEGIN_ALLOW_THREADS
280 conn = dbus_connection_open_private(address, &error);
281 Py_END_ALLOW_THREADS
283 if (!conn) {
284 DBusPyException_ConsumeError(&error);
285 return NULL;
287 self = DBusPyConnection_NewConsumingDBusConnection(cls, conn, mainloop);
289 return self;
292 /* Destructor */
293 static void Connection_tp_dealloc(Connection *self)
295 DBusConnection *conn = self->conn;
296 PyObject *filters = self->filters;
297 PyObject *object_paths = self->object_paths;
299 DBG("Deallocating Connection at %p (DBusConnection at %p)", self, conn);
301 self->filters = NULL;
302 Py_XDECREF(filters);
303 self->object_paths = NULL;
304 Py_XDECREF(object_paths);
306 /* make sure to do this last to preserve the invariant that
307 * self->conn is always non-NULL for any referenced Connection
308 * (until the filters and object paths were freed, we might have been
309 * in a reference cycle!)
311 self->conn = NULL;
312 if (conn) {
313 Py_BEGIN_ALLOW_THREADS
314 dbus_connection_close(conn);
315 Py_END_ALLOW_THREADS
317 dbus_connection_unref(conn);
320 (self->ob_type->tp_free)((PyObject *)self);
323 /* Connection type object =========================================== */
325 static PyTypeObject ConnectionType = {
326 PyObject_HEAD_INIT(NULL)
327 0, /*ob_size*/
328 "_dbus_bindings.Connection", /*tp_name*/
329 sizeof(Connection), /*tp_basicsize*/
330 0, /*tp_itemsize*/
331 /* methods */
332 (destructor)Connection_tp_dealloc,
333 0, /*tp_print*/
334 0, /*tp_getattr*/
335 0, /*tp_setattr*/
336 0, /*tp_compare*/
337 0, /*tp_repr*/
338 0, /*tp_as_number*/
339 0, /*tp_as_sequence*/
340 0, /*tp_as_mapping*/
341 0, /*tp_hash*/
342 0, /*tp_call*/
343 0, /*tp_str*/
344 0, /*tp_getattro*/
345 0, /*tp_setattro*/
346 0, /*tp_as_buffer*/
347 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_WEAKREFS | Py_TPFLAGS_BASETYPE,
348 Connection_tp_doc, /*tp_doc*/
349 0, /*tp_traverse*/
350 0, /*tp_clear*/
351 0, /*tp_richcompare*/
352 offsetof(Connection, weaklist), /*tp_weaklistoffset*/
353 0, /*tp_iter*/
354 0, /*tp_iternext*/
355 DBusPyConnection_tp_methods, /*tp_methods*/
356 0, /*tp_members*/
357 0, /*tp_getset*/
358 0, /*tp_base*/
359 0, /*tp_dict*/
360 0, /*tp_descr_get*/
361 0, /*tp_descr_set*/
362 0, /*tp_dictoffset*/
363 0, /*tp_init*/
364 0, /*tp_alloc*/
365 Connection_tp_new, /*tp_new*/
366 0, /*tp_free*/
367 0, /*tp_is_gc*/
370 static inline dbus_bool_t
371 init_conn_types(void)
373 /* Get a slot to store our weakref on DBus Connections */
374 _connection_python_slot = -1;
375 if (!dbus_connection_allocate_data_slot(&_connection_python_slot))
376 return FALSE;
377 if (PyType_Ready(&ConnectionType) < 0)
378 return FALSE;
379 return TRUE;
382 static inline dbus_bool_t
383 insert_conn_types(PyObject *this_module)
385 if (PyModule_AddObject(this_module, "Connection",
386 (PyObject *)&ConnectionType) < 0) return FALSE;
387 return TRUE;
390 /* vim:set ft=c cino< sw=4 sts=4 et: */