_dbus_bindings: debug-impl.h -> debug.c
[dbus-python-phuang.git] / _dbus_bindings / message-append-impl.h
blob8367f65567cf4da1c06ea15a648bc50792ade845
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 /* Return the number of variants wrapping the given object. Return 0
27 * if the object is not a D-Bus type.
29 static long
30 get_variant_level(PyObject *obj)
32 if (DBusPythonInt_Check(obj)) {
33 return ((DBusPythonInt *)obj)->variant_level;
35 else if (DBusPythonFloat_Check(obj)) {
36 return ((DBusPythonFloat *)obj)->variant_level;
38 else if (Array_Check(obj)) {
39 return ((Array *)obj)->variant_level;
41 else if (Dict_Check(obj)) {
42 return ((Dict *)obj)->variant_level;
44 else if (DBusPythonLong_Check(obj) ||
45 DBusPythonString_Check(obj) ||
46 String_Check(obj) ||
47 Struct_Check(obj)) {
48 return PyInt_AsLong(PyObject_GetAttr(obj, variant_level_const));
50 else {
51 return 0;
55 PyDoc_STRVAR(Message_append__doc__,
56 "set_args(*args[, **kwargs])\n\n"
57 "Set the message's arguments from the positional parameter, according to\n"
58 "the signature given by the ``signature`` keyword parameter.\n"
59 "\n"
60 "The following type conversions are supported:\n\n"
61 "=============================== ===========================\n"
62 "D-Bus (in signature) Python\n"
63 "=============================== ===========================\n"
64 "boolean (b) any object (via bool())\n"
65 "byte (y) string of length 1\n"
66 " any integer\n"
67 "any integer type any integer\n"
68 "double (d) any float\n"
69 "variant Variant\n"
70 " any object (guess type as below)\n"
71 "string, signature, object path str (must be UTF-8) or unicode\n"
72 "dict (a{...}) any mapping\n"
73 "array (a...) any iterable over appropriate objects\n"
74 "struct ((...)) any iterable over appropriate objects\n"
75 "=============================== ===========================\n"
76 "\n"
77 "Here 'any integer' means anything on which int() or long()\n"
78 "(as appropriate) will work, except for basestring subclasses.\n"
79 "'Any float' means anything on which float() will work, except\n"
80 "for basestring subclasses.\n"
81 "\n"
82 "If there is no signature, guess from the arguments using\n"
83 "the static method `Message.guess_signature`.\n"
86 PyDoc_STRVAR(Message_guess_signature__doc__,
87 "guess_signature(*args) -> Signature [static method]\n\n"
88 "Guess a D-Bus signature which should be used to encode the given\n"
89 "Python objects.\n"
90 "\n"
91 "The signature is constructed as follows:\n\n"
92 "=============================== ===========================\n"
93 "Python D-Bus\n"
94 "=============================== ===========================\n"
95 "D-Bus type, variant_level > 0 variant (v)\n"
96 "D-Bus type, variant_level == 0 the corresponding type\n"
97 "bool boolean (y)\n"
98 "any other int subclass int32 (i) (FIXME: make this error?)\n"
99 "any other long subclass int64 (x) (FIXME: make this error?)\n"
100 "any other float subclass double (d)\n"
101 "any other str subclass string (s)\n"
102 "any other unicode subclass string (s)\n"
103 "any other tuple subclass struct ((...)), guess contents' types\n"
104 "any other list subclass array (a...), guess contents' type\n"
105 " according to type of first item\n"
106 "any other dict subclass dict (a{...}), guess key, value type\n"
107 " according to types for an arbitrary item\n"
108 "anything else raise TypeError\n"
109 "=============================== ===========================\n"
112 /* Return a new reference. If the object is a variant and variant_level_ptr
113 * is not NULL, put the variant level in the variable pointed to, and
114 * return the contained type instead of "v". */
115 static PyObject *
116 _signature_string_from_pyobject(PyObject *obj, long *variant_level_ptr)
118 long variant_level = get_variant_level(obj);
119 if (variant_level_ptr) {
120 *variant_level_ptr = variant_level;
122 else if (variant_level > 0) {
123 return PyString_FromString(DBUS_TYPE_VARIANT_AS_STRING);
126 /* Ordering is important: some of these are subclasses of each other. */
127 if (obj == Py_True || obj == Py_False) {
128 return PyString_FromString(DBUS_TYPE_BOOLEAN_AS_STRING);
130 else if (PyInt_Check(obj)) {
131 if (Int16_Check(obj))
132 return PyString_FromString(DBUS_TYPE_INT16_AS_STRING);
133 else if (Int32_Check(obj))
134 return PyString_FromString(DBUS_TYPE_INT32_AS_STRING);
135 else if (Byte_Check(obj))
136 return PyString_FromString(DBUS_TYPE_BYTE_AS_STRING);
137 else if (UInt16_Check(obj))
138 return PyString_FromString(DBUS_TYPE_UINT16_AS_STRING);
139 else if (Boolean_Check(obj))
140 return PyString_FromString(DBUS_TYPE_BOOLEAN_AS_STRING);
141 else
142 return PyString_FromString(DBUS_TYPE_INT32_AS_STRING);
144 else if (PyLong_Check(obj)) {
145 if (Int64_Check(obj))
146 return PyString_FromString(DBUS_TYPE_INT64_AS_STRING);
147 else if (UInt32_Check (obj))
148 return PyString_FromString(DBUS_TYPE_UINT32_AS_STRING);
149 else if (UInt64_Check (obj))
150 return PyString_FromString(DBUS_TYPE_UINT64_AS_STRING);
151 else
152 return PyString_FromString(DBUS_TYPE_INT64_AS_STRING);
154 else if (PyUnicode_Check(obj))
155 return PyString_FromString(DBUS_TYPE_STRING_AS_STRING);
156 else if (PyFloat_Check(obj)) {
157 #ifdef WITH_DBUS_FLOAT32
158 if (Double_Check(obj))
159 return PyString_FromString(DBUS_TYPE_DOUBLE_AS_STRING);
160 else if (Float_Check(obj))
161 return PyString_FromString(DBUS_TYPE_FLOAT_AS_STRING);
162 else
163 #endif
164 return PyString_FromString(DBUS_TYPE_DOUBLE_AS_STRING);
166 else if (PyString_Check(obj)) {
167 if (ObjectPath_Check(obj))
168 return PyString_FromString(DBUS_TYPE_OBJECT_PATH_AS_STRING);
169 else if (Signature_Check(obj))
170 return PyString_FromString(DBUS_TYPE_SIGNATURE_AS_STRING);
171 else if (ByteArray_Check(obj))
172 return PyString_FromString(DBUS_TYPE_ARRAY_AS_STRING
173 DBUS_TYPE_BYTE_AS_STRING);
174 else
175 return PyString_FromString(DBUS_TYPE_STRING_AS_STRING);
177 else if (PyTuple_Check (obj)) {
178 int len = PyTuple_GET_SIZE(obj);
179 PyObject *list = PyList_New(len + 2); /* new ref */
180 PyObject *item; /* temporary new ref */
181 PyObject *empty_str; /* temporary new ref */
182 PyObject *ret;
183 int i;
185 if (!list) return NULL;
186 if (len == 0) {
187 PyErr_SetString(PyExc_ValueError, "D-Bus structs cannot be empty");
188 Py_DECREF (list);
189 return NULL;
191 /* Set the first and last elements of list to be the parentheses */
192 item = PyString_FromString(DBUS_STRUCT_BEGIN_CHAR_AS_STRING);
193 if (PyList_SetItem(list, 0, item) < 0) {
194 Py_DECREF(list);
195 return NULL;
197 item = PyString_FromString(DBUS_STRUCT_END_CHAR_AS_STRING);
198 if (PyList_SetItem(list, len + 1, item) < 0) {
199 Py_DECREF(list);
200 return NULL;
202 if (!item || !PyList_GET_ITEM(list, 0)) {
203 Py_DECREF(list);
204 return NULL;
206 item = NULL;
208 for (i = 0; i < len; i++) {
209 item = PyTuple_GetItem(obj, i);
210 if (!item) {
211 Py_DECREF(list);
212 return NULL;
214 item = _signature_string_from_pyobject(item, NULL);
215 if (!item) {
216 Py_DECREF(list);
217 return NULL;
219 if (PyList_SetItem(list, i + 1, item) < 0) {
220 Py_DECREF(list);
221 return NULL;
223 item = NULL;
225 empty_str = PyString_FromString("");
226 if (!empty_str) {
227 /* really shouldn't happen */
228 Py_DECREF(list);
229 return NULL;
231 ret = PyObject_CallMethod(empty_str, "join", "(O)", list); /* new ref */
232 /* whether ret is NULL or not, */
233 Py_DECREF(empty_str);
234 Py_DECREF(list);
235 return ret;
237 else if (PyList_Check(obj)) {
238 PyObject *tmp;
239 PyObject *ret = PyString_FromString(DBUS_TYPE_ARRAY_AS_STRING);
240 if (!ret) return NULL;
241 if (Array_Check(obj) && PyString_Check(((Array *)obj)->signature)) {
242 PyString_Concat(&ret, ((Array *)obj)->signature);
243 return ret;
245 if (PyList_GET_SIZE(obj) == 0) {
246 /* No items, so fail. Or should we guess "av"? */
247 PyErr_SetString (PyExc_ValueError, "Unable to guess signature "
248 "from an empty list");
249 return NULL;
251 tmp = PyList_GetItem(obj, 0);
252 tmp = _signature_string_from_pyobject(tmp, NULL);
253 if (!tmp) return NULL;
254 PyString_ConcatAndDel (&ret, tmp);
255 return ret;
257 else if (PyDict_Check(obj)) {
258 PyObject *key, *value, *keysig, *valuesig;
259 int pos = 0;
260 PyObject *ret = NULL;
262 if (Dict_Check(obj) && PyString_Check(((Dict *)obj)->signature)) {
263 const char *sig = PyString_AS_STRING(((Dict *)obj)->signature);
265 return PyString_FromFormat((DBUS_TYPE_ARRAY_AS_STRING
266 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
267 "%s"
268 DBUS_DICT_ENTRY_END_CHAR_AS_STRING),
269 sig);
271 if (!PyDict_Next(obj, &pos, &key, &value)) {
272 /* No items, so fail. Or should we guess "a{vv}"? */
273 PyErr_SetString(PyExc_ValueError, "Unable to guess signature "
274 "from an empty dict");
275 return NULL;
277 keysig = _signature_string_from_pyobject(key, NULL);
278 valuesig = _signature_string_from_pyobject(value, NULL);
279 if (keysig && valuesig) {
280 ret = PyString_FromFormat ((DBUS_TYPE_ARRAY_AS_STRING
281 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
282 "%s%s"
283 DBUS_DICT_ENTRY_END_CHAR_AS_STRING),
284 PyString_AS_STRING(keysig),
285 PyString_AS_STRING(valuesig));
287 Py_XDECREF(keysig);
288 Py_XDECREF(valuesig);
289 return ret;
291 else {
292 PyErr_Format(PyExc_TypeError, "Don't know how which D-Bus type "
293 "to use to encode type \"%s\"",
294 obj->ob_type->tp_name);
295 return NULL;
299 static PyObject *
300 Message_guess_signature(PyObject *unused UNUSED, PyObject *args)
302 PyObject *tmp, *ret = NULL;
304 if (!args) {
305 if (!PyErr_Occurred()) {
306 PyErr_BadInternalCall();
308 return NULL;
311 #ifdef USING_DBG
312 fprintf(stderr, "DBG/%ld: called Message_guess_signature", (long)getpid());
313 PyObject_Print(args, stderr, 0);
314 fprintf(stderr, "\n");
315 #endif
317 if (!PyTuple_Check(args)) {
318 DBG("%s", "Message_guess_signature: args not a tuple");
319 PyErr_BadInternalCall();
320 return NULL;
323 /* if there were no args, easy */
324 if (PyTuple_GET_SIZE(args) == 0) {
325 DBG("%s", "Message_guess_signature: no args, so return Signature('')");
326 return PyObject_CallFunction((PyObject *)&SignatureType, "(s)", "");
329 /* if there were args, the signature we want is, by construction,
330 * exactly the signature we get for the tuple args, except that we don't
331 * want the parentheses. */
332 tmp = _signature_string_from_pyobject(args, NULL);
333 if (!tmp) {
334 DBG("%s", "Message_guess_signature: failed");
335 return NULL;
337 if (!PyString_Check(tmp) || PyString_GET_SIZE(tmp) < 2) {
338 PyErr_SetString(PyExc_RuntimeError, "Internal error: "
339 "_signature_string_from_pyobject returned "
340 "a bad result");
341 Py_DECREF(tmp);
342 return NULL;
344 ret = PyObject_CallFunction((PyObject *)&SignatureType, "(s#)",
345 PyString_AS_STRING (tmp) + 1,
346 PyString_GET_SIZE (tmp) - 2);
347 DBG("Message_guess_signature: returning Signature at %p \"%s\"", ret,
348 ret ? PyString_AS_STRING(ret) : "(NULL)");
349 Py_DECREF (tmp);
350 return ret;
353 static int _message_iter_append_pyobject(DBusMessageIter *appender,
354 DBusSignatureIter *sig_iter,
355 PyObject *obj);
357 static int
358 _message_iter_append_string(DBusMessageIter *appender,
359 int sig_type, PyObject *obj)
361 char *s;
363 if (PyString_Check (obj)) {
364 PyObject *unicode;
366 /* Raise TypeError if the string has embedded NULs */
367 if (PyString_AsStringAndSize (obj, &s, NULL) < 0) return -1;
368 /* Surely there's a faster stdlib way to validate UTF-8... */
369 unicode = PyUnicode_DecodeUTF8 (s, PyString_GET_SIZE (obj), NULL);
370 if (!unicode) {
371 PyErr_SetString (PyExc_UnicodeError, "String parameters "
372 "to be sent over D-Bus must be valid UTF-8");
373 return -1;
375 Py_DECREF (unicode);
376 unicode = NULL;
378 DBG("Performing actual append: string %s", s);
379 if (!dbus_message_iter_append_basic (appender, sig_type,
380 &s)) {
381 PyErr_NoMemory();
382 return -1;
385 else if (PyUnicode_Check (obj)) {
386 PyObject *utf8 = PyUnicode_AsUTF8String (obj);
387 if (!utf8) return -1;
388 /* Raise TypeError if the string has embedded NULs */
389 if (PyString_AsStringAndSize (utf8, &s, NULL) < 0) return -1;
390 DBG("Performing actual append: string (from unicode) %s", s);
391 if (!dbus_message_iter_append_basic (appender, sig_type, &s)) {
392 PyErr_NoMemory();
393 return -1;
395 Py_DECREF (utf8);
397 else {
398 PyErr_SetString (PyExc_TypeError,
399 "Expected a string or unicode object");
400 return -1;
402 return 0;
405 static int
406 _message_iter_append_byte(DBusMessageIter *appender, PyObject *obj)
408 unsigned char y;
410 if (PyString_Check(obj)) {
411 if (PyString_GET_SIZE(obj) != 1) {
412 PyErr_Format(PyExc_ValueError, "Expected a string of "
413 "length 1 byte, but found %d bytes",
414 PyString_GET_SIZE (obj));
415 return -1;
417 y = *(unsigned char *)PyString_AS_STRING(obj);
419 else {
420 long i = PyInt_AsLong (obj);
422 if (i == -1 && PyErr_Occurred()) return -1;
423 if (i < 0 || i > 0xff) {
424 PyErr_Format(PyExc_ValueError, "%d outside range for a "
425 "byte value", (int)i);
426 return -1;
428 y = i;
430 DBG("Performing actual append: byte (from unicode) \\x%02x", (unsigned)y);
431 if (!dbus_message_iter_append_basic(appender, DBUS_TYPE_BYTE, &y)) {
432 PyErr_NoMemory();
433 return -1;
435 return 0;
438 static int
439 _message_iter_append_dictentry(DBusMessageIter *appender,
440 DBusSignatureIter *sig_iter,
441 PyObject *dict, PyObject *key)
443 DBusSignatureIter sub_sig_iter;
444 DBusMessageIter sub;
445 int ret = -1;
446 PyObject *value = PyObject_GetItem(dict, key);
448 if (!value) return -1;
450 #ifdef USING_DBG
451 fprintf(stderr, "Append dictentry: ");
452 PyObject_Print(key, stderr, 0);
453 fprintf(stderr, " => ");
454 PyObject_Print(value, stderr, 0);
455 fprintf(stderr, "\n");
456 #endif
458 DBG("Recursing signature iterator %p -> %p", sig_iter, &sub_sig_iter);
459 dbus_signature_iter_recurse(sig_iter, &sub_sig_iter);
460 #ifdef USING_DBG
462 char *s;
463 s = dbus_signature_iter_get_signature(sig_iter);
464 DBG("Signature of parent iterator %p is %s", sig_iter, s);
465 dbus_free(s);
466 s = dbus_signature_iter_get_signature(&sub_sig_iter);
467 DBG("Signature of sub-iterator %p is %s", &sub_sig_iter, s);
468 dbus_free(s);
470 #endif
472 DBG("%s", "Opening DICT_ENTRY container");
473 if (!dbus_message_iter_open_container(appender, DBUS_TYPE_DICT_ENTRY,
474 NULL, &sub)) {
475 PyErr_NoMemory();
476 goto out;
478 ret = _message_iter_append_pyobject(&sub, &sub_sig_iter, key);
479 if (ret == 0) {
480 ret = _message_iter_append_pyobject(&sub, &sub_sig_iter, value);
482 DBG("%s", "Closing DICT_ENTRY container");
483 if (!dbus_message_iter_close_container(appender, &sub)) {
484 PyErr_NoMemory();
485 ret = -1;
487 out:
488 Py_DECREF(value);
489 return ret;
492 static int
493 _message_iter_append_multi(DBusMessageIter *appender,
494 const DBusSignatureIter *sig_iter,
495 int mode, PyObject *obj)
497 DBusMessageIter sub_appender;
498 DBusSignatureIter sub_sig_iter;
499 PyObject *contents;
500 int ret;
501 PyObject *iterator = PyObject_GetIter(obj);
502 char *sig = NULL;
503 int container = mode;
505 #ifdef USING_DBG
506 fprintf(stderr, "Appending multiple: ");
507 PyObject_Print(obj, stderr, 0);
508 fprintf(stderr, "\n");
509 #endif
511 if (!iterator) return -1;
512 if (mode == DBUS_TYPE_DICT_ENTRY) container = DBUS_TYPE_ARRAY;
514 DBG("Recursing signature iterator %p -> %p", sig_iter, &sub_sig_iter);
515 dbus_signature_iter_recurse(sig_iter, &sub_sig_iter);
516 #ifdef USING_DBG
518 char *s;
519 s = dbus_signature_iter_get_signature(sig_iter);
520 DBG("Signature of parent iterator %p is %s", sig_iter, s);
521 dbus_free(s);
522 s = dbus_signature_iter_get_signature(&sub_sig_iter);
523 DBG("Signature of sub-iterator %p is %s", &sub_sig_iter, s);
524 dbus_free(s);
526 #endif
528 if (mode == DBUS_TYPE_ARRAY || mode == DBUS_TYPE_DICT_ENTRY) {
529 sig = dbus_signature_iter_get_signature(&sub_sig_iter);
530 if (!sig) {
531 PyErr_NoMemory();
532 ret = -1;
533 goto out;
536 /* else leave sig set to NULL. */
538 DBG("Opening %c container", container);
539 if (!dbus_message_iter_open_container(appender, container,
540 sig, &sub_appender)) {
541 PyErr_NoMemory();
542 ret = -1;
543 goto out;
545 ret = 0;
546 while ((contents = PyIter_Next(iterator))) {
548 if (mode == DBUS_TYPE_ARRAY || mode == DBUS_TYPE_DICT_ENTRY) {
549 DBG("Recursing signature iterator %p -> %p", sig_iter, &sub_sig_iter);
550 dbus_signature_iter_recurse(sig_iter, &sub_sig_iter);
551 #ifdef USING_DBG
553 char *s;
554 s = dbus_signature_iter_get_signature(sig_iter);
555 DBG("Signature of parent iterator %p is %s", sig_iter, s);
556 dbus_free(s);
557 s = dbus_signature_iter_get_signature(&sub_sig_iter);
558 DBG("Signature of sub-iterator %p is %s", &sub_sig_iter, s);
559 dbus_free(s);
561 #endif
564 if (mode == DBUS_TYPE_DICT_ENTRY) {
565 ret = _message_iter_append_dictentry(&sub_appender, &sub_sig_iter,
566 obj, contents);
568 else {
569 ret = _message_iter_append_pyobject(&sub_appender, &sub_sig_iter,
570 contents);
572 Py_DECREF(contents);
573 if (ret < 0) {
574 break;
577 if (PyErr_Occurred()) ret = -1;
578 /* This must be run as cleanup, even on failure. */
579 DBG("Closing %c container", container);
580 if (!dbus_message_iter_close_container(appender, &sub_appender)) {
581 PyErr_NoMemory();
582 ret = -1;
585 out:
586 Py_XDECREF(iterator);
587 dbus_free(sig);
588 return ret;
591 static int
592 _message_iter_append_string_as_byte_array (DBusMessageIter *appender,
593 PyObject *obj)
595 /* a bit of a faster path for byte arrays that are strings */
596 int len = PyString_GET_SIZE(obj);
597 const char *s;
598 DBusMessageIter sub;
599 int ret;
601 s = PyString_AS_STRING(obj);
602 DBG("%s", "Opening ARRAY container");
603 if (!dbus_message_iter_open_container(appender, DBUS_TYPE_ARRAY,
604 DBUS_TYPE_BYTE_AS_STRING, &sub)) {
605 PyErr_NoMemory();
606 return -1;
608 DBG("Appending fixed array of %d bytes", len);
609 if (dbus_message_iter_append_fixed_array(&sub, DBUS_TYPE_BYTE, &s, len)) {
610 ret = 0;
612 else {
613 PyErr_NoMemory();
614 ret = -1;
616 DBG("%s", "Closing ARRAY container");
617 if (!dbus_message_iter_close_container(appender, &sub)) {
618 PyErr_NoMemory();
619 return -1;
621 return ret;
624 /* Encode some Python object into a D-Bus variant slot. */
625 static int
626 _message_iter_append_variant(DBusMessageIter *appender, PyObject *obj)
628 DBusSignatureIter obj_sig_iter;
629 const char *obj_sig_str;
630 PyObject *obj_sig;
631 int ret;
632 long variant_level;
634 /* Separate the object into the contained object, and the number of
635 * variants it's wrapped in. */
636 obj_sig = _signature_string_from_pyobject(obj, &variant_level);
637 if (!obj_sig) return -1;
639 obj_sig_str = PyString_AsString(obj_sig);
640 if (!obj_sig_str) return -1;
642 if (variant_level < 1) {
643 variant_level = 1;
646 dbus_signature_iter_init(&obj_sig_iter, obj_sig_str);
648 { /* scope for variant_iters */
649 DBusMessageIter variant_iters[variant_level];
650 long i;
652 for (i = 0; i < variant_level; i++) {
653 DBusMessageIter *child = &variant_iters[i];
654 /* The first is a special case: its parent is the iter passed in
655 * to this function, instead of being the previous one in the
656 * stack
658 DBusMessageIter *parent = (i == 0
659 ? appender
660 : &(variant_iters[i-1]));
661 /* The last is also a special case: it contains the actual
662 * object, rather than another variant
664 const char *sig_str = (i == variant_level-1
665 ? obj_sig_str
666 : DBUS_TYPE_VARIANT_AS_STRING);
668 DBG("Opening VARIANT container %p inside %p containing '%s'",
669 child, parent, sig_str);
670 if (!dbus_message_iter_open_container (parent, DBUS_TYPE_VARIANT,
671 sig_str, child)) {
672 PyErr_NoMemory();
673 ret = -1;
674 goto out;
678 /* Put the object itself into the innermost variant */
679 ret = _message_iter_append_pyobject(&variant_iters[variant_level-1],
680 &obj_sig_iter, obj);
682 /* here we rely on i (and variant_level) being a signed long */
683 for (i = variant_level - 1; i >= 0; i--) {
684 DBusMessageIter *child = &variant_iters[i];
685 /* The first is a special case: its parent is the iter passed in
686 * to this function, instead of being the previous one in the
687 * stack
689 DBusMessageIter *parent = (i == 0 ? appender
690 : &(variant_iters[i-1]));
692 DBG("Closing VARIANT container %p inside %p", child, parent);
693 if (!dbus_message_iter_close_container (parent, child)) {
694 PyErr_NoMemory();
695 ret = -1;
696 goto out;
702 out:
703 Py_XDECREF (obj_sig);
704 return ret;
707 static int
708 _message_iter_append_pyobject(DBusMessageIter *appender,
709 DBusSignatureIter *sig_iter,
710 PyObject *obj)
712 int sig_type = dbus_signature_iter_get_current_type (sig_iter);
713 union {
714 dbus_bool_t b;
715 double d;
716 dbus_uint16_t uint16;
717 dbus_int16_t int16;
718 dbus_uint32_t uint32;
719 dbus_int32_t int32;
720 #if defined(DBUS_HAVE_INT64) && defined(HAVE_LONG_LONG)
721 dbus_uint64_t uint64;
722 dbus_int64_t int64;
723 #endif
724 } u;
725 int ret = -1;
727 #ifdef USING_DBG
728 fprintf(stderr, "Appending object at %p: ", obj);
729 PyObject_Print(obj, stderr, 0);
730 fprintf(stderr, " into appender at %p, dbus wants type %c\n",
731 appender, sig_type);
732 #endif
734 switch (sig_type) {
735 /* The numeric types are relatively simple to deal with, so are
736 * inlined here. */
738 case DBUS_TYPE_BOOLEAN:
739 if (PyObject_IsTrue(obj)) {
740 u.b = 1;
742 else {
743 u.b = 0;
745 DBG("Performing actual append: bool(%ld)", (long)u.b);
746 if (!dbus_message_iter_append_basic (appender, sig_type, &u.b)) {
747 PyErr_NoMemory();
748 ret = -1;
749 break;
751 ret = 0;
752 break;
754 case DBUS_TYPE_DOUBLE:
755 u.d = PyFloat_AsDouble (obj);
756 if (PyErr_Occurred()) {
757 ret = -1;
758 break;
760 DBG("Performing actual append: double(%f)", u.d);
761 if (!dbus_message_iter_append_basic(appender, sig_type, &u.d)) {
762 PyErr_NoMemory();
763 ret = -1;
764 break;
766 ret = 0;
767 break;
769 #ifdef WITH_DBUS_FLOAT32
770 case DBUS_TYPE_FLOAT:
771 u.d = PyFloat_AsDouble (obj);
772 if (PyErr_Occurred()) {
773 ret = -1;
774 break;
776 u.f = (float)u.d;
777 DBG("Performing actual append: float(%f)", u.f);
778 if (!dbus_message_iter_append_basic(appender, sig_type, &u.f)) {
779 PyErr_NoMemory();
780 ret = -1;
781 break;
783 ret = 0;
784 break;
785 #endif
787 /* The integer types are all basically the same - we delegate to
788 intNN_range_check() */
789 #define PROCESS_INTEGER(size) \
790 u.size = size##_range_check (obj);\
791 if (u.size == (dbus_##size##_t)(-1) && PyErr_Occurred()) {\
792 ret = -1; \
793 break; \
795 DBG("Performing actual append: " #size "(%lld)", (long long)u.size); \
796 if (!dbus_message_iter_append_basic(appender, sig_type, &u.size)) {\
797 PyErr_NoMemory();\
798 ret = -1;\
799 break;\
801 ret = 0;
803 case DBUS_TYPE_INT16:
804 PROCESS_INTEGER(int16)
805 break;
806 case DBUS_TYPE_UINT16:
807 PROCESS_INTEGER(uint16)
808 break;
809 case DBUS_TYPE_INT32:
810 PROCESS_INTEGER(int32)
811 break;
812 case DBUS_TYPE_UINT32:
813 PROCESS_INTEGER(uint32)
814 break;
815 #if defined(DBUS_HAVE_INT64) && defined(HAVE_LONG_LONG)
816 case DBUS_TYPE_INT64:
817 PROCESS_INTEGER(int64)
818 break;
819 case DBUS_TYPE_UINT64:
820 PROCESS_INTEGER(uint64)
821 break;
822 #else
823 case DBUS_TYPE_INT64:
824 case DBUS_TYPE_UINT64:
825 PyErr_SetString(PyExc_NotImplementedError, "64-bit integer "
826 "types are not supported on this platform");
827 ret = -1;
828 break;
829 #endif
830 #undef PROCESS_INTEGER
832 /* Now the more complicated cases, which are delegated to helper
833 * functions (although in practice, the compiler will hopefully
834 * inline them anyway). */
836 case DBUS_TYPE_STRING:
837 case DBUS_TYPE_SIGNATURE:
838 case DBUS_TYPE_OBJECT_PATH:
839 ret = _message_iter_append_string(appender, sig_type, obj);
840 break;
842 case DBUS_TYPE_BYTE:
843 ret = _message_iter_append_byte(appender, obj);
844 break;
846 case DBUS_TYPE_ARRAY:
847 /* 3 cases - it might actually be a dict, or it might be a byte array
848 * being copied from a string (for which we have a faster path),
849 * or it might be a generic array. */
851 sig_type = dbus_signature_iter_get_element_type(sig_iter);
852 if (sig_type == DBUS_TYPE_DICT_ENTRY)
853 ret = _message_iter_append_multi(appender, sig_iter,
854 DBUS_TYPE_DICT_ENTRY, obj);
855 else if (sig_type == DBUS_TYPE_BYTE && PyString_Check(obj))
856 ret = _message_iter_append_string_as_byte_array(appender, obj);
857 else
858 ret = _message_iter_append_multi(appender, sig_iter,
859 DBUS_TYPE_ARRAY, obj);
860 DBG("_message_iter_append_multi(): %d", ret);
861 break;
863 case DBUS_TYPE_STRUCT:
864 ret = _message_iter_append_multi(appender, sig_iter, sig_type, obj);
865 break;
867 case DBUS_TYPE_VARIANT:
868 ret = _message_iter_append_variant(appender, obj);
869 break;
871 case DBUS_TYPE_INVALID:
872 PyErr_SetString(PyExc_TypeError, "Fewer items found in D-Bus "
873 "signature than in Python arguments");
874 ret = -1;
875 break;
877 default:
878 PyErr_Format(PyExc_TypeError, "Unknown type '\\x%x' in D-Bus "
879 "signature", sig_type);
880 ret = -1;
881 break;
883 if (ret < 0) return -1;
885 DBG("Advancing signature iter at %p", sig_iter);
886 #ifdef USING_DBG
888 dbus_bool_t b =
889 #endif
890 dbus_signature_iter_next(sig_iter);
891 #ifdef USING_DBG
892 DBG("- result: %ld, type %02x '%c'", (long)b,
893 (int)dbus_signature_iter_get_current_type(sig_iter),
894 (int)dbus_signature_iter_get_current_type(sig_iter));
896 #endif
897 return 0;
901 static PyObject *
902 Message_append(Message *self, PyObject *args, PyObject *kwargs)
904 const char *signature = NULL;
905 PyObject *signature_obj = NULL;
906 DBusSignatureIter sig_iter;
907 DBusMessageIter appender;
908 int i;
909 static char *argnames[] = {"signature", NULL};
911 if (!self->msg) return DBusException_UnusableMessage ();
913 #ifdef USING_DBG
914 fprintf(stderr, "DBG/%ld: called Message_append(*", (long)getpid());
915 PyObject_Print(args, stderr, 0);
916 if (kwargs) {
917 fprintf(stderr, ", **");
918 PyObject_Print(kwargs, stderr, 0);
920 fprintf(stderr, ")\n");
921 #endif
923 /* only use kwargs for this step: deliberately ignore args for now */
924 if (!PyArg_ParseTupleAndKeywords(empty_tuple, kwargs, "|z:append",
925 argnames, &signature)) return NULL;
927 if (!signature) {
928 DBG("%s", "No signature for message, guessing...");
929 signature_obj = Message_guess_signature(NULL, args);
930 if (!signature_obj) return NULL;
931 signature = PyString_AS_STRING (signature_obj);
933 /* from here onwards, you have to do a goto rather than returning NULL
934 to make sure signature_obj gets freed */
936 /* iterate over args and the signature, together */
937 if (!dbus_signature_validate(signature, NULL)) {
938 PyErr_SetString(PyExc_ValueError, "Corrupt type signature");
939 goto err;
941 dbus_signature_iter_init(&sig_iter, signature);
942 dbus_message_iter_init_append(self->msg, &appender);
943 for (i = 0; i < PyTuple_GET_SIZE(args); i++) {
944 if (_message_iter_append_pyobject(&appender, &sig_iter,
945 PyTuple_GET_ITEM (args, i)) < 0) {
946 goto hosed;
949 if (dbus_signature_iter_get_current_type(&sig_iter)
950 != DBUS_TYPE_INVALID) {
951 PyErr_SetString(PyExc_TypeError, "More items found in D-Bus "
952 "signature than in Python arguments");
953 goto hosed;
956 /* success! */
957 Py_XDECREF(signature_obj);
958 Py_RETURN_NONE;
960 hosed:
961 /* "If appending any of the arguments fails due to lack of memory,
962 * generally the message is hosed and you have to start over" -libdbus docs
963 * Enforce this by throwing away the message structure.
965 dbus_message_unref(self->msg);
966 self->msg = NULL;
967 err:
968 Py_XDECREF(signature_obj);
969 return NULL;
972 /* vim:set ft=c cino< sw=4 sts=4 et: */