Add a test case for the various options to get_args_list.
[dbus-python-phuang.git] / _dbus_bindings / conn-methods-impl.h
blobf5b212593b6792e0b7e562445eaa7f05d742eb97
1 /* Implementation of normal Python-accessible methods on the _dbus_bindings
2 * Connection type; separated out to keep the file size manageable.
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 PyDoc_STRVAR(Connection_close__doc__,
27 "close()\n\n"
28 "Close the connection.");
29 static PyObject *
30 Connection_close (Connection *self, PyObject *args)
32 if (!PyArg_ParseTuple(args, ":close")) return NULL;
33 /* Because the user explicitly asked to close the connection, we'll even
34 let them close shared connections. */
35 if (self->conn) {
36 Py_BEGIN_ALLOW_THREADS
37 dbus_connection_close (self->conn);
38 Py_END_ALLOW_THREADS
40 Py_RETURN_NONE;
43 PyDoc_STRVAR(Connection_get_is_connected__doc__,
44 "get_is_connected() -> bool\n\n"
45 "Return true if this Connection is connected.\n");
46 static PyObject *
47 Connection_get_is_connected (Connection *self, PyObject *args)
49 dbus_bool_t ret;
50 if (!PyArg_ParseTuple(args, ":get_is_connected")) return NULL;
51 Py_BEGIN_ALLOW_THREADS
52 ret = dbus_connection_get_is_connected (self->conn);
53 Py_END_ALLOW_THREADS
54 return PyBool_FromLong (ret);
57 PyDoc_STRVAR(Connection_get_is_authenticated__doc__,
58 "get_is_authenticated() -> bool\n\n"
59 "Return true if this Connection was ever authenticated.\n");
60 static PyObject *
61 Connection_get_is_authenticated (Connection *self, PyObject *args)
63 dbus_bool_t ret;
64 if (!PyArg_ParseTuple(args, ":get_is_authenticated")) return NULL;
65 Py_BEGIN_ALLOW_THREADS
66 ret = dbus_connection_get_is_authenticated (self->conn);
67 Py_END_ALLOW_THREADS
68 return PyBool_FromLong (ret);
71 PyDoc_STRVAR(Connection_set_exit_on_disconnect__doc__,
72 "set_exit_on_disconnect(bool)\n\n"
73 "Set whether the C function ``_exit`` will be called when this Connection\n"
74 "becomes disconnected. This will cause the program to exit without calling\n"
75 "any cleanup code or exit handlers.\n"
76 "\n"
77 "The default is for this feature to be disabled for Connections and enabled\n"
78 "for Buses.\n");
79 static PyObject *
80 Connection_set_exit_on_disconnect (Connection *self, PyObject *args)
82 int exit_on_disconnect;
83 if (!PyArg_ParseTuple(args, "i:set_exit_on_disconnect",
84 &exit_on_disconnect)) {
85 return NULL;
87 Py_BEGIN_ALLOW_THREADS
88 dbus_connection_set_exit_on_disconnect (self->conn,
89 exit_on_disconnect ? 1 : 0);
90 Py_END_ALLOW_THREADS
91 Py_RETURN_NONE;
94 PyDoc_STRVAR(Connection__send__doc__,
95 "_send(msg: Message) -> long\n\n"
96 "Queue the given message for sending, and return the message serial number.\n"
98 static PyObject *
99 Connection__send (Connection *self, PyObject *args)
101 dbus_bool_t ok;
102 PyObject *obj;
103 DBusMessage *msg;
104 dbus_uint32_t serial;
106 if (!PyArg_ParseTuple(args, "O", &obj)) return NULL;
108 msg = Message_BorrowDBusMessage(obj);
109 if (!msg) return NULL;
111 Py_BEGIN_ALLOW_THREADS
112 ok = dbus_connection_send(self->conn, msg, &serial);
113 Py_END_ALLOW_THREADS
115 if (!ok) {
116 return PyErr_NoMemory();
119 return PyLong_FromUnsignedLong(serial);
122 /* The timeout is in seconds here, since that's conventional in Python. */
123 PyDoc_STRVAR(Connection__send_with_reply__doc__,
124 "_send_with_reply(msg: Message, reply_handler: callable[, timeout_s: float])"
125 " -> PendingCall\n\n"
126 "Queue the message for sending; expect a reply via the returned PendingCall.\n"
127 "\n"
128 ":Parameters:\n"
129 " `msg` : Message\n"
130 " The message to be sent\n"
131 " `reply_handler` : callable\n"
132 " Asynchronous reply handler: will be called with one positional\n"
133 " parameter, a Message instance representing the reply.\n"
134 " `timeout_s` : float\n"
135 " If the reply takes more than this many seconds, a timeout error\n"
136 " will be created locally and raised instead. If this timeout is\n"
137 " negative (default), a sane default (supplied by libdbus) is used.\n"
138 ":Returns\n"
139 " A `PendingCall` instance which can be used to cancel the pending call.\n"
140 "\n"
142 static PyObject *
143 Connection__send_with_reply(Connection *self, PyObject *args)
145 dbus_bool_t ok;
146 double timeout_s = -1.0;
147 int timeout_ms;
148 PyObject *obj, *callable;
149 DBusMessage *msg;
150 DBusPendingCall *pending;
152 if (!PyArg_ParseTuple(args, "OO|f:send_with_reply", &obj, &callable,
153 &timeout_s)) {
154 return NULL;
157 msg = Message_BorrowDBusMessage(obj);
158 if (!msg) return NULL;
160 if (timeout_s < 0) {
161 timeout_ms = -1;
163 else {
164 if (timeout_s > ((double)INT_MAX) / 1000.0) {
165 PyErr_SetString(PyExc_ValueError, "Timeout too long");
166 return NULL;
168 timeout_ms = (int)(timeout_s * 1000.0);
171 Py_BEGIN_ALLOW_THREADS
172 ok = dbus_connection_send_with_reply(self->conn, msg, &pending,
173 timeout_ms);
174 Py_END_ALLOW_THREADS
176 if (!ok) {
177 return PyErr_NoMemory();
180 return PendingCall_ConsumeDBusPendingCall(pending, callable);
183 /* Again, the timeout is in seconds, since that's conventional in Python. */
184 PyDoc_STRVAR(Connection__send_with_reply_and_block__doc__,
185 "_send_with_reply_and_block(msg: Message, [, timeout_s: float])"
186 " -> Message\n\n"
187 "Send the message and block while waiting for a reply.\n"
188 "\n"
189 "This does not re-enter the main loop, so it can lead to a deadlock, if\n"
190 "the called method tries to make a synchronous call to a method in this\n"
191 "application. As such, it's probably a bad idea.\n"
192 "\n"
193 ":Parameters:\n"
194 " `msg` : Message\n"
195 " The message to be sent\n"
196 " `timeout_s` : float\n"
197 " If the reply takes more than this many seconds, a timeout error\n"
198 " will be created locally and raised instead. If this timeout is\n"
199 " negative (default), a sane default (supplied by libdbus) is used.\n"
200 ":Returns\n"
201 " A `Message` instance (probably a `MethodReturnMessage`) on success\n"
202 ":Raises\n"
203 " A `DBusException` on error (including if the reply arrives but is an\n"
204 " error message)\n"
205 "\n"
207 static PyObject *
208 Connection__send_with_reply_and_block(Connection *self, PyObject *args)
210 double timeout_s = -1.0;
211 int timeout_ms;
212 PyObject *obj;
213 DBusMessage *msg, *reply;
214 DBusError error;
216 if (!PyArg_ParseTuple(args, "O|f:_send_with_reply_and_block", &obj,
217 &timeout_s)) {
218 return NULL;
221 msg = Message_BorrowDBusMessage(obj);
222 if (!msg) return NULL;
224 if (timeout_s < 0) {
225 timeout_ms = -1;
227 else {
228 if (timeout_s > ((double)INT_MAX) / 1000.0) {
229 PyErr_SetString(PyExc_ValueError, "Timeout too long");
230 return NULL;
232 timeout_ms = (int)(timeout_s * 1000.0);
235 dbus_error_init(&error);
236 Py_BEGIN_ALLOW_THREADS
237 reply = dbus_connection_send_with_reply_and_block(self->conn, msg,
238 timeout_ms, &error);
239 Py_END_ALLOW_THREADS
241 if (!reply) {
242 return DBusException_ConsumeError(&error);
244 return Message_ConsumeDBusMessage(reply);
247 PyDoc_STRVAR(Connection_flush__doc__,
248 "flush()\n\n"
249 "Block until the outgoing message queue is empty.\n");
250 static PyObject *
251 Connection_flush (Connection *self, PyObject *args)
253 Py_BEGIN_ALLOW_THREADS
254 dbus_connection_flush (self->conn);
255 Py_END_ALLOW_THREADS
256 Py_RETURN_NONE;
259 /* Unsupported:
260 * dbus_connection_preallocate_send
261 * dbus_connection_free_preallocated_send
262 * dbus_connection_send_preallocated
263 * dbus_connection_borrow_message
264 * dbus_connection_return_message
265 * dbus_connection_steal_borrowed_message
266 * dbus_connection_pop_message
269 /* Non-main-loop handling not yet implemented: */
270 /* dbus_connection_read_write_dispatch */
271 /* dbus_connection_read_write */
273 /* Main loop handling not yet implemented: */
274 /* dbus_connection_get_dispatch_status */
275 /* dbus_connection_dispatch */
276 /* dbus_connection_set_watch_functions */
277 /* dbus_connection_set_timeout_functions */
278 /* dbus_connection_set_wakeup_main_function */
279 /* dbus_connection_set_dispatch_status_function */
281 /* Normally in Python this would be called fileno(), but I don't want to
282 * encourage people to select() on it */
283 PyDoc_STRVAR(Connection_get_unix_fd__doc__,
284 "get_unix_fd() -> int or None\n\n"
285 "Get the connection's UNIX file descriptor, if any.\n\n"
286 "This can be used for SELinux access control checks with ``getpeercon()``\n"
287 "for example. **Do not** read or write to the file descriptor, or try to\n"
288 "``select()`` on it.\n");
289 static PyObject *
290 Connection_get_unix_fd (Connection *self, PyObject *unused)
292 int fd;
293 dbus_bool_t ok;
295 Py_BEGIN_ALLOW_THREADS
296 ok = dbus_connection_get_unix_fd (self->conn, &fd);
297 Py_END_ALLOW_THREADS
298 if (!ok) Py_RETURN_NONE;
299 return PyInt_FromLong(fd);
302 PyDoc_STRVAR(Connection_get_peer_unix_user__doc__,
303 "get_peer_unix_user() -> long or None\n\n"
304 "Get the UNIX user ID at the other end of the connection, if it has been\n"
305 "authenticated. Return None if this is a non-UNIX platform or the\n"
306 "connection has not been authenticated.\n");
307 static PyObject *
308 Connection_get_peer_unix_user (Connection *self, PyObject *unused)
310 unsigned long uid;
311 dbus_bool_t ok;
313 Py_BEGIN_ALLOW_THREADS
314 ok = dbus_connection_get_unix_user (self->conn, &uid);
315 Py_END_ALLOW_THREADS
316 if (!ok) Py_RETURN_NONE;
317 return PyLong_FromUnsignedLong (uid);
320 PyDoc_STRVAR(Connection_get_peer_unix_process_id__doc__,
321 "get_peer_unix_process_id() -> long or None\n\n"
322 "Get the UNIX process ID at the other end of the connection, if it has been\n"
323 "authenticated. Return None if this is a non-UNIX platform or the\n"
324 "connection has not been authenticated.\n");
325 static PyObject *
326 Connection_get_peer_unix_process_id (Connection *self, PyObject *unused)
328 unsigned long pid;
329 dbus_bool_t ok;
331 Py_BEGIN_ALLOW_THREADS
332 ok = dbus_connection_get_unix_process_id (self->conn, &pid);
333 Py_END_ALLOW_THREADS
334 if (!ok) Py_RETURN_NONE;
335 return PyLong_FromUnsignedLong (pid);
338 /* TODO: wrap dbus_connection_set_unix_user_function Pythonically */
340 PyDoc_STRVAR(Connection__add_filter__doc__,
341 "_add_filter(callable)\n\n"
342 "Add the given message filter to the internal list.\n\n"
343 "Filters are handlers that are run on all incoming messages, prior to the\n"
344 "objects registered with `_register_object_path`.\n"
345 "Filters are run in the order that they were added. The same handler can\n"
346 "be added as a filter more than once, in which case it will be run more\n"
347 "than once. Filters added during a filter callback won't be run on the\n"
348 "message being processed.\n"
350 static PyObject *
351 Connection__add_filter(Connection *self, PyObject *callable)
353 dbus_bool_t ok;
355 /* The callable must be referenced by ->filters *before* it is
356 * given to libdbus, which does not own a reference to it.
358 if (PyList_Append(self->filters, callable) < 0) {
359 return NULL;
362 Py_BEGIN_ALLOW_THREADS
363 ok = dbus_connection_add_filter(self->conn, _filter_message, callable,
364 NULL);
365 Py_END_ALLOW_THREADS
367 if (!ok) {
368 Py_XDECREF(PyObject_CallMethod(self->filters, "remove", "(O)",
369 callable));
370 PyErr_NoMemory();
371 return NULL;
373 Py_RETURN_NONE;
376 PyDoc_STRVAR(Connection__remove_filter__doc__,
377 "_remove_filter(callable)\n\n"
378 "Remove the given message filter (see `_add_filter` for details).\n");
379 static PyObject *
380 Connection__remove_filter(Connection *self, PyObject *callable)
382 PyObject *obj;
384 /* It's safe to do this before removing it from libdbus, because
385 * the presence of callable in our arguments means we have a ref
386 * to it. */
387 obj = PyObject_CallMethod(self->filters, "remove", "(O)", callable);
388 if (!obj) return NULL;
389 Py_DECREF(obj);
391 Py_BEGIN_ALLOW_THREADS
392 dbus_connection_remove_filter(self->conn, _filter_message, callable);
393 Py_END_ALLOW_THREADS
395 Py_RETURN_NONE;
398 /* object exports not yet implemented: */
400 PyDoc_STRVAR(Connection__register_object_path__doc__,
401 "_register_object_path(path: str, on_message: callable[, on_unregister: "
402 "callable][, **kwargs])\n\n"
403 "Keyword arguments accepted:\n"
404 "fallback: bool (default False): if True, when a message arrives for a\n"
405 "'subdirectory' of the given path and there is no more specific handler,\n"
406 "use this handler. Normally this handler is only run if the paths match\n"
407 "exactly.\n"
409 static PyObject *
410 Connection__register_object_path(Connection *self, PyObject *args,
411 PyObject *kwargs)
413 dbus_bool_t ok;
414 int fallback = 0;
415 PyObject *callbacks, *path, *tuple, *on_message, *on_unregister = Py_None;
416 static char *argnames[] = {"path", "on_message", "on_unregister",
417 "fallback", NULL};
419 if (!PyArg_ParseTupleAndKeywords(args, kwargs,
420 "OO|Oi:_register_object_path",
421 argnames,
422 &path,
423 &on_message, &on_unregister,
424 &fallback)) return NULL;
426 /* Take a reference to path, which we give away to libdbus in a moment.
428 Also, path needs to be a string (not a subclass which could do something
429 mad) to preserve the desirable property that the DBusConnection can
430 never strongly reference the Connection, even indirectly.
431 We make an exception for ObjectPaths because they're equally simple,
432 are known to have the same __eq__ and __hash__, and are what calling code
433 ought to be using. */
434 if (PyString_CheckExact(path) || path->ob_type == &ObjectPathType) {
435 Py_INCREF(path);
437 else if (PyUnicode_Check(path)) {
438 path = PyUnicode_AsUTF8String(path);
439 if (!path) return NULL;
441 else if (PyString_Check(path)) {
442 path = PyString_FromString(PyString_AS_STRING(path));
443 if (!path) return NULL;
446 if (!_validate_object_path(PyString_AS_STRING(path))) {
447 Py_DECREF(path);
448 return NULL;
451 tuple = Py_BuildValue("(OO)", on_unregister, on_message);
452 if (!tuple) {
453 Py_DECREF(path);
454 return NULL;
457 /* Guard against registering a handler that already exists. */
458 callbacks = PyDict_GetItem(self->object_paths, path);
459 if (callbacks && callbacks != Py_None) {
460 PyErr_Format(PyExc_KeyError, "Can't register the object-path "
461 "handler for '%s': there is already a handler",
462 PyString_AS_STRING(path));
463 Py_DECREF(tuple);
464 Py_DECREF(path);
465 return NULL;
468 /* Pre-allocate a slot in the dictionary, so we know we'll be able
469 * to replace it with the callbacks without OOM.
470 * This ensures we can keep libdbus' opinion of whether those
471 * paths are handled in sync with our own. */
472 if (PyDict_SetItem(self->object_paths, path, Py_None) < 0) {
473 Py_DECREF(tuple);
474 Py_DECREF(path);
475 return NULL;
478 Py_BEGIN_ALLOW_THREADS
479 if (fallback) {
480 ok = dbus_connection_register_fallback(self->conn,
481 PyString_AS_STRING(path),
482 &_object_path_vtable,
483 path);
485 else {
486 ok = dbus_connection_register_object_path(self->conn,
487 PyString_AS_STRING(path),
488 &_object_path_vtable,
489 path);
491 Py_END_ALLOW_THREADS
493 if (ok) {
494 if (PyDict_SetItem(self->object_paths, path, tuple) < 0) {
495 /* That shouldn't have happened, we already allocated enough
496 memory for it. Oh well, try to undo the registration to keep
497 things in sync. If this fails too, we've leaked a bit of
498 memory in libdbus, but tbh we should never get here anyway. */
499 Py_BEGIN_ALLOW_THREADS
500 ok = dbus_connection_unregister_object_path(self->conn,
501 PyString_AS_STRING(path));
502 Py_END_ALLOW_THREADS
503 return NULL;
505 /* don't DECREF path: libdbus owns a ref now */
506 Py_DECREF(tuple);
507 Py_RETURN_NONE;
509 else {
510 /* Oops, OOM. Tidy up, if we can, ignoring any error. */
511 PyDict_DelItem(self->object_paths, path);
512 PyErr_Clear();
513 Py_DECREF(tuple);
514 Py_DECREF(path);
515 PyErr_NoMemory();
516 return NULL;
520 PyDoc_STRVAR(Connection__unregister_object_path__doc__,
521 "_unregister_object_path(path: str)\n\n"
524 static PyObject *
525 Connection__unregister_object_path(Connection *self, PyObject *args,
526 PyObject *kwargs)
528 dbus_bool_t ok;
529 PyObject *path;
530 PyObject *callbacks;
531 static char *argnames[] = {"path", NULL};
533 if (!PyArg_ParseTupleAndKeywords(args, kwargs,
534 "O!:_unregister_object_path",
535 argnames,
536 &PyString_Type, &path)) return NULL;
538 /* Take a ref to the path. Same comments as for _register_object_path. */
539 if (PyString_CheckExact(path) || path->ob_type == &ObjectPathType) {
540 Py_INCREF(path);
542 else {
543 path = PyString_FromString(PyString_AS_STRING(path));
544 if (!path) return NULL;
547 /* Guard against unregistering a handler that doesn't, in fact, exist,
548 or whose unregistration is already in progress. */
549 callbacks = PyDict_GetItem(self->object_paths, path);
550 if (!callbacks || callbacks == Py_None) {
551 PyErr_Format(PyExc_KeyError, "Can't unregister the object-path "
552 "handler for '%s': there is no such handler",
553 PyString_AS_STRING(path));
554 Py_DECREF(path);
555 return NULL;
558 /* Hang on to a reference to the callbacks for the moment. */
559 Py_INCREF(callbacks);
561 /* Get rid of the object-path while we still have the GIL, to
562 guard against unregistering twice from different threads (which
563 causes undefined behaviour in libdbus).
565 Because deletion would make it possible for the re-insertion below
566 to fail, we instead set the handler to None as a placeholder.
568 if (PyDict_SetItem(self->object_paths, path, Py_None) < 0) {
569 /* If that failed, there's no need to be paranoid as below - the
570 callbacks are still set, so we failed, but at least everything
571 is in sync. */
572 Py_DECREF(callbacks);
573 Py_DECREF(path);
574 return NULL;
577 /* BEGIN PARANOIA
578 This is something of a critical section - the dict of object-paths
579 and libdbus' internal structures are out of sync for a bit. We have
580 to be able to cope with that.
582 It's really annoying that dbus_connection_unregister_object_path
583 can fail, *and* has undefined behaviour if the object path has
584 already been unregistered. Either/or would be fine.
587 Py_BEGIN_ALLOW_THREADS
588 ok = dbus_connection_unregister_object_path(self->conn,
589 PyString_AS_STRING(path));
590 Py_END_ALLOW_THREADS
592 if (ok) {
593 Py_DECREF(callbacks);
594 PyDict_DelItem(self->object_paths, path);
595 /* END PARANOIA on successful code path */
596 /* The above can't fail unless by some strange trickery the key is no
597 longer present. Ignore any errors. */
598 Py_DECREF(path);
599 PyErr_Clear();
600 Py_RETURN_NONE;
602 else {
603 /* Oops, OOM. Put the callbacks back in the dict so
604 * we'll have another go if/when the user frees some memory
605 * and tries calling this method again. */
606 PyDict_SetItem(self->object_paths, path, callbacks);
607 /* END PARANOIA on failing code path */
608 /* If the SetItem failed, there's nothing we can do about it - but
609 since we know it's an existing entry, it shouldn't be able to fail
610 anyway. */
611 Py_DECREF(path);
612 Py_DECREF(callbacks);
613 return PyErr_NoMemory();
617 /* dbus_connection_get_object_path_data - not useful to Python,
618 * the object path data is just a PyString containing the path */
619 /* dbus_connection_list_registered could be useful, though */
621 /* dbus_connection_set_change_sigpipe - sets global state */
623 /* Maxima. Does Python code ever need to manipulate these?
624 * OTOH they're easy to wrap */
625 /* dbus_connection_set_max_message_size */
626 /* dbus_connection_get_max_message_size */
627 /* dbus_connection_set_max_received_size */
628 /* dbus_connection_get_max_received_size */
630 /* dbus_connection_get_outgoing_size - almost certainly unneeded */
632 static struct PyMethodDef Connection_tp_methods[] = {
633 #define ENTRY(name, flags) {#name, (PyCFunction)Connection_##name, flags, Connection_##name##__doc__}
634 ENTRY(close, METH_NOARGS),
635 ENTRY(flush, METH_NOARGS),
636 ENTRY(get_is_connected, METH_NOARGS),
637 ENTRY(get_is_authenticated, METH_NOARGS),
638 ENTRY(set_exit_on_disconnect, METH_VARARGS),
639 ENTRY(get_unix_fd, METH_NOARGS),
640 ENTRY(get_peer_unix_user, METH_NOARGS),
641 ENTRY(get_peer_unix_process_id, METH_NOARGS),
642 ENTRY(_add_filter, METH_O),
643 ENTRY(_register_object_path, METH_VARARGS|METH_KEYWORDS),
644 ENTRY(_remove_filter, METH_O),
645 ENTRY(_send, METH_VARARGS),
646 ENTRY(_send_with_reply, METH_VARARGS),
647 ENTRY(_send_with_reply_and_block, METH_VARARGS),
648 ENTRY(_unregister_object_path, METH_VARARGS|METH_KEYWORDS),
649 {NULL},
650 #undef ENTRY
653 /* vim:set ft=c cino< sw=4 sts=4 et: */