Add special case to serialization: objects with a __dbus_object_path__ attribute...
[dbus-python-phuang.git] / _dbus_bindings / message-append.c
blob74faf9fcf09b4bce7c96f4c5636eadab848a1d8a
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 * 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 #define DBG_IS_TOO_VERBOSE
27 #include "types-internal.h"
28 #include "message-internal.h"
30 /* Return the number of variants wrapping the given object. Return 0
31 * if the object is not a D-Bus type.
33 static long
34 get_variant_level(PyObject *obj)
36 if (DBusPyIntBase_Check(obj)) {
37 return ((DBusPyIntBase *)obj)->variant_level;
39 else if (DBusPyFloatBase_Check(obj)) {
40 return ((DBusPyFloatBase *)obj)->variant_level;
42 else if (DBusPyArray_Check(obj)) {
43 return ((DBusPyArray *)obj)->variant_level;
45 else if (DBusPyDict_Check(obj)) {
46 return ((DBusPyDict *)obj)->variant_level;
48 else if (DBusPyLongBase_Check(obj) ||
49 DBusPyStrBase_Check(obj) ||
50 DBusPyString_Check(obj) ||
51 DBusPyStruct_Check(obj)) {
52 return PyInt_AsLong(PyObject_GetAttr(obj, dbus_py_variant_level_const));
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 int 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 int 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 int 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 static int _message_iter_append_variant(DBusMessageIter *appender,
415 PyObject *obj);
417 static int
418 _message_iter_append_string(DBusMessageIter *appender,
419 int sig_type, PyObject *obj,
420 dbus_bool_t allow_object_path_attr)
422 char *s;
424 if (sig_type == DBUS_TYPE_OBJECT_PATH && allow_object_path_attr) {
425 PyObject *object_path = get_object_path (obj);
427 if (object_path == Py_None) {
428 Py_DECREF(object_path);
430 else if (!object_path) {
431 return -1;
433 else {
434 int ret = _message_iter_append_string(appender, sig_type,
435 object_path, FALSE);
436 Py_DECREF(object_path);
437 return ret;
441 if (PyString_Check(obj)) {
442 PyObject *unicode;
444 /* Raise TypeError if the string has embedded NULs */
445 if (PyString_AsStringAndSize(obj, &s, NULL) < 0) return -1;
446 /* Surely there's a faster stdlib way to validate UTF-8... */
447 unicode = PyUnicode_DecodeUTF8(s, PyString_GET_SIZE(obj), NULL);
448 if (!unicode) {
449 PyErr_SetString(PyExc_UnicodeError, "String parameters "
450 "to be sent over D-Bus must be valid UTF-8");
451 return -1;
453 Py_DECREF(unicode);
454 unicode = NULL;
456 DBG("Performing actual append: string %s", s);
457 if (!dbus_message_iter_append_basic(appender, sig_type,
458 &s)) {
459 PyErr_NoMemory();
460 return -1;
463 else if (PyUnicode_Check(obj)) {
464 PyObject *utf8 = PyUnicode_AsUTF8String(obj);
465 if (!utf8) return -1;
466 /* Raise TypeError if the string has embedded NULs */
467 if (PyString_AsStringAndSize(utf8, &s, NULL) < 0) return -1;
468 DBG("Performing actual append: string (from unicode) %s", s);
469 if (!dbus_message_iter_append_basic(appender, sig_type, &s)) {
470 PyErr_NoMemory();
471 return -1;
473 Py_DECREF(utf8);
475 else {
476 PyErr_SetString(PyExc_TypeError,
477 "Expected a string or unicode object");
478 return -1;
480 return 0;
483 static int
484 _message_iter_append_byte(DBusMessageIter *appender, PyObject *obj)
486 unsigned char y;
488 if (PyString_Check(obj)) {
489 if (PyString_GET_SIZE(obj) != 1) {
490 PyErr_Format(PyExc_ValueError, "Expected a string of "
491 "length 1 byte, but found %d bytes",
492 PyString_GET_SIZE(obj));
493 return -1;
495 y = *(unsigned char *)PyString_AS_STRING(obj);
497 else {
498 long i = PyInt_AsLong(obj);
500 if (i == -1 && PyErr_Occurred()) return -1;
501 if (i < 0 || i > 0xff) {
502 PyErr_Format(PyExc_ValueError, "%d outside range for a "
503 "byte value", (int)i);
504 return -1;
506 y = i;
508 DBG("Performing actual append: byte \\x%02x", (unsigned)y);
509 if (!dbus_message_iter_append_basic(appender, DBUS_TYPE_BYTE, &y)) {
510 PyErr_NoMemory();
511 return -1;
513 return 0;
516 static int
517 _message_iter_append_dictentry(DBusMessageIter *appender,
518 DBusSignatureIter *sig_iter,
519 PyObject *dict, PyObject *key)
521 DBusSignatureIter sub_sig_iter;
522 DBusMessageIter sub;
523 int ret = -1;
524 PyObject *value = PyObject_GetItem(dict, key);
526 if (!value) return -1;
528 #ifdef USING_DBG
529 fprintf(stderr, "Append dictentry: ");
530 PyObject_Print(key, stderr, 0);
531 fprintf(stderr, " => ");
532 PyObject_Print(value, stderr, 0);
533 fprintf(stderr, "\n");
534 #endif
536 DBG("Recursing signature iterator %p -> %p", sig_iter, &sub_sig_iter);
537 dbus_signature_iter_recurse(sig_iter, &sub_sig_iter);
538 #ifdef USING_DBG
540 char *s;
541 s = dbus_signature_iter_get_signature(sig_iter);
542 DBG("Signature of parent iterator %p is %s", sig_iter, s);
543 dbus_free(s);
544 s = dbus_signature_iter_get_signature(&sub_sig_iter);
545 DBG("Signature of sub-iterator %p is %s", &sub_sig_iter, s);
546 dbus_free(s);
548 #endif
550 DBG("%s", "Opening DICT_ENTRY container");
551 if (!dbus_message_iter_open_container(appender, DBUS_TYPE_DICT_ENTRY,
552 NULL, &sub)) {
553 PyErr_NoMemory();
554 goto out;
556 ret = _message_iter_append_pyobject(&sub, &sub_sig_iter, key);
557 if (ret == 0) {
558 ret = _message_iter_append_pyobject(&sub, &sub_sig_iter, value);
560 DBG("%s", "Closing DICT_ENTRY container");
561 if (!dbus_message_iter_close_container(appender, &sub)) {
562 PyErr_NoMemory();
563 ret = -1;
565 out:
566 Py_DECREF(value);
567 return ret;
570 static int
571 _message_iter_append_multi(DBusMessageIter *appender,
572 const DBusSignatureIter *sig_iter,
573 int mode, PyObject *obj)
575 DBusMessageIter sub_appender;
576 DBusSignatureIter sub_sig_iter;
577 PyObject *contents;
578 int ret;
579 PyObject *iterator = PyObject_GetIter(obj);
580 char *sig = NULL;
581 int container = mode;
582 dbus_bool_t is_byte_array = DBusPyByteArray_Check(obj);
583 int inner_type;
585 #ifdef USING_DBG
586 fprintf(stderr, "Appending multiple: ");
587 PyObject_Print(obj, stderr, 0);
588 fprintf(stderr, "\n");
589 #endif
591 if (!iterator) return -1;
592 if (mode == DBUS_TYPE_DICT_ENTRY) container = DBUS_TYPE_ARRAY;
594 DBG("Recursing signature iterator %p -> %p", sig_iter, &sub_sig_iter);
595 dbus_signature_iter_recurse(sig_iter, &sub_sig_iter);
596 #ifdef USING_DBG
598 char *s;
599 s = dbus_signature_iter_get_signature(sig_iter);
600 DBG("Signature of parent iterator %p is %s", sig_iter, s);
601 dbus_free(s);
602 s = dbus_signature_iter_get_signature(&sub_sig_iter);
603 DBG("Signature of sub-iterator %p is %s", &sub_sig_iter, s);
604 dbus_free(s);
606 #endif
607 inner_type = dbus_signature_iter_get_current_type(&sub_sig_iter);
609 if (mode == DBUS_TYPE_ARRAY || mode == DBUS_TYPE_DICT_ENTRY) {
610 sig = dbus_signature_iter_get_signature(&sub_sig_iter);
611 if (!sig) {
612 PyErr_NoMemory();
613 ret = -1;
614 goto out;
617 /* else leave sig set to NULL. */
619 DBG("Opening %c container", container);
620 if (!dbus_message_iter_open_container(appender, container,
621 sig, &sub_appender)) {
622 PyErr_NoMemory();
623 ret = -1;
624 goto out;
626 ret = 0;
627 while ((contents = PyIter_Next(iterator))) {
629 if (mode == DBUS_TYPE_ARRAY || mode == DBUS_TYPE_DICT_ENTRY) {
630 DBG("Recursing signature iterator %p -> %p", sig_iter, &sub_sig_iter);
631 dbus_signature_iter_recurse(sig_iter, &sub_sig_iter);
632 #ifdef USING_DBG
634 char *s;
635 s = dbus_signature_iter_get_signature(sig_iter);
636 DBG("Signature of parent iterator %p is %s", sig_iter, s);
637 dbus_free(s);
638 s = dbus_signature_iter_get_signature(&sub_sig_iter);
639 DBG("Signature of sub-iterator %p is %s", &sub_sig_iter, s);
640 dbus_free(s);
642 #endif
645 if (mode == DBUS_TYPE_DICT_ENTRY) {
646 ret = _message_iter_append_dictentry(&sub_appender, &sub_sig_iter,
647 obj, contents);
649 else if (mode == DBUS_TYPE_ARRAY && is_byte_array
650 && inner_type == DBUS_TYPE_VARIANT) {
651 /* Subscripting a ByteArray gives a str of length 1, but if the
652 * container is a ByteArray and the parameter is an array of
653 * variants, we want to produce an array of variants containing
654 * bytes, not strings.
656 PyObject *args = Py_BuildValue("(O)", contents);
657 PyObject *byte;
659 if (!args)
660 break;
661 byte = PyObject_Call((PyObject *)&DBusPyByte_Type, args, NULL);
662 Py_DECREF(args);
663 if (!byte)
664 break;
665 ret = _message_iter_append_variant(&sub_appender, byte);
666 Py_DECREF(byte);
668 else {
669 ret = _message_iter_append_pyobject(&sub_appender, &sub_sig_iter,
670 contents);
672 Py_DECREF(contents);
673 if (ret < 0) {
674 break;
677 if (PyErr_Occurred()) ret = -1;
678 /* This must be run as cleanup, even on failure. */
679 DBG("Closing %c container", container);
680 if (!dbus_message_iter_close_container(appender, &sub_appender)) {
681 PyErr_NoMemory();
682 ret = -1;
685 out:
686 Py_XDECREF(iterator);
687 dbus_free(sig);
688 return ret;
691 static int
692 _message_iter_append_string_as_byte_array(DBusMessageIter *appender,
693 PyObject *obj)
695 /* a bit of a faster path for byte arrays that are strings */
696 int len = PyString_GET_SIZE(obj);
697 const char *s;
698 DBusMessageIter sub;
699 int ret;
701 s = PyString_AS_STRING(obj);
702 DBG("%s", "Opening ARRAY container");
703 if (!dbus_message_iter_open_container(appender, DBUS_TYPE_ARRAY,
704 DBUS_TYPE_BYTE_AS_STRING, &sub)) {
705 PyErr_NoMemory();
706 return -1;
708 DBG("Appending fixed array of %d bytes", len);
709 if (dbus_message_iter_append_fixed_array(&sub, DBUS_TYPE_BYTE, &s, len)) {
710 ret = 0;
712 else {
713 PyErr_NoMemory();
714 ret = -1;
716 DBG("%s", "Closing ARRAY container");
717 if (!dbus_message_iter_close_container(appender, &sub)) {
718 PyErr_NoMemory();
719 return -1;
721 return ret;
724 /* Encode some Python object into a D-Bus variant slot. */
725 static int
726 _message_iter_append_variant(DBusMessageIter *appender, PyObject *obj)
728 DBusSignatureIter obj_sig_iter;
729 const char *obj_sig_str;
730 PyObject *obj_sig;
731 int ret;
732 long variant_level;
734 /* Separate the object into the contained object, and the number of
735 * variants it's wrapped in. */
736 obj_sig = _signature_string_from_pyobject(obj, &variant_level);
737 if (!obj_sig) return -1;
739 obj_sig_str = PyString_AsString(obj_sig);
740 if (!obj_sig_str) return -1;
742 if (variant_level < 1) {
743 variant_level = 1;
746 dbus_signature_iter_init(&obj_sig_iter, obj_sig_str);
748 { /* scope for variant_iters */
749 DBusMessageIter variant_iters[variant_level];
750 long i;
752 for (i = 0; i < variant_level; i++) {
753 DBusMessageIter *child = &variant_iters[i];
754 /* The first is a special case: its parent is the iter passed in
755 * to this function, instead of being the previous one in the
756 * stack
758 DBusMessageIter *parent = (i == 0
759 ? appender
760 : &(variant_iters[i-1]));
761 /* The last is also a special case: it contains the actual
762 * object, rather than another variant
764 const char *sig_str = (i == variant_level-1
765 ? obj_sig_str
766 : DBUS_TYPE_VARIANT_AS_STRING);
768 DBG("Opening VARIANT container %p inside %p containing '%s'",
769 child, parent, sig_str);
770 if (!dbus_message_iter_open_container(parent, DBUS_TYPE_VARIANT,
771 sig_str, child)) {
772 PyErr_NoMemory();
773 ret = -1;
774 goto out;
778 /* Put the object itself into the innermost variant */
779 ret = _message_iter_append_pyobject(&variant_iters[variant_level-1],
780 &obj_sig_iter, obj);
782 /* here we rely on i (and variant_level) being a signed long */
783 for (i = variant_level - 1; i >= 0; i--) {
784 DBusMessageIter *child = &variant_iters[i];
785 /* The first is a special case: its parent is the iter passed in
786 * to this function, instead of being the previous one in the
787 * stack
789 DBusMessageIter *parent = (i == 0 ? appender
790 : &(variant_iters[i-1]));
792 DBG("Closing VARIANT container %p inside %p", child, parent);
793 if (!dbus_message_iter_close_container(parent, child)) {
794 PyErr_NoMemory();
795 ret = -1;
796 goto out;
802 out:
803 Py_XDECREF(obj_sig);
804 return ret;
807 static int
808 _message_iter_append_pyobject(DBusMessageIter *appender,
809 DBusSignatureIter *sig_iter,
810 PyObject *obj)
812 int sig_type = dbus_signature_iter_get_current_type(sig_iter);
813 union {
814 dbus_bool_t b;
815 double d;
816 dbus_uint16_t uint16;
817 dbus_int16_t int16;
818 dbus_uint32_t uint32;
819 dbus_int32_t int32;
820 #if defined(DBUS_HAVE_INT64) && defined(HAVE_LONG_LONG)
821 dbus_uint64_t uint64;
822 dbus_int64_t int64;
823 #endif
824 } u;
825 int ret = -1;
827 #ifdef USING_DBG
828 fprintf(stderr, "Appending object at %p: ", obj);
829 PyObject_Print(obj, stderr, 0);
830 fprintf(stderr, " into appender at %p, dbus wants type %c\n",
831 appender, sig_type);
832 #endif
834 switch (sig_type) {
835 /* The numeric types are relatively simple to deal with, so are
836 * inlined here. */
838 case DBUS_TYPE_BOOLEAN:
839 if (PyObject_IsTrue(obj)) {
840 u.b = 1;
842 else {
843 u.b = 0;
845 DBG("Performing actual append: bool(%ld)", (long)u.b);
846 if (!dbus_message_iter_append_basic(appender, sig_type, &u.b)) {
847 PyErr_NoMemory();
848 ret = -1;
849 break;
851 ret = 0;
852 break;
854 case DBUS_TYPE_DOUBLE:
855 u.d = PyFloat_AsDouble(obj);
856 if (PyErr_Occurred()) {
857 ret = -1;
858 break;
860 DBG("Performing actual append: double(%f)", u.d);
861 if (!dbus_message_iter_append_basic(appender, sig_type, &u.d)) {
862 PyErr_NoMemory();
863 ret = -1;
864 break;
866 ret = 0;
867 break;
869 #ifdef WITH_DBUS_FLOAT32
870 case DBUS_TYPE_FLOAT:
871 u.d = PyFloat_AsDouble(obj);
872 if (PyErr_Occurred()) {
873 ret = -1;
874 break;
876 u.f = (float)u.d;
877 DBG("Performing actual append: float(%f)", u.f);
878 if (!dbus_message_iter_append_basic(appender, sig_type, &u.f)) {
879 PyErr_NoMemory();
880 ret = -1;
881 break;
883 ret = 0;
884 break;
885 #endif
887 /* The integer types are all basically the same - we delegate to
888 intNN_range_check() */
889 #define PROCESS_INTEGER(size) \
890 u.size = dbus_py_##size##_range_check(obj);\
891 if (u.size == (dbus_##size##_t)(-1) && PyErr_Occurred()) {\
892 ret = -1; \
893 break; \
895 DBG("Performing actual append: " #size "(%lld)", (long long)u.size); \
896 if (!dbus_message_iter_append_basic(appender, sig_type, &u.size)) {\
897 PyErr_NoMemory();\
898 ret = -1;\
899 break;\
901 ret = 0;
903 case DBUS_TYPE_INT16:
904 PROCESS_INTEGER(int16)
905 break;
906 case DBUS_TYPE_UINT16:
907 PROCESS_INTEGER(uint16)
908 break;
909 case DBUS_TYPE_INT32:
910 PROCESS_INTEGER(int32)
911 break;
912 case DBUS_TYPE_UINT32:
913 PROCESS_INTEGER(uint32)
914 break;
915 #if defined(DBUS_HAVE_INT64) && defined(HAVE_LONG_LONG)
916 case DBUS_TYPE_INT64:
917 PROCESS_INTEGER(int64)
918 break;
919 case DBUS_TYPE_UINT64:
920 PROCESS_INTEGER(uint64)
921 break;
922 #else
923 case DBUS_TYPE_INT64:
924 case DBUS_TYPE_UINT64:
925 PyErr_SetString(PyExc_NotImplementedError, "64-bit integer "
926 "types are not supported on this platform");
927 ret = -1;
928 break;
929 #endif
930 #undef PROCESS_INTEGER
932 /* Now the more complicated cases, which are delegated to helper
933 * functions (although in practice, the compiler will hopefully
934 * inline them anyway). */
936 case DBUS_TYPE_STRING:
937 case DBUS_TYPE_SIGNATURE:
938 case DBUS_TYPE_OBJECT_PATH:
939 ret = _message_iter_append_string(appender, sig_type, obj, TRUE);
940 break;
942 case DBUS_TYPE_BYTE:
943 ret = _message_iter_append_byte(appender, obj);
944 break;
946 case DBUS_TYPE_ARRAY:
947 /* 3 cases - it might actually be a dict, or it might be a byte array
948 * being copied from a string (for which we have a faster path),
949 * or it might be a generic array. */
951 sig_type = dbus_signature_iter_get_element_type(sig_iter);
952 if (sig_type == DBUS_TYPE_DICT_ENTRY)
953 ret = _message_iter_append_multi(appender, sig_iter,
954 DBUS_TYPE_DICT_ENTRY, obj);
955 else if (sig_type == DBUS_TYPE_BYTE && PyString_Check(obj))
956 ret = _message_iter_append_string_as_byte_array(appender, obj);
957 else
958 ret = _message_iter_append_multi(appender, sig_iter,
959 DBUS_TYPE_ARRAY, obj);
960 DBG("_message_iter_append_multi(): %d", ret);
961 break;
963 case DBUS_TYPE_STRUCT:
964 ret = _message_iter_append_multi(appender, sig_iter, sig_type, obj);
965 break;
967 case DBUS_TYPE_VARIANT:
968 ret = _message_iter_append_variant(appender, obj);
969 break;
971 case DBUS_TYPE_INVALID:
972 PyErr_SetString(PyExc_TypeError, "Fewer items found in D-Bus "
973 "signature than in Python arguments");
974 ret = -1;
975 break;
977 default:
978 PyErr_Format(PyExc_TypeError, "Unknown type '\\x%x' in D-Bus "
979 "signature", sig_type);
980 ret = -1;
981 break;
983 if (ret < 0) return -1;
985 DBG("Advancing signature iter at %p", sig_iter);
986 #ifdef USING_DBG
988 dbus_bool_t b =
989 #endif
990 dbus_signature_iter_next(sig_iter);
991 #ifdef USING_DBG
992 DBG("- result: %ld, type %02x '%c'", (long)b,
993 (int)dbus_signature_iter_get_current_type(sig_iter),
994 (int)dbus_signature_iter_get_current_type(sig_iter));
996 #endif
997 return 0;
1001 PyObject *
1002 dbus_py_Message_append(Message *self, PyObject *args, PyObject *kwargs)
1004 const char *signature = NULL;
1005 PyObject *signature_obj = NULL;
1006 DBusSignatureIter sig_iter;
1007 DBusMessageIter appender;
1008 int i;
1009 static char *argnames[] = {"signature", NULL};
1011 if (!self->msg) return DBusPy_RaiseUnusableMessage();
1013 #ifdef USING_DBG
1014 fprintf(stderr, "DBG/%ld: called Message_append(*", (long)getpid());
1015 PyObject_Print(args, stderr, 0);
1016 if (kwargs) {
1017 fprintf(stderr, ", **");
1018 PyObject_Print(kwargs, stderr, 0);
1020 fprintf(stderr, ")\n");
1021 #endif
1023 /* only use kwargs for this step: deliberately ignore args for now */
1024 if (!PyArg_ParseTupleAndKeywords(dbus_py_empty_tuple, kwargs, "|z:append",
1025 argnames, &signature)) return NULL;
1027 if (!signature) {
1028 DBG("%s", "No signature for message, guessing...");
1029 signature_obj = dbus_py_Message_guess_signature(NULL, args);
1030 if (!signature_obj) return NULL;
1031 signature = PyString_AS_STRING(signature_obj);
1033 /* from here onwards, you have to do a goto rather than returning NULL
1034 to make sure signature_obj gets freed */
1036 /* iterate over args and the signature, together */
1037 if (!dbus_signature_validate(signature, NULL)) {
1038 PyErr_SetString(PyExc_ValueError, "Corrupt type signature");
1039 goto err;
1041 dbus_signature_iter_init(&sig_iter, signature);
1042 dbus_message_iter_init_append(self->msg, &appender);
1043 for (i = 0; i < PyTuple_GET_SIZE(args); i++) {
1044 if (_message_iter_append_pyobject(&appender, &sig_iter,
1045 PyTuple_GET_ITEM(args, i)) < 0) {
1046 goto hosed;
1049 if (dbus_signature_iter_get_current_type(&sig_iter)
1050 != DBUS_TYPE_INVALID) {
1051 PyErr_SetString(PyExc_TypeError, "More items found in D-Bus "
1052 "signature than in Python arguments");
1053 goto hosed;
1056 /* success! */
1057 Py_XDECREF(signature_obj);
1058 Py_RETURN_NONE;
1060 hosed:
1061 /* "If appending any of the arguments fails due to lack of memory,
1062 * generally the message is hosed and you have to start over" -libdbus docs
1063 * Enforce this by throwing away the message structure.
1065 dbus_message_unref(self->msg);
1066 self->msg = NULL;
1067 err:
1068 Py_XDECREF(signature_obj);
1069 return NULL;
1072 /* vim:set ft=c cino< sw=4 sts=4 et: */