Remove old libxml2-based introspection parser
[dbus-python-phuang.git] / _dbus_bindings / message-append.c
bloba98c42d8d03c6c0229fdea4846482e1e8b62023f
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 "variant Variant\n"
74 " any object (guess type as below)\n"
75 "string, signature, object path str (must be UTF-8) or unicode\n"
76 "dict (a{...}) any mapping\n"
77 "array (a...) any iterable over appropriate objects\n"
78 "struct ((...)) any iterable over appropriate objects\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 "D-Bus type, variant_level == 0 the corresponding type\n"
101 "bool boolean (y)\n"
102 "any other int subclass int32 (i) (FIXME: make this error?)\n"
103 "any other long subclass int64 (x) (FIXME: make this error?)\n"
104 "any other float subclass double (d)\n"
105 "any other str subclass string (s)\n"
106 "any other unicode subclass string (s)\n"
107 "any other tuple subclass struct ((...)), guess contents' types\n"
108 "any other list subclass array (a...), guess contents' type\n"
109 " according to type of first item\n"
110 "any other dict subclass dict (a{...}), guess key, value type\n"
111 " according to types for an arbitrary item\n"
112 "anything else raise TypeError\n"
113 "=============================== ===========================\n"
116 /* Return a new reference. If the object is a variant and variant_level_ptr
117 * is not NULL, put the variant level in the variable pointed to, and
118 * return the contained type instead of "v". */
119 static PyObject *
120 _signature_string_from_pyobject(PyObject *obj, long *variant_level_ptr)
122 long variant_level = get_variant_level(obj);
123 if (variant_level_ptr) {
124 *variant_level_ptr = variant_level;
126 else if (variant_level > 0) {
127 return PyString_FromString(DBUS_TYPE_VARIANT_AS_STRING);
130 /* Ordering is important: some of these are subclasses of each other. */
131 if (obj == Py_True || obj == Py_False) {
132 return PyString_FromString(DBUS_TYPE_BOOLEAN_AS_STRING);
134 else if (PyInt_Check(obj)) {
135 if (DBusPyInt16_Check(obj))
136 return PyString_FromString(DBUS_TYPE_INT16_AS_STRING);
137 else if (DBusPyInt32_Check(obj))
138 return PyString_FromString(DBUS_TYPE_INT32_AS_STRING);
139 else if (DBusPyByte_Check(obj))
140 return PyString_FromString(DBUS_TYPE_BYTE_AS_STRING);
141 else if (DBusPyUInt16_Check(obj))
142 return PyString_FromString(DBUS_TYPE_UINT16_AS_STRING);
143 else if (DBusPyBoolean_Check(obj))
144 return PyString_FromString(DBUS_TYPE_BOOLEAN_AS_STRING);
145 else
146 return PyString_FromString(DBUS_TYPE_INT32_AS_STRING);
148 else if (PyLong_Check(obj)) {
149 if (DBusPyInt64_Check(obj))
150 return PyString_FromString(DBUS_TYPE_INT64_AS_STRING);
151 else if (DBusPyUInt32_Check(obj))
152 return PyString_FromString(DBUS_TYPE_UINT32_AS_STRING);
153 else if (DBusPyUInt64_Check(obj))
154 return PyString_FromString(DBUS_TYPE_UINT64_AS_STRING);
155 else
156 return PyString_FromString(DBUS_TYPE_INT64_AS_STRING);
158 else if (PyUnicode_Check(obj))
159 return PyString_FromString(DBUS_TYPE_STRING_AS_STRING);
160 else if (PyFloat_Check(obj)) {
161 #ifdef WITH_DBUS_FLOAT32
162 if (DBusPyDouble_Check(obj))
163 return PyString_FromString(DBUS_TYPE_DOUBLE_AS_STRING);
164 else if (DBusPyFloat_Check(obj))
165 return PyString_FromString(DBUS_TYPE_FLOAT_AS_STRING);
166 else
167 #endif
168 return PyString_FromString(DBUS_TYPE_DOUBLE_AS_STRING);
170 else if (PyString_Check(obj)) {
171 if (DBusPyObjectPath_Check(obj))
172 return PyString_FromString(DBUS_TYPE_OBJECT_PATH_AS_STRING);
173 else if (DBusPySignature_Check(obj))
174 return PyString_FromString(DBUS_TYPE_SIGNATURE_AS_STRING);
175 else if (DBusPyByteArray_Check(obj))
176 return PyString_FromString(DBUS_TYPE_ARRAY_AS_STRING
177 DBUS_TYPE_BYTE_AS_STRING);
178 else
179 return PyString_FromString(DBUS_TYPE_STRING_AS_STRING);
181 else if (PyTuple_Check(obj)) {
182 int len = PyTuple_GET_SIZE(obj);
183 PyObject *list = PyList_New(len + 2); /* new ref */
184 PyObject *item; /* temporary new ref */
185 PyObject *empty_str; /* temporary new ref */
186 PyObject *ret;
187 int i;
189 if (!list) return NULL;
190 if (len == 0) {
191 PyErr_SetString(PyExc_ValueError, "D-Bus structs cannot be empty");
192 Py_DECREF(list);
193 return NULL;
195 /* Set the first and last elements of list to be the parentheses */
196 item = PyString_FromString(DBUS_STRUCT_BEGIN_CHAR_AS_STRING);
197 if (PyList_SetItem(list, 0, item) < 0) {
198 Py_DECREF(list);
199 return NULL;
201 item = PyString_FromString(DBUS_STRUCT_END_CHAR_AS_STRING);
202 if (PyList_SetItem(list, len + 1, item) < 0) {
203 Py_DECREF(list);
204 return NULL;
206 if (!item || !PyList_GET_ITEM(list, 0)) {
207 Py_DECREF(list);
208 return NULL;
210 item = NULL;
212 for (i = 0; i < len; i++) {
213 item = PyTuple_GetItem(obj, i);
214 if (!item) {
215 Py_DECREF(list);
216 return NULL;
218 item = _signature_string_from_pyobject(item, NULL);
219 if (!item) {
220 Py_DECREF(list);
221 return NULL;
223 if (PyList_SetItem(list, i + 1, item) < 0) {
224 Py_DECREF(list);
225 return NULL;
227 item = NULL;
229 empty_str = PyString_FromString("");
230 if (!empty_str) {
231 /* really shouldn't happen */
232 Py_DECREF(list);
233 return NULL;
235 ret = PyObject_CallMethod(empty_str, "join", "(O)", list); /* new ref */
236 /* whether ret is NULL or not, */
237 Py_DECREF(empty_str);
238 Py_DECREF(list);
239 return ret;
241 else if (PyList_Check(obj)) {
242 PyObject *tmp;
243 PyObject *ret = PyString_FromString(DBUS_TYPE_ARRAY_AS_STRING);
244 if (!ret) return NULL;
245 if (DBusPyArray_Check(obj) && PyString_Check(((DBusPyArray *)obj)->signature)) {
246 PyString_Concat(&ret, ((DBusPyArray *)obj)->signature);
247 return ret;
249 if (PyList_GET_SIZE(obj) == 0) {
250 /* No items, so fail. Or should we guess "av"? */
251 PyErr_SetString(PyExc_ValueError, "Unable to guess signature "
252 "from an empty list");
253 return NULL;
255 tmp = PyList_GetItem(obj, 0);
256 tmp = _signature_string_from_pyobject(tmp, NULL);
257 if (!tmp) return NULL;
258 PyString_ConcatAndDel(&ret, tmp);
259 return ret;
261 else if (PyDict_Check(obj)) {
262 PyObject *key, *value, *keysig, *valuesig;
263 int pos = 0;
264 PyObject *ret = NULL;
266 if (DBusPyDict_Check(obj) && PyString_Check(((DBusPyDict *)obj)->signature)) {
267 const char *sig = PyString_AS_STRING(((DBusPyDict *)obj)->signature);
269 return PyString_FromFormat((DBUS_TYPE_ARRAY_AS_STRING
270 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
271 "%s"
272 DBUS_DICT_ENTRY_END_CHAR_AS_STRING),
273 sig);
275 if (!PyDict_Next(obj, &pos, &key, &value)) {
276 /* No items, so fail. Or should we guess "a{vv}"? */
277 PyErr_SetString(PyExc_ValueError, "Unable to guess signature "
278 "from an empty dict");
279 return NULL;
281 keysig = _signature_string_from_pyobject(key, NULL);
282 valuesig = _signature_string_from_pyobject(value, NULL);
283 if (keysig && valuesig) {
284 ret = PyString_FromFormat((DBUS_TYPE_ARRAY_AS_STRING
285 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
286 "%s%s"
287 DBUS_DICT_ENTRY_END_CHAR_AS_STRING),
288 PyString_AS_STRING(keysig),
289 PyString_AS_STRING(valuesig));
291 Py_XDECREF(keysig);
292 Py_XDECREF(valuesig);
293 return ret;
295 else {
296 PyErr_Format(PyExc_TypeError, "Don't know how which D-Bus type "
297 "to use to encode type \"%s\"",
298 obj->ob_type->tp_name);
299 return NULL;
303 PyObject *
304 dbus_py_Message_guess_signature(PyObject *unused UNUSED, PyObject *args)
306 PyObject *tmp, *ret = NULL;
308 if (!args) {
309 if (!PyErr_Occurred()) {
310 PyErr_BadInternalCall();
312 return NULL;
315 #ifdef USING_DBG
316 fprintf(stderr, "DBG/%ld: called Message_guess_signature", (long)getpid());
317 PyObject_Print(args, stderr, 0);
318 fprintf(stderr, "\n");
319 #endif
321 if (!PyTuple_Check(args)) {
322 DBG("%s", "Message_guess_signature: args not a tuple");
323 PyErr_BadInternalCall();
324 return NULL;
327 /* if there were no args, easy */
328 if (PyTuple_GET_SIZE(args) == 0) {
329 DBG("%s", "Message_guess_signature: no args, so return Signature('')");
330 return PyObject_CallFunction((PyObject *)&DBusPySignature_Type, "(s)", "");
333 /* if there were args, the signature we want is, by construction,
334 * exactly the signature we get for the tuple args, except that we don't
335 * want the parentheses. */
336 tmp = _signature_string_from_pyobject(args, NULL);
337 if (!tmp) {
338 DBG("%s", "Message_guess_signature: failed");
339 return NULL;
341 if (!PyString_Check(tmp) || PyString_GET_SIZE(tmp) < 2) {
342 PyErr_SetString(PyExc_RuntimeError, "Internal error: "
343 "_signature_string_from_pyobject returned "
344 "a bad result");
345 Py_DECREF(tmp);
346 return NULL;
348 ret = PyObject_CallFunction((PyObject *)&DBusPySignature_Type, "(s#)",
349 PyString_AS_STRING(tmp) + 1,
350 PyString_GET_SIZE(tmp) - 2);
351 DBG("Message_guess_signature: returning Signature at %p \"%s\"", ret,
352 ret ? PyString_AS_STRING(ret) : "(NULL)");
353 Py_DECREF(tmp);
354 return ret;
357 static int _message_iter_append_pyobject(DBusMessageIter *appender,
358 DBusSignatureIter *sig_iter,
359 PyObject *obj);
361 static int
362 _message_iter_append_string(DBusMessageIter *appender,
363 int sig_type, PyObject *obj)
365 char *s;
367 if (PyString_Check(obj)) {
368 PyObject *unicode;
370 /* Raise TypeError if the string has embedded NULs */
371 if (PyString_AsStringAndSize(obj, &s, NULL) < 0) return -1;
372 /* Surely there's a faster stdlib way to validate UTF-8... */
373 unicode = PyUnicode_DecodeUTF8(s, PyString_GET_SIZE(obj), NULL);
374 if (!unicode) {
375 PyErr_SetString(PyExc_UnicodeError, "String parameters "
376 "to be sent over D-Bus must be valid UTF-8");
377 return -1;
379 Py_DECREF(unicode);
380 unicode = NULL;
382 DBG("Performing actual append: string %s", s);
383 if (!dbus_message_iter_append_basic(appender, sig_type,
384 &s)) {
385 PyErr_NoMemory();
386 return -1;
389 else if (PyUnicode_Check(obj)) {
390 PyObject *utf8 = PyUnicode_AsUTF8String(obj);
391 if (!utf8) return -1;
392 /* Raise TypeError if the string has embedded NULs */
393 if (PyString_AsStringAndSize(utf8, &s, NULL) < 0) return -1;
394 DBG("Performing actual append: string (from unicode) %s", s);
395 if (!dbus_message_iter_append_basic(appender, sig_type, &s)) {
396 PyErr_NoMemory();
397 return -1;
399 Py_DECREF(utf8);
401 else {
402 PyErr_SetString(PyExc_TypeError,
403 "Expected a string or unicode object");
404 return -1;
406 return 0;
409 static int
410 _message_iter_append_byte(DBusMessageIter *appender, PyObject *obj)
412 unsigned char y;
414 if (PyString_Check(obj)) {
415 if (PyString_GET_SIZE(obj) != 1) {
416 PyErr_Format(PyExc_ValueError, "Expected a string of "
417 "length 1 byte, but found %d bytes",
418 PyString_GET_SIZE(obj));
419 return -1;
421 y = *(unsigned char *)PyString_AS_STRING(obj);
423 else {
424 long i = PyInt_AsLong(obj);
426 if (i == -1 && PyErr_Occurred()) return -1;
427 if (i < 0 || i > 0xff) {
428 PyErr_Format(PyExc_ValueError, "%d outside range for a "
429 "byte value", (int)i);
430 return -1;
432 y = i;
434 DBG("Performing actual append: byte (from unicode) \\x%02x", (unsigned)y);
435 if (!dbus_message_iter_append_basic(appender, DBUS_TYPE_BYTE, &y)) {
436 PyErr_NoMemory();
437 return -1;
439 return 0;
442 static int
443 _message_iter_append_dictentry(DBusMessageIter *appender,
444 DBusSignatureIter *sig_iter,
445 PyObject *dict, PyObject *key)
447 DBusSignatureIter sub_sig_iter;
448 DBusMessageIter sub;
449 int ret = -1;
450 PyObject *value = PyObject_GetItem(dict, key);
452 if (!value) return -1;
454 #ifdef USING_DBG
455 fprintf(stderr, "Append dictentry: ");
456 PyObject_Print(key, stderr, 0);
457 fprintf(stderr, " => ");
458 PyObject_Print(value, stderr, 0);
459 fprintf(stderr, "\n");
460 #endif
462 DBG("Recursing signature iterator %p -> %p", sig_iter, &sub_sig_iter);
463 dbus_signature_iter_recurse(sig_iter, &sub_sig_iter);
464 #ifdef USING_DBG
466 char *s;
467 s = dbus_signature_iter_get_signature(sig_iter);
468 DBG("Signature of parent iterator %p is %s", sig_iter, s);
469 dbus_free(s);
470 s = dbus_signature_iter_get_signature(&sub_sig_iter);
471 DBG("Signature of sub-iterator %p is %s", &sub_sig_iter, s);
472 dbus_free(s);
474 #endif
476 DBG("%s", "Opening DICT_ENTRY container");
477 if (!dbus_message_iter_open_container(appender, DBUS_TYPE_DICT_ENTRY,
478 NULL, &sub)) {
479 PyErr_NoMemory();
480 goto out;
482 ret = _message_iter_append_pyobject(&sub, &sub_sig_iter, key);
483 if (ret == 0) {
484 ret = _message_iter_append_pyobject(&sub, &sub_sig_iter, value);
486 DBG("%s", "Closing DICT_ENTRY container");
487 if (!dbus_message_iter_close_container(appender, &sub)) {
488 PyErr_NoMemory();
489 ret = -1;
491 out:
492 Py_DECREF(value);
493 return ret;
496 static int
497 _message_iter_append_multi(DBusMessageIter *appender,
498 const DBusSignatureIter *sig_iter,
499 int mode, PyObject *obj)
501 DBusMessageIter sub_appender;
502 DBusSignatureIter sub_sig_iter;
503 PyObject *contents;
504 int ret;
505 PyObject *iterator = PyObject_GetIter(obj);
506 char *sig = NULL;
507 int container = mode;
509 #ifdef USING_DBG
510 fprintf(stderr, "Appending multiple: ");
511 PyObject_Print(obj, stderr, 0);
512 fprintf(stderr, "\n");
513 #endif
515 if (!iterator) return -1;
516 if (mode == DBUS_TYPE_DICT_ENTRY) container = DBUS_TYPE_ARRAY;
518 DBG("Recursing signature iterator %p -> %p", sig_iter, &sub_sig_iter);
519 dbus_signature_iter_recurse(sig_iter, &sub_sig_iter);
520 #ifdef USING_DBG
522 char *s;
523 s = dbus_signature_iter_get_signature(sig_iter);
524 DBG("Signature of parent iterator %p is %s", sig_iter, s);
525 dbus_free(s);
526 s = dbus_signature_iter_get_signature(&sub_sig_iter);
527 DBG("Signature of sub-iterator %p is %s", &sub_sig_iter, s);
528 dbus_free(s);
530 #endif
532 if (mode == DBUS_TYPE_ARRAY || mode == DBUS_TYPE_DICT_ENTRY) {
533 sig = dbus_signature_iter_get_signature(&sub_sig_iter);
534 if (!sig) {
535 PyErr_NoMemory();
536 ret = -1;
537 goto out;
540 /* else leave sig set to NULL. */
542 DBG("Opening %c container", container);
543 if (!dbus_message_iter_open_container(appender, container,
544 sig, &sub_appender)) {
545 PyErr_NoMemory();
546 ret = -1;
547 goto out;
549 ret = 0;
550 while ((contents = PyIter_Next(iterator))) {
552 if (mode == DBUS_TYPE_ARRAY || mode == DBUS_TYPE_DICT_ENTRY) {
553 DBG("Recursing signature iterator %p -> %p", sig_iter, &sub_sig_iter);
554 dbus_signature_iter_recurse(sig_iter, &sub_sig_iter);
555 #ifdef USING_DBG
557 char *s;
558 s = dbus_signature_iter_get_signature(sig_iter);
559 DBG("Signature of parent iterator %p is %s", sig_iter, s);
560 dbus_free(s);
561 s = dbus_signature_iter_get_signature(&sub_sig_iter);
562 DBG("Signature of sub-iterator %p is %s", &sub_sig_iter, s);
563 dbus_free(s);
565 #endif
568 if (mode == DBUS_TYPE_DICT_ENTRY) {
569 ret = _message_iter_append_dictentry(&sub_appender, &sub_sig_iter,
570 obj, contents);
572 else {
573 ret = _message_iter_append_pyobject(&sub_appender, &sub_sig_iter,
574 contents);
576 Py_DECREF(contents);
577 if (ret < 0) {
578 break;
581 if (PyErr_Occurred()) ret = -1;
582 /* This must be run as cleanup, even on failure. */
583 DBG("Closing %c container", container);
584 if (!dbus_message_iter_close_container(appender, &sub_appender)) {
585 PyErr_NoMemory();
586 ret = -1;
589 out:
590 Py_XDECREF(iterator);
591 dbus_free(sig);
592 return ret;
595 static int
596 _message_iter_append_string_as_byte_array(DBusMessageIter *appender,
597 PyObject *obj)
599 /* a bit of a faster path for byte arrays that are strings */
600 int len = PyString_GET_SIZE(obj);
601 const char *s;
602 DBusMessageIter sub;
603 int ret;
605 s = PyString_AS_STRING(obj);
606 DBG("%s", "Opening ARRAY container");
607 if (!dbus_message_iter_open_container(appender, DBUS_TYPE_ARRAY,
608 DBUS_TYPE_BYTE_AS_STRING, &sub)) {
609 PyErr_NoMemory();
610 return -1;
612 DBG("Appending fixed array of %d bytes", len);
613 if (dbus_message_iter_append_fixed_array(&sub, DBUS_TYPE_BYTE, &s, len)) {
614 ret = 0;
616 else {
617 PyErr_NoMemory();
618 ret = -1;
620 DBG("%s", "Closing ARRAY container");
621 if (!dbus_message_iter_close_container(appender, &sub)) {
622 PyErr_NoMemory();
623 return -1;
625 return ret;
628 /* Encode some Python object into a D-Bus variant slot. */
629 static int
630 _message_iter_append_variant(DBusMessageIter *appender, PyObject *obj)
632 DBusSignatureIter obj_sig_iter;
633 const char *obj_sig_str;
634 PyObject *obj_sig;
635 int ret;
636 long variant_level;
638 /* Separate the object into the contained object, and the number of
639 * variants it's wrapped in. */
640 obj_sig = _signature_string_from_pyobject(obj, &variant_level);
641 if (!obj_sig) return -1;
643 obj_sig_str = PyString_AsString(obj_sig);
644 if (!obj_sig_str) return -1;
646 if (variant_level < 1) {
647 variant_level = 1;
650 dbus_signature_iter_init(&obj_sig_iter, obj_sig_str);
652 { /* scope for variant_iters */
653 DBusMessageIter variant_iters[variant_level];
654 long i;
656 for (i = 0; i < variant_level; i++) {
657 DBusMessageIter *child = &variant_iters[i];
658 /* The first is a special case: its parent is the iter passed in
659 * to this function, instead of being the previous one in the
660 * stack
662 DBusMessageIter *parent = (i == 0
663 ? appender
664 : &(variant_iters[i-1]));
665 /* The last is also a special case: it contains the actual
666 * object, rather than another variant
668 const char *sig_str = (i == variant_level-1
669 ? obj_sig_str
670 : DBUS_TYPE_VARIANT_AS_STRING);
672 DBG("Opening VARIANT container %p inside %p containing '%s'",
673 child, parent, sig_str);
674 if (!dbus_message_iter_open_container(parent, DBUS_TYPE_VARIANT,
675 sig_str, child)) {
676 PyErr_NoMemory();
677 ret = -1;
678 goto out;
682 /* Put the object itself into the innermost variant */
683 ret = _message_iter_append_pyobject(&variant_iters[variant_level-1],
684 &obj_sig_iter, obj);
686 /* here we rely on i (and variant_level) being a signed long */
687 for (i = variant_level - 1; i >= 0; i--) {
688 DBusMessageIter *child = &variant_iters[i];
689 /* The first is a special case: its parent is the iter passed in
690 * to this function, instead of being the previous one in the
691 * stack
693 DBusMessageIter *parent = (i == 0 ? appender
694 : &(variant_iters[i-1]));
696 DBG("Closing VARIANT container %p inside %p", child, parent);
697 if (!dbus_message_iter_close_container(parent, child)) {
698 PyErr_NoMemory();
699 ret = -1;
700 goto out;
706 out:
707 Py_XDECREF(obj_sig);
708 return ret;
711 static int
712 _message_iter_append_pyobject(DBusMessageIter *appender,
713 DBusSignatureIter *sig_iter,
714 PyObject *obj)
716 int sig_type = dbus_signature_iter_get_current_type(sig_iter);
717 union {
718 dbus_bool_t b;
719 double d;
720 dbus_uint16_t uint16;
721 dbus_int16_t int16;
722 dbus_uint32_t uint32;
723 dbus_int32_t int32;
724 #if defined(DBUS_HAVE_INT64) && defined(HAVE_LONG_LONG)
725 dbus_uint64_t uint64;
726 dbus_int64_t int64;
727 #endif
728 } u;
729 int ret = -1;
731 #ifdef USING_DBG
732 fprintf(stderr, "Appending object at %p: ", obj);
733 PyObject_Print(obj, stderr, 0);
734 fprintf(stderr, " into appender at %p, dbus wants type %c\n",
735 appender, sig_type);
736 #endif
738 switch (sig_type) {
739 /* The numeric types are relatively simple to deal with, so are
740 * inlined here. */
742 case DBUS_TYPE_BOOLEAN:
743 if (PyObject_IsTrue(obj)) {
744 u.b = 1;
746 else {
747 u.b = 0;
749 DBG("Performing actual append: bool(%ld)", (long)u.b);
750 if (!dbus_message_iter_append_basic(appender, sig_type, &u.b)) {
751 PyErr_NoMemory();
752 ret = -1;
753 break;
755 ret = 0;
756 break;
758 case DBUS_TYPE_DOUBLE:
759 u.d = PyFloat_AsDouble(obj);
760 if (PyErr_Occurred()) {
761 ret = -1;
762 break;
764 DBG("Performing actual append: double(%f)", u.d);
765 if (!dbus_message_iter_append_basic(appender, sig_type, &u.d)) {
766 PyErr_NoMemory();
767 ret = -1;
768 break;
770 ret = 0;
771 break;
773 #ifdef WITH_DBUS_FLOAT32
774 case DBUS_TYPE_FLOAT:
775 u.d = PyFloat_AsDouble(obj);
776 if (PyErr_Occurred()) {
777 ret = -1;
778 break;
780 u.f = (float)u.d;
781 DBG("Performing actual append: float(%f)", u.f);
782 if (!dbus_message_iter_append_basic(appender, sig_type, &u.f)) {
783 PyErr_NoMemory();
784 ret = -1;
785 break;
787 ret = 0;
788 break;
789 #endif
791 /* The integer types are all basically the same - we delegate to
792 intNN_range_check() */
793 #define PROCESS_INTEGER(size) \
794 u.size = dbus_py_##size##_range_check(obj);\
795 if (u.size == (dbus_##size##_t)(-1) && PyErr_Occurred()) {\
796 ret = -1; \
797 break; \
799 DBG("Performing actual append: " #size "(%lld)", (long long)u.size); \
800 if (!dbus_message_iter_append_basic(appender, sig_type, &u.size)) {\
801 PyErr_NoMemory();\
802 ret = -1;\
803 break;\
805 ret = 0;
807 case DBUS_TYPE_INT16:
808 PROCESS_INTEGER(int16)
809 break;
810 case DBUS_TYPE_UINT16:
811 PROCESS_INTEGER(uint16)
812 break;
813 case DBUS_TYPE_INT32:
814 PROCESS_INTEGER(int32)
815 break;
816 case DBUS_TYPE_UINT32:
817 PROCESS_INTEGER(uint32)
818 break;
819 #if defined(DBUS_HAVE_INT64) && defined(HAVE_LONG_LONG)
820 case DBUS_TYPE_INT64:
821 PROCESS_INTEGER(int64)
822 break;
823 case DBUS_TYPE_UINT64:
824 PROCESS_INTEGER(uint64)
825 break;
826 #else
827 case DBUS_TYPE_INT64:
828 case DBUS_TYPE_UINT64:
829 PyErr_SetString(PyExc_NotImplementedError, "64-bit integer "
830 "types are not supported on this platform");
831 ret = -1;
832 break;
833 #endif
834 #undef PROCESS_INTEGER
836 /* Now the more complicated cases, which are delegated to helper
837 * functions (although in practice, the compiler will hopefully
838 * inline them anyway). */
840 case DBUS_TYPE_STRING:
841 case DBUS_TYPE_SIGNATURE:
842 case DBUS_TYPE_OBJECT_PATH:
843 ret = _message_iter_append_string(appender, sig_type, obj);
844 break;
846 case DBUS_TYPE_BYTE:
847 ret = _message_iter_append_byte(appender, obj);
848 break;
850 case DBUS_TYPE_ARRAY:
851 /* 3 cases - it might actually be a dict, or it might be a byte array
852 * being copied from a string (for which we have a faster path),
853 * or it might be a generic array. */
855 sig_type = dbus_signature_iter_get_element_type(sig_iter);
856 if (sig_type == DBUS_TYPE_DICT_ENTRY)
857 ret = _message_iter_append_multi(appender, sig_iter,
858 DBUS_TYPE_DICT_ENTRY, obj);
859 else if (sig_type == DBUS_TYPE_BYTE && PyString_Check(obj))
860 ret = _message_iter_append_string_as_byte_array(appender, obj);
861 else
862 ret = _message_iter_append_multi(appender, sig_iter,
863 DBUS_TYPE_ARRAY, obj);
864 DBG("_message_iter_append_multi(): %d", ret);
865 break;
867 case DBUS_TYPE_STRUCT:
868 ret = _message_iter_append_multi(appender, sig_iter, sig_type, obj);
869 break;
871 case DBUS_TYPE_VARIANT:
872 ret = _message_iter_append_variant(appender, obj);
873 break;
875 case DBUS_TYPE_INVALID:
876 PyErr_SetString(PyExc_TypeError, "Fewer items found in D-Bus "
877 "signature than in Python arguments");
878 ret = -1;
879 break;
881 default:
882 PyErr_Format(PyExc_TypeError, "Unknown type '\\x%x' in D-Bus "
883 "signature", sig_type);
884 ret = -1;
885 break;
887 if (ret < 0) return -1;
889 DBG("Advancing signature iter at %p", sig_iter);
890 #ifdef USING_DBG
892 dbus_bool_t b =
893 #endif
894 dbus_signature_iter_next(sig_iter);
895 #ifdef USING_DBG
896 DBG("- result: %ld, type %02x '%c'", (long)b,
897 (int)dbus_signature_iter_get_current_type(sig_iter),
898 (int)dbus_signature_iter_get_current_type(sig_iter));
900 #endif
901 return 0;
905 PyObject *
906 dbus_py_Message_append(Message *self, PyObject *args, PyObject *kwargs)
908 const char *signature = NULL;
909 PyObject *signature_obj = NULL;
910 DBusSignatureIter sig_iter;
911 DBusMessageIter appender;
912 int i;
913 static char *argnames[] = {"signature", NULL};
915 if (!self->msg) return DBusPy_RaiseUnusableMessage();
917 #ifdef USING_DBG
918 fprintf(stderr, "DBG/%ld: called Message_append(*", (long)getpid());
919 PyObject_Print(args, stderr, 0);
920 if (kwargs) {
921 fprintf(stderr, ", **");
922 PyObject_Print(kwargs, stderr, 0);
924 fprintf(stderr, ")\n");
925 #endif
927 /* only use kwargs for this step: deliberately ignore args for now */
928 if (!PyArg_ParseTupleAndKeywords(dbus_py_empty_tuple, kwargs, "|z:append",
929 argnames, &signature)) return NULL;
931 if (!signature) {
932 DBG("%s", "No signature for message, guessing...");
933 signature_obj = dbus_py_Message_guess_signature(NULL, args);
934 if (!signature_obj) return NULL;
935 signature = PyString_AS_STRING(signature_obj);
937 /* from here onwards, you have to do a goto rather than returning NULL
938 to make sure signature_obj gets freed */
940 /* iterate over args and the signature, together */
941 if (!dbus_signature_validate(signature, NULL)) {
942 PyErr_SetString(PyExc_ValueError, "Corrupt type signature");
943 goto err;
945 dbus_signature_iter_init(&sig_iter, signature);
946 dbus_message_iter_init_append(self->msg, &appender);
947 for (i = 0; i < PyTuple_GET_SIZE(args); i++) {
948 if (_message_iter_append_pyobject(&appender, &sig_iter,
949 PyTuple_GET_ITEM(args, i)) < 0) {
950 goto hosed;
953 if (dbus_signature_iter_get_current_type(&sig_iter)
954 != DBUS_TYPE_INVALID) {
955 PyErr_SetString(PyExc_TypeError, "More items found in D-Bus "
956 "signature than in Python arguments");
957 goto hosed;
960 /* success! */
961 Py_XDECREF(signature_obj);
962 Py_RETURN_NONE;
964 hosed:
965 /* "If appending any of the arguments fails due to lack of memory,
966 * generally the message is hosed and you have to start over" -libdbus docs
967 * Enforce this by throwing away the message structure.
969 dbus_message_unref(self->msg);
970 self->msg = NULL;
971 err:
972 Py_XDECREF(signature_obj);
973 return NULL;
976 /* vim:set ft=c cino< sw=4 sts=4 et: */