NEWS: Describe the fix for #10457
[dbus-python-phuang.git] / _dbus_bindings / message-append.c
blobfa557cbf8a2f09e1fc43e9a5777be2be9b6ee88b
1 /* D-Bus Message serialization. This contains all the logic to map from
2 * Python objects to D-Bus types.
4 * Copyright (C) 2006 Collabora Ltd.
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 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #define DBG_IS_TOO_VERBOSE
25 #include "types-internal.h"
26 #include "message-internal.h"
28 /* Return the number of variants wrapping the given object. Return 0
29 * if the object is not a D-Bus type.
31 static long
32 get_variant_level(PyObject *obj)
34 if (DBusPyIntBase_Check(obj)) {
35 return ((DBusPyIntBase *)obj)->variant_level;
37 else if (DBusPyFloatBase_Check(obj)) {
38 return ((DBusPyFloatBase *)obj)->variant_level;
40 else if (DBusPyArray_Check(obj)) {
41 return ((DBusPyArray *)obj)->variant_level;
43 else if (DBusPyDict_Check(obj)) {
44 return ((DBusPyDict *)obj)->variant_level;
46 else if (DBusPyString_Check(obj)) {
47 return ((DBusPyString *)obj)->variant_level;
49 else if (DBusPyLongBase_Check(obj) ||
50 DBusPyStrBase_Check(obj) ||
51 DBusPyStruct_Check(obj)) {
52 return dbus_py_variant_level_get(obj);
54 else {
55 return 0;
59 char dbus_py_Message_append__doc__[] = (
60 "set_args(*args[, **kwargs])\n\n"
61 "Set the message's arguments from the positional parameter, according to\n"
62 "the signature given by the ``signature`` keyword parameter.\n"
63 "\n"
64 "The following type conversions are supported:\n\n"
65 "=============================== ===========================\n"
66 "D-Bus (in signature) Python\n"
67 "=============================== ===========================\n"
68 "boolean (b) any object (via bool())\n"
69 "byte (y) string of length 1\n"
70 " any integer\n"
71 "any integer type any integer\n"
72 "double (d) any float\n"
73 "object path anything with a __dbus_object_path__ attribute\n"
74 "string, signature, object path str (must be UTF-8) or unicode\n"
75 "dict (a{...}) any mapping\n"
76 "array (a...) any iterable over appropriate objects\n"
77 "struct ((...)) any iterable over appropriate objects\n"
78 "variant any object above (guess type as below)\n"
79 "=============================== ===========================\n"
80 "\n"
81 "Here 'any integer' means anything on which int() or long()\n"
82 "(as appropriate) will work, except for basestring subclasses.\n"
83 "'Any float' means anything on which float() will work, except\n"
84 "for basestring subclasses.\n"
85 "\n"
86 "If there is no signature, guess from the arguments using\n"
87 "the static method `Message.guess_signature`.\n"
90 char dbus_py_Message_guess_signature__doc__[] = (
91 "guess_signature(*args) -> Signature [static method]\n\n"
92 "Guess a D-Bus signature which should be used to encode the given\n"
93 "Python objects.\n"
94 "\n"
95 "The signature is constructed as follows:\n\n"
96 "+-------------------------------+---------------------------+\n"
97 "|Python |D-Bus |\n"
98 "+===============================+===========================+\n"
99 "|D-Bus type, variant_level > 0 |variant (v) |\n"
100 "+-------------------------------+---------------------------+\n"
101 "|D-Bus type, variant_level == 0 |the corresponding type |\n"
102 "+-------------------------------+---------------------------+\n"
103 "|anything with a |object path |\n"
104 "|__dbus_object_path__ attribute | |\n"
105 "+-------------------------------+---------------------------+\n"
106 "|bool |boolean (y) |\n"
107 "+-------------------------------+---------------------------+\n"
108 "|any other int subclass |int32 (i) |\n"
109 "+-------------------------------+---------------------------+\n"
110 "|any other long subclass |int64 (x) |\n"
111 "+-------------------------------+---------------------------+\n"
112 "|any other float subclass |double (d) |\n"
113 "+-------------------------------+---------------------------+\n"
114 "|any other str subclass |string (s) |\n"
115 "+-------------------------------+---------------------------+\n"
116 "|any other unicode subclass |string (s) |\n"
117 "+-------------------------------+---------------------------+\n"
118 "|any other tuple subclass |struct ((...)) |\n"
119 "+-------------------------------+---------------------------+\n"
120 "|any other list subclass |array (a...), guess |\n"
121 "| |contents' type according to|\n"
122 "| |type of first item |\n"
123 "+-------------------------------+---------------------------+\n"
124 "|any other dict subclass |dict (a{...}), guess key, |\n"
125 "| |value type according to |\n"
126 "| |types for an arbitrary item|\n"
127 "+-------------------------------+---------------------------+\n"
128 "|anything else |raise TypeError |\n"
129 "+-------------------------------+---------------------------+\n"
132 /* return a new reference, possibly to None */
133 static PyObject *
134 get_object_path(PyObject *obj)
136 PyObject *magic_attr = PyObject_GetAttr(obj, dbus_py__dbus_object_path__const);
138 if (magic_attr) {
139 if (PyString_Check(magic_attr)) {
140 return magic_attr;
142 else {
143 Py_DECREF(magic_attr);
144 PyErr_SetString(PyExc_TypeError, "__dbus_object_path__ must be "
145 "a string");
146 return NULL;
149 else {
150 /* Ignore exceptions, except for SystemExit and KeyboardInterrupt */
151 if (PyErr_ExceptionMatches(PyExc_SystemExit) ||
152 PyErr_ExceptionMatches(PyExc_KeyboardInterrupt))
153 return NULL;
154 PyErr_Clear();
155 Py_RETURN_NONE;
159 /* Return a new reference. If the object is a variant and variant_level_ptr
160 * is not NULL, put the variant level in the variable pointed to, and
161 * return the contained type instead of "v". */
162 static PyObject *
163 _signature_string_from_pyobject(PyObject *obj, long *variant_level_ptr)
165 PyObject *magic_attr;
166 long variant_level = get_variant_level(obj);
167 if (variant_level_ptr) {
168 *variant_level_ptr = variant_level;
170 else if (variant_level > 0) {
171 return PyString_FromString(DBUS_TYPE_VARIANT_AS_STRING);
174 if (obj == Py_True || obj == Py_False) {
175 return PyString_FromString(DBUS_TYPE_BOOLEAN_AS_STRING);
178 magic_attr = get_object_path(obj);
179 if (!magic_attr)
180 return NULL;
181 if (magic_attr != Py_None) {
182 Py_DECREF(magic_attr);
183 return PyString_FromString(DBUS_TYPE_OBJECT_PATH_AS_STRING);
185 Py_DECREF(magic_attr);
187 /* Ordering is important: some of these are subclasses of each other. */
188 if (PyInt_Check(obj)) {
189 if (DBusPyInt16_Check(obj))
190 return PyString_FromString(DBUS_TYPE_INT16_AS_STRING);
191 else if (DBusPyInt32_Check(obj))
192 return PyString_FromString(DBUS_TYPE_INT32_AS_STRING);
193 else if (DBusPyByte_Check(obj))
194 return PyString_FromString(DBUS_TYPE_BYTE_AS_STRING);
195 else if (DBusPyUInt16_Check(obj))
196 return PyString_FromString(DBUS_TYPE_UINT16_AS_STRING);
197 else if (DBusPyBoolean_Check(obj))
198 return PyString_FromString(DBUS_TYPE_BOOLEAN_AS_STRING);
199 else
200 return PyString_FromString(DBUS_TYPE_INT32_AS_STRING);
202 else if (PyLong_Check(obj)) {
203 if (DBusPyInt64_Check(obj))
204 return PyString_FromString(DBUS_TYPE_INT64_AS_STRING);
205 else if (DBusPyUInt32_Check(obj))
206 return PyString_FromString(DBUS_TYPE_UINT32_AS_STRING);
207 else if (DBusPyUInt64_Check(obj))
208 return PyString_FromString(DBUS_TYPE_UINT64_AS_STRING);
209 else
210 return PyString_FromString(DBUS_TYPE_INT64_AS_STRING);
212 else if (PyUnicode_Check(obj))
213 return PyString_FromString(DBUS_TYPE_STRING_AS_STRING);
214 else if (PyFloat_Check(obj)) {
215 #ifdef WITH_DBUS_FLOAT32
216 if (DBusPyDouble_Check(obj))
217 return PyString_FromString(DBUS_TYPE_DOUBLE_AS_STRING);
218 else if (DBusPyFloat_Check(obj))
219 return PyString_FromString(DBUS_TYPE_FLOAT_AS_STRING);
220 else
221 #endif
222 return PyString_FromString(DBUS_TYPE_DOUBLE_AS_STRING);
224 else if (PyString_Check(obj)) {
225 if (DBusPyObjectPath_Check(obj))
226 return PyString_FromString(DBUS_TYPE_OBJECT_PATH_AS_STRING);
227 else if (DBusPySignature_Check(obj))
228 return PyString_FromString(DBUS_TYPE_SIGNATURE_AS_STRING);
229 else if (DBusPyByteArray_Check(obj))
230 return PyString_FromString(DBUS_TYPE_ARRAY_AS_STRING
231 DBUS_TYPE_BYTE_AS_STRING);
232 else
233 return PyString_FromString(DBUS_TYPE_STRING_AS_STRING);
235 else if (PyTuple_Check(obj)) {
236 Py_ssize_t len = PyTuple_GET_SIZE(obj);
237 PyObject *list = PyList_New(len + 2); /* new ref */
238 PyObject *item; /* temporary new ref */
239 PyObject *empty_str; /* temporary new ref */
240 PyObject *ret;
241 Py_ssize_t i;
243 if (!list) return NULL;
244 if (len == 0) {
245 PyErr_SetString(PyExc_ValueError, "D-Bus structs cannot be empty");
246 Py_DECREF(list);
247 return NULL;
249 /* Set the first and last elements of list to be the parentheses */
250 item = PyString_FromString(DBUS_STRUCT_BEGIN_CHAR_AS_STRING);
251 if (PyList_SetItem(list, 0, item) < 0) {
252 Py_DECREF(list);
253 return NULL;
255 item = PyString_FromString(DBUS_STRUCT_END_CHAR_AS_STRING);
256 if (PyList_SetItem(list, len + 1, item) < 0) {
257 Py_DECREF(list);
258 return NULL;
260 if (!item || !PyList_GET_ITEM(list, 0)) {
261 Py_DECREF(list);
262 return NULL;
264 item = NULL;
266 for (i = 0; i < len; i++) {
267 item = PyTuple_GetItem(obj, i);
268 if (!item) {
269 Py_DECREF(list);
270 return NULL;
272 item = _signature_string_from_pyobject(item, NULL);
273 if (!item) {
274 Py_DECREF(list);
275 return NULL;
277 if (PyList_SetItem(list, i + 1, item) < 0) {
278 Py_DECREF(list);
279 return NULL;
281 item = NULL;
283 empty_str = PyString_FromString("");
284 if (!empty_str) {
285 /* really shouldn't happen */
286 Py_DECREF(list);
287 return NULL;
289 ret = PyObject_CallMethod(empty_str, "join", "(O)", list); /* new ref */
290 /* whether ret is NULL or not, */
291 Py_DECREF(empty_str);
292 Py_DECREF(list);
293 return ret;
295 else if (PyList_Check(obj)) {
296 PyObject *tmp;
297 PyObject *ret = PyString_FromString(DBUS_TYPE_ARRAY_AS_STRING);
298 if (!ret) return NULL;
299 if (DBusPyArray_Check(obj) && PyString_Check(((DBusPyArray *)obj)->signature)) {
300 PyString_Concat(&ret, ((DBusPyArray *)obj)->signature);
301 return ret;
303 if (PyList_GET_SIZE(obj) == 0) {
304 /* No items, so fail. Or should we guess "av"? */
305 PyErr_SetString(PyExc_ValueError, "Unable to guess signature "
306 "from an empty list");
307 return NULL;
309 tmp = PyList_GetItem(obj, 0);
310 tmp = _signature_string_from_pyobject(tmp, NULL);
311 if (!tmp) return NULL;
312 PyString_ConcatAndDel(&ret, tmp);
313 return ret;
315 else if (PyDict_Check(obj)) {
316 PyObject *key, *value, *keysig, *valuesig;
317 Py_ssize_t pos = 0;
318 PyObject *ret = NULL;
320 if (DBusPyDict_Check(obj) && PyString_Check(((DBusPyDict *)obj)->signature)) {
321 const char *sig = PyString_AS_STRING(((DBusPyDict *)obj)->signature);
323 return PyString_FromFormat((DBUS_TYPE_ARRAY_AS_STRING
324 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
325 "%s"
326 DBUS_DICT_ENTRY_END_CHAR_AS_STRING),
327 sig);
329 if (!PyDict_Next(obj, &pos, &key, &value)) {
330 /* No items, so fail. Or should we guess "a{vv}"? */
331 PyErr_SetString(PyExc_ValueError, "Unable to guess signature "
332 "from an empty dict");
333 return NULL;
335 keysig = _signature_string_from_pyobject(key, NULL);
336 valuesig = _signature_string_from_pyobject(value, NULL);
337 if (keysig && valuesig) {
338 ret = PyString_FromFormat((DBUS_TYPE_ARRAY_AS_STRING
339 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
340 "%s%s"
341 DBUS_DICT_ENTRY_END_CHAR_AS_STRING),
342 PyString_AS_STRING(keysig),
343 PyString_AS_STRING(valuesig));
345 Py_XDECREF(keysig);
346 Py_XDECREF(valuesig);
347 return ret;
349 else {
350 PyErr_Format(PyExc_TypeError, "Don't know how which D-Bus type "
351 "to use to encode type \"%s\"",
352 obj->ob_type->tp_name);
353 return NULL;
357 PyObject *
358 dbus_py_Message_guess_signature(PyObject *unused UNUSED, PyObject *args)
360 PyObject *tmp, *ret = NULL;
362 if (!args) {
363 if (!PyErr_Occurred()) {
364 PyErr_BadInternalCall();
366 return NULL;
369 #ifdef USING_DBG
370 fprintf(stderr, "DBG/%ld: called Message_guess_signature", (long)getpid());
371 PyObject_Print(args, stderr, 0);
372 fprintf(stderr, "\n");
373 #endif
375 if (!PyTuple_Check(args)) {
376 DBG("%s", "Message_guess_signature: args not a tuple");
377 PyErr_BadInternalCall();
378 return NULL;
381 /* if there were no args, easy */
382 if (PyTuple_GET_SIZE(args) == 0) {
383 DBG("%s", "Message_guess_signature: no args, so return Signature('')");
384 return PyObject_CallFunction((PyObject *)&DBusPySignature_Type, "(s)", "");
387 /* if there were args, the signature we want is, by construction,
388 * exactly the signature we get for the tuple args, except that we don't
389 * want the parentheses. */
390 tmp = _signature_string_from_pyobject(args, NULL);
391 if (!tmp) {
392 DBG("%s", "Message_guess_signature: failed");
393 return NULL;
395 if (!PyString_Check(tmp) || PyString_GET_SIZE(tmp) < 2) {
396 PyErr_SetString(PyExc_RuntimeError, "Internal error: "
397 "_signature_string_from_pyobject returned "
398 "a bad result");
399 Py_DECREF(tmp);
400 return NULL;
402 ret = PyObject_CallFunction((PyObject *)&DBusPySignature_Type, "(s#)",
403 PyString_AS_STRING(tmp) + 1,
404 PyString_GET_SIZE(tmp) - 2);
405 DBG("Message_guess_signature: returning Signature at %p \"%s\"", ret,
406 ret ? PyString_AS_STRING(ret) : "(NULL)");
407 Py_DECREF(tmp);
408 return ret;
411 static int _message_iter_append_pyobject(DBusMessageIter *appender,
412 DBusSignatureIter *sig_iter,
413 PyObject *obj,
414 dbus_bool_t *more);
415 static int _message_iter_append_variant(DBusMessageIter *appender,
416 PyObject *obj);
418 static int
419 _message_iter_append_string(DBusMessageIter *appender,
420 int sig_type, PyObject *obj,
421 dbus_bool_t allow_object_path_attr)
423 char *s;
425 if (sig_type == DBUS_TYPE_OBJECT_PATH && allow_object_path_attr) {
426 PyObject *object_path = get_object_path (obj);
428 if (object_path == Py_None) {
429 Py_DECREF(object_path);
431 else if (!object_path) {
432 return -1;
434 else {
435 int ret = _message_iter_append_string(appender, sig_type,
436 object_path, FALSE);
437 Py_DECREF(object_path);
438 return ret;
442 if (PyString_Check(obj)) {
443 PyObject *unicode;
445 /* Raise TypeError if the string has embedded NULs */
446 if (PyString_AsStringAndSize(obj, &s, NULL) < 0) return -1;
447 /* Surely there's a faster stdlib way to validate UTF-8... */
448 unicode = PyUnicode_DecodeUTF8(s, PyString_GET_SIZE(obj), NULL);
449 if (!unicode) {
450 PyErr_SetString(PyExc_UnicodeError, "String parameters "
451 "to be sent over D-Bus must be valid UTF-8");
452 return -1;
454 Py_DECREF(unicode);
455 unicode = NULL;
457 DBG("Performing actual append: string %s", s);
458 if (!dbus_message_iter_append_basic(appender, sig_type,
459 &s)) {
460 PyErr_NoMemory();
461 return -1;
464 else if (PyUnicode_Check(obj)) {
465 PyObject *utf8 = PyUnicode_AsUTF8String(obj);
466 if (!utf8) return -1;
467 /* Raise TypeError if the string has embedded NULs */
468 if (PyString_AsStringAndSize(utf8, &s, NULL) < 0) return -1;
469 DBG("Performing actual append: string (from unicode) %s", s);
470 if (!dbus_message_iter_append_basic(appender, sig_type, &s)) {
471 PyErr_NoMemory();
472 return -1;
474 Py_DECREF(utf8);
476 else {
477 PyErr_SetString(PyExc_TypeError,
478 "Expected a string or unicode object");
479 return -1;
481 return 0;
484 static int
485 _message_iter_append_byte(DBusMessageIter *appender, PyObject *obj)
487 unsigned char y;
489 if (PyString_Check(obj)) {
490 if (PyString_GET_SIZE(obj) != 1) {
491 PyErr_Format(PyExc_ValueError, "Expected a string of "
492 "length 1 byte, but found %d bytes",
493 PyString_GET_SIZE(obj));
494 return -1;
496 y = *(unsigned char *)PyString_AS_STRING(obj);
498 else {
499 long i = PyInt_AsLong(obj);
501 if (i == -1 && PyErr_Occurred()) return -1;
502 if (i < 0 || i > 0xff) {
503 PyErr_Format(PyExc_ValueError, "%d outside range for a "
504 "byte value", (int)i);
505 return -1;
507 y = i;
509 DBG("Performing actual append: byte \\x%02x", (unsigned)y);
510 if (!dbus_message_iter_append_basic(appender, DBUS_TYPE_BYTE, &y)) {
511 PyErr_NoMemory();
512 return -1;
514 return 0;
517 static int
518 _message_iter_append_dictentry(DBusMessageIter *appender,
519 DBusSignatureIter *sig_iter,
520 PyObject *dict, PyObject *key)
522 DBusSignatureIter sub_sig_iter;
523 DBusMessageIter sub;
524 int ret = -1;
525 PyObject *value = PyObject_GetItem(dict, key);
526 dbus_bool_t more;
528 if (!value) return -1;
530 #ifdef USING_DBG
531 fprintf(stderr, "Append dictentry: ");
532 PyObject_Print(key, stderr, 0);
533 fprintf(stderr, " => ");
534 PyObject_Print(value, stderr, 0);
535 fprintf(stderr, "\n");
536 #endif
538 DBG("Recursing signature iterator %p -> %p", sig_iter, &sub_sig_iter);
539 dbus_signature_iter_recurse(sig_iter, &sub_sig_iter);
540 #ifdef USING_DBG
542 char *s;
543 s = dbus_signature_iter_get_signature(sig_iter);
544 DBG("Signature of parent iterator %p is %s", sig_iter, s);
545 dbus_free(s);
546 s = dbus_signature_iter_get_signature(&sub_sig_iter);
547 DBG("Signature of sub-iterator %p is %s", &sub_sig_iter, s);
548 dbus_free(s);
550 #endif
552 DBG("%s", "Opening DICT_ENTRY container");
553 if (!dbus_message_iter_open_container(appender, DBUS_TYPE_DICT_ENTRY,
554 NULL, &sub)) {
555 PyErr_NoMemory();
556 goto out;
558 ret = _message_iter_append_pyobject(&sub, &sub_sig_iter, key, &more);
559 if (ret == 0) {
560 ret = _message_iter_append_pyobject(&sub, &sub_sig_iter, value, &more);
562 DBG("%s", "Closing DICT_ENTRY container");
563 if (!dbus_message_iter_close_container(appender, &sub)) {
564 PyErr_NoMemory();
565 ret = -1;
567 out:
568 Py_DECREF(value);
569 return ret;
572 static int
573 _message_iter_append_multi(DBusMessageIter *appender,
574 const DBusSignatureIter *sig_iter,
575 int mode, PyObject *obj)
577 DBusMessageIter sub_appender;
578 DBusSignatureIter sub_sig_iter;
579 PyObject *contents;
580 int ret;
581 PyObject *iterator = PyObject_GetIter(obj);
582 char *sig = NULL;
583 int container = mode;
584 dbus_bool_t is_byte_array = DBusPyByteArray_Check(obj);
585 int inner_type;
586 dbus_bool_t more;
588 #ifdef USING_DBG
589 fprintf(stderr, "Appending multiple: ");
590 PyObject_Print(obj, stderr, 0);
591 fprintf(stderr, "\n");
592 #endif
594 if (!iterator) return -1;
595 if (mode == DBUS_TYPE_DICT_ENTRY) container = DBUS_TYPE_ARRAY;
597 DBG("Recursing signature iterator %p -> %p", sig_iter, &sub_sig_iter);
598 dbus_signature_iter_recurse(sig_iter, &sub_sig_iter);
599 #ifdef USING_DBG
601 char *s;
602 s = dbus_signature_iter_get_signature(sig_iter);
603 DBG("Signature of parent iterator %p is %s", sig_iter, s);
604 dbus_free(s);
605 s = dbus_signature_iter_get_signature(&sub_sig_iter);
606 DBG("Signature of sub-iterator %p is %s", &sub_sig_iter, s);
607 dbus_free(s);
609 #endif
610 inner_type = dbus_signature_iter_get_current_type(&sub_sig_iter);
612 if (mode == DBUS_TYPE_ARRAY || mode == DBUS_TYPE_DICT_ENTRY) {
613 sig = dbus_signature_iter_get_signature(&sub_sig_iter);
614 if (!sig) {
615 PyErr_NoMemory();
616 ret = -1;
617 goto out;
620 /* else leave sig set to NULL. */
622 DBG("Opening %c container", container);
623 if (!dbus_message_iter_open_container(appender, container,
624 sig, &sub_appender)) {
625 PyErr_NoMemory();
626 ret = -1;
627 goto out;
629 ret = 0;
630 while ((contents = PyIter_Next(iterator))) {
632 if (mode == DBUS_TYPE_ARRAY || mode == DBUS_TYPE_DICT_ENTRY) {
633 DBG("Recursing signature iterator %p -> %p", sig_iter, &sub_sig_iter);
634 dbus_signature_iter_recurse(sig_iter, &sub_sig_iter);
635 #ifdef USING_DBG
637 char *s;
638 s = dbus_signature_iter_get_signature(sig_iter);
639 DBG("Signature of parent iterator %p is %s", sig_iter, s);
640 dbus_free(s);
641 s = dbus_signature_iter_get_signature(&sub_sig_iter);
642 DBG("Signature of sub-iterator %p is %s", &sub_sig_iter, s);
643 dbus_free(s);
645 #endif
648 if (mode == DBUS_TYPE_DICT_ENTRY) {
649 ret = _message_iter_append_dictentry(&sub_appender, &sub_sig_iter,
650 obj, contents);
652 else if (mode == DBUS_TYPE_ARRAY && is_byte_array
653 && inner_type == DBUS_TYPE_VARIANT) {
654 /* Subscripting a ByteArray gives a str of length 1, but if the
655 * container is a ByteArray and the parameter is an array of
656 * variants, we want to produce an array of variants containing
657 * bytes, not strings.
659 PyObject *args = Py_BuildValue("(O)", contents);
660 PyObject *byte;
662 if (!args)
663 break;
664 byte = PyObject_Call((PyObject *)&DBusPyByte_Type, args, NULL);
665 Py_DECREF(args);
666 if (!byte)
667 break;
668 ret = _message_iter_append_variant(&sub_appender, byte);
669 Py_DECREF(byte);
671 else {
672 /* advances sub_sig_iter and sets more on success - for array
673 * this doesn't matter, for struct it's essential */
674 ret = _message_iter_append_pyobject(&sub_appender, &sub_sig_iter,
675 contents, &more);
678 Py_DECREF(contents);
679 if (ret < 0) {
680 break;
684 if (PyErr_Occurred()) {
685 ret = -1;
687 else if (mode == DBUS_TYPE_STRUCT && more) {
688 PyErr_Format(PyExc_TypeError, "More items found in struct's D-Bus "
689 "signature than in Python arguments ");
690 ret = -1;
693 /* This must be run as cleanup, even on failure. */
694 DBG("Closing %c container", container);
695 if (!dbus_message_iter_close_container(appender, &sub_appender)) {
696 PyErr_NoMemory();
697 ret = -1;
700 out:
701 Py_XDECREF(iterator);
702 dbus_free(sig);
703 return ret;
706 static int
707 _message_iter_append_string_as_byte_array(DBusMessageIter *appender,
708 PyObject *obj)
710 /* a bit of a faster path for byte arrays that are strings */
711 Py_ssize_t len = PyString_GET_SIZE(obj);
712 const char *s;
713 DBusMessageIter sub;
714 int ret;
716 s = PyString_AS_STRING(obj);
717 DBG("%s", "Opening ARRAY container");
718 if (!dbus_message_iter_open_container(appender, DBUS_TYPE_ARRAY,
719 DBUS_TYPE_BYTE_AS_STRING, &sub)) {
720 PyErr_NoMemory();
721 return -1;
723 DBG("Appending fixed array of %d bytes", len);
724 if (dbus_message_iter_append_fixed_array(&sub, DBUS_TYPE_BYTE, &s, len)) {
725 ret = 0;
727 else {
728 PyErr_NoMemory();
729 ret = -1;
731 DBG("%s", "Closing ARRAY container");
732 if (!dbus_message_iter_close_container(appender, &sub)) {
733 PyErr_NoMemory();
734 return -1;
736 return ret;
739 /* Encode some Python object into a D-Bus variant slot. */
740 static int
741 _message_iter_append_variant(DBusMessageIter *appender, PyObject *obj)
743 DBusSignatureIter obj_sig_iter;
744 const char *obj_sig_str;
745 PyObject *obj_sig;
746 int ret;
747 long variant_level;
748 dbus_bool_t dummy;
750 /* Separate the object into the contained object, and the number of
751 * variants it's wrapped in. */
752 obj_sig = _signature_string_from_pyobject(obj, &variant_level);
753 if (!obj_sig) return -1;
755 obj_sig_str = PyString_AsString(obj_sig);
756 if (!obj_sig_str) return -1;
758 if (variant_level < 1) {
759 variant_level = 1;
762 dbus_signature_iter_init(&obj_sig_iter, obj_sig_str);
764 { /* scope for variant_iters */
765 DBusMessageIter variant_iters[variant_level];
766 long i;
768 for (i = 0; i < variant_level; i++) {
769 DBusMessageIter *child = &variant_iters[i];
770 /* The first is a special case: its parent is the iter passed in
771 * to this function, instead of being the previous one in the
772 * stack
774 DBusMessageIter *parent = (i == 0
775 ? appender
776 : &(variant_iters[i-1]));
777 /* The last is also a special case: it contains the actual
778 * object, rather than another variant
780 const char *sig_str = (i == variant_level-1
781 ? obj_sig_str
782 : DBUS_TYPE_VARIANT_AS_STRING);
784 DBG("Opening VARIANT container %p inside %p containing '%s'",
785 child, parent, sig_str);
786 if (!dbus_message_iter_open_container(parent, DBUS_TYPE_VARIANT,
787 sig_str, child)) {
788 PyErr_NoMemory();
789 ret = -1;
790 goto out;
794 /* Put the object itself into the innermost variant */
795 ret = _message_iter_append_pyobject(&variant_iters[variant_level-1],
796 &obj_sig_iter, obj, &dummy);
798 /* here we rely on i (and variant_level) being a signed long */
799 for (i = variant_level - 1; i >= 0; i--) {
800 DBusMessageIter *child = &variant_iters[i];
801 /* The first is a special case: its parent is the iter passed in
802 * to this function, instead of being the previous one in the
803 * stack
805 DBusMessageIter *parent = (i == 0 ? appender
806 : &(variant_iters[i-1]));
808 DBG("Closing VARIANT container %p inside %p", child, parent);
809 if (!dbus_message_iter_close_container(parent, child)) {
810 PyErr_NoMemory();
811 ret = -1;
812 goto out;
818 out:
819 Py_XDECREF(obj_sig);
820 return ret;
823 /* On success, *more is set to whether there's more in the signature. */
824 static int
825 _message_iter_append_pyobject(DBusMessageIter *appender,
826 DBusSignatureIter *sig_iter,
827 PyObject *obj,
828 dbus_bool_t *more)
830 int sig_type = dbus_signature_iter_get_current_type(sig_iter);
831 union {
832 dbus_bool_t b;
833 double d;
834 dbus_uint16_t uint16;
835 dbus_int16_t int16;
836 dbus_uint32_t uint32;
837 dbus_int32_t int32;
838 #if defined(DBUS_HAVE_INT64) && defined(HAVE_LONG_LONG)
839 dbus_uint64_t uint64;
840 dbus_int64_t int64;
841 #endif
842 } u;
843 int ret = -1;
845 #ifdef USING_DBG
846 fprintf(stderr, "Appending object at %p: ", obj);
847 PyObject_Print(obj, stderr, 0);
848 fprintf(stderr, " into appender at %p, dbus wants type %c\n",
849 appender, sig_type);
850 #endif
852 switch (sig_type) {
853 /* The numeric types are relatively simple to deal with, so are
854 * inlined here. */
856 case DBUS_TYPE_BOOLEAN:
857 if (PyObject_IsTrue(obj)) {
858 u.b = 1;
860 else {
861 u.b = 0;
863 DBG("Performing actual append: bool(%ld)", (long)u.b);
864 if (!dbus_message_iter_append_basic(appender, sig_type, &u.b)) {
865 PyErr_NoMemory();
866 ret = -1;
867 break;
869 ret = 0;
870 break;
872 case DBUS_TYPE_DOUBLE:
873 u.d = PyFloat_AsDouble(obj);
874 if (PyErr_Occurred()) {
875 ret = -1;
876 break;
878 DBG("Performing actual append: double(%f)", u.d);
879 if (!dbus_message_iter_append_basic(appender, sig_type, &u.d)) {
880 PyErr_NoMemory();
881 ret = -1;
882 break;
884 ret = 0;
885 break;
887 #ifdef WITH_DBUS_FLOAT32
888 case DBUS_TYPE_FLOAT:
889 u.d = PyFloat_AsDouble(obj);
890 if (PyErr_Occurred()) {
891 ret = -1;
892 break;
894 u.f = (float)u.d;
895 DBG("Performing actual append: float(%f)", u.f);
896 if (!dbus_message_iter_append_basic(appender, sig_type, &u.f)) {
897 PyErr_NoMemory();
898 ret = -1;
899 break;
901 ret = 0;
902 break;
903 #endif
905 /* The integer types are all basically the same - we delegate to
906 intNN_range_check() */
907 #define PROCESS_INTEGER(size) \
908 u.size = dbus_py_##size##_range_check(obj);\
909 if (u.size == (dbus_##size##_t)(-1) && PyErr_Occurred()) {\
910 ret = -1; \
911 break; \
913 DBG("Performing actual append: " #size "(%lld)", (long long)u.size); \
914 if (!dbus_message_iter_append_basic(appender, sig_type, &u.size)) {\
915 PyErr_NoMemory();\
916 ret = -1;\
917 break;\
919 ret = 0;
921 case DBUS_TYPE_INT16:
922 PROCESS_INTEGER(int16)
923 break;
924 case DBUS_TYPE_UINT16:
925 PROCESS_INTEGER(uint16)
926 break;
927 case DBUS_TYPE_INT32:
928 PROCESS_INTEGER(int32)
929 break;
930 case DBUS_TYPE_UINT32:
931 PROCESS_INTEGER(uint32)
932 break;
933 #if defined(DBUS_HAVE_INT64) && defined(HAVE_LONG_LONG)
934 case DBUS_TYPE_INT64:
935 PROCESS_INTEGER(int64)
936 break;
937 case DBUS_TYPE_UINT64:
938 PROCESS_INTEGER(uint64)
939 break;
940 #else
941 case DBUS_TYPE_INT64:
942 case DBUS_TYPE_UINT64:
943 PyErr_SetString(PyExc_NotImplementedError, "64-bit integer "
944 "types are not supported on this platform");
945 ret = -1;
946 break;
947 #endif
948 #undef PROCESS_INTEGER
950 /* Now the more complicated cases, which are delegated to helper
951 * functions (although in practice, the compiler will hopefully
952 * inline them anyway). */
954 case DBUS_TYPE_STRING:
955 case DBUS_TYPE_SIGNATURE:
956 case DBUS_TYPE_OBJECT_PATH:
957 ret = _message_iter_append_string(appender, sig_type, obj, TRUE);
958 break;
960 case DBUS_TYPE_BYTE:
961 ret = _message_iter_append_byte(appender, obj);
962 break;
964 case DBUS_TYPE_ARRAY:
965 /* 3 cases - it might actually be a dict, or it might be a byte array
966 * being copied from a string (for which we have a faster path),
967 * or it might be a generic array. */
969 sig_type = dbus_signature_iter_get_element_type(sig_iter);
970 if (sig_type == DBUS_TYPE_DICT_ENTRY)
971 ret = _message_iter_append_multi(appender, sig_iter,
972 DBUS_TYPE_DICT_ENTRY, obj);
973 else if (sig_type == DBUS_TYPE_BYTE && PyString_Check(obj))
974 ret = _message_iter_append_string_as_byte_array(appender, obj);
975 else
976 ret = _message_iter_append_multi(appender, sig_iter,
977 DBUS_TYPE_ARRAY, obj);
978 DBG("_message_iter_append_multi(): %d", ret);
979 break;
981 case DBUS_TYPE_STRUCT:
982 ret = _message_iter_append_multi(appender, sig_iter, sig_type, obj);
983 break;
985 case DBUS_TYPE_VARIANT:
986 ret = _message_iter_append_variant(appender, obj);
987 break;
989 case DBUS_TYPE_INVALID:
990 PyErr_SetString(PyExc_TypeError, "Fewer items found in D-Bus "
991 "signature than in Python arguments");
992 ret = -1;
993 break;
995 default:
996 PyErr_Format(PyExc_TypeError, "Unknown type '\\x%x' in D-Bus "
997 "signature", sig_type);
998 ret = -1;
999 break;
1001 if (ret < 0) return -1;
1003 DBG("Advancing signature iter at %p", sig_iter);
1004 *more = dbus_signature_iter_next(sig_iter);
1005 #ifdef USING_DBG
1006 DBG("- result: %ld, type %02x '%c'", (long)(*more),
1007 (int)dbus_signature_iter_get_current_type(sig_iter),
1008 (int)dbus_signature_iter_get_current_type(sig_iter));
1009 #endif
1010 return 0;
1014 PyObject *
1015 dbus_py_Message_append(Message *self, PyObject *args, PyObject *kwargs)
1017 const char *signature = NULL;
1018 PyObject *signature_obj = NULL;
1019 DBusSignatureIter sig_iter;
1020 DBusMessageIter appender;
1021 int i;
1022 static char *argnames[] = {"signature", NULL};
1023 /* must start FALSE for the case where there's nothing there and we
1024 * never iterate at all */
1025 dbus_bool_t more;
1027 if (!self->msg) return DBusPy_RaiseUnusableMessage();
1029 #ifdef USING_DBG
1030 fprintf(stderr, "DBG/%ld: called Message_append(*", (long)getpid());
1031 PyObject_Print(args, stderr, 0);
1032 if (kwargs) {
1033 fprintf(stderr, ", **");
1034 PyObject_Print(kwargs, stderr, 0);
1036 fprintf(stderr, ")\n");
1037 #endif
1039 /* only use kwargs for this step: deliberately ignore args for now */
1040 if (!PyArg_ParseTupleAndKeywords(dbus_py_empty_tuple, kwargs, "|z:append",
1041 argnames, &signature)) return NULL;
1043 if (!signature) {
1044 DBG("%s", "No signature for message, guessing...");
1045 signature_obj = dbus_py_Message_guess_signature(NULL, args);
1046 if (!signature_obj) return NULL;
1047 signature = PyString_AS_STRING(signature_obj);
1049 /* from here onwards, you have to do a goto rather than returning NULL
1050 to make sure signature_obj gets freed */
1052 /* iterate over args and the signature, together */
1053 if (!dbus_signature_validate(signature, NULL)) {
1054 PyErr_SetString(PyExc_ValueError, "Corrupt type signature");
1055 goto err;
1057 dbus_signature_iter_init(&sig_iter, signature);
1058 dbus_message_iter_init_append(self->msg, &appender);
1059 more = (signature[0] != '\0');
1060 for (i = 0; i < PyTuple_GET_SIZE(args); i++) {
1061 if (_message_iter_append_pyobject(&appender, &sig_iter,
1062 PyTuple_GET_ITEM(args, i),
1063 &more) < 0) {
1064 goto hosed;
1067 if (more) {
1068 PyErr_SetString(PyExc_TypeError, "More items found in D-Bus "
1069 "signature than in Python arguments");
1070 goto hosed;
1073 /* success! */
1074 Py_XDECREF(signature_obj);
1075 Py_RETURN_NONE;
1077 hosed:
1078 /* "If appending any of the arguments fails due to lack of memory,
1079 * generally the message is hosed and you have to start over" -libdbus docs
1080 * Enforce this by throwing away the message structure.
1082 dbus_message_unref(self->msg);
1083 self->msg = NULL;
1084 err:
1085 Py_XDECREF(signature_obj);
1086 return NULL;
1089 /* vim:set ft=c cino< sw=4 sts=4 et: */