Remove old libxml2-based introspection parser
[dbus-python-phuang.git] / _dbus_bindings / message-get-args.c
blob585b4cbe453cc8f94ec69d21be6434718655208b
1 /* D-Bus Message unserialization. This contains all the logic to map from
2 * D-Bus types to Python objects.
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 char dbus_py_Message_get_args_list__doc__[] = (
31 "get_args_list(**kwargs) -> list\n\n"
32 "Return the message's arguments. Keyword arguments control the translation\n"
33 "of D-Bus types to Python:\n"
34 "\n"
35 ":Keywords:\n"
36 " `byte_arrays` : bool\n"
37 " If true, convert arrays of byte (signature 'ay') into ByteArray,\n"
38 " a str subclass. In practice, this is usually what you want, but\n"
39 " it's off by default for consistency.\n"
40 "\n"
41 " If false (default), convert them into a dbus.Array of Bytes.\n"
42 " `utf8_strings` : bool\n"
43 " If true, return D-Bus strings as Python 8-bit strings (of UTF-8).\n"
44 " If false (default), return D-Bus strings as Python unicode objects.\n"
45 "\n"
46 "Most of the type mappings should be fairly obvious:\n"
47 "\n"
48 "=============== ===================================================\n"
49 "D-Bus Python\n"
50 "=============== ===================================================\n"
51 "byte (y) Byte (int if integer_bytes set)\n"
52 "bool (b) Boolean (int subclass)\n"
53 "Signature (g) Signature (str subclass)\n"
54 "intNN, uintNN IntNN, UIntNN (int/long subclass)\n"
55 "double (d) float\n"
56 "string (s) unicode (str if utf8_strings set)\n"
57 "Object path (o) ObjectPath (str subclass)\n"
58 "dict (a{...}) dict\n"
59 "array (a...) list of appropriate types\n"
60 "byte array (ay) ByteArray (str subclass) if byte_arrays set; or\n"
61 " list of Byte\n"
62 "struct ((...)) tuple of appropriate types\n"
63 "variant (v) contained type, but with variant_level > 0\n"
64 "=============== ===================================================\n"
67 typedef struct {
68 int byte_arrays;
69 int utf8_strings;
70 } Message_get_args_options;
72 static PyObject *_message_iter_get_pyobject(DBusMessageIter *iter,
73 Message_get_args_options *opts,
74 long extra_variants);
76 /* Append all the items iterated over to the given Python list object.
77 * Return 0 on success/-1 with exception on failure. */
78 static int
79 _message_iter_append_all_to_list(DBusMessageIter *iter, PyObject *list,
80 Message_get_args_options *opts)
82 int ret, type;
83 while ((type = dbus_message_iter_get_arg_type(iter))
84 != DBUS_TYPE_INVALID) {
85 PyObject *item;
86 DBG("type == %d '%c'", type, type);
88 item = _message_iter_get_pyobject(iter, opts, 0);
89 if (!item) return -1;
90 #ifdef USING_DBG
91 fprintf(stderr, "DBG/%ld: appending to list: %p == ", (long)getpid(), item);
92 PyObject_Print(item, stderr, 0);
93 fprintf(stderr, " of type %p\n", item->ob_type);
94 #endif
95 ret = PyList_Append(list, item);
96 Py_DECREF(item);
97 item = NULL;
98 if (ret < 0) return -1;
99 #ifdef USING_DBG
100 fprintf(stderr, "DBG/%ld: list now contains: ", (long)getpid());
101 PyObject_Print(list, stderr, 0);
102 fprintf(stderr, "\n");
103 #endif
104 dbus_message_iter_next(iter);
106 return 0;
109 static inline PyObject *
110 _message_iter_get_dict(DBusMessageIter *iter,
111 Message_get_args_options *opts,
112 PyObject *kwargs)
114 DBusMessageIter entries;
115 char *sig_str = dbus_message_iter_get_signature(iter);
116 PyObject *sig;
117 PyObject *ret;
118 int status;
120 sig = PyObject_CallFunction((PyObject *)&DBusPySignature_Type,
121 "(s#)", sig_str+2, strlen(sig_str)-3);
122 status = PyDict_SetItem(kwargs, dbus_py_signature_const, sig);
123 Py_DECREF(sig);
124 if (status < 0) {
125 return NULL;
128 ret = PyObject_Call((PyObject *)&DBusPyDict_Type, dbus_py_empty_tuple, kwargs);
129 if (!ret) {
130 return NULL;
133 dbus_message_iter_recurse(iter, &entries);
134 while (dbus_message_iter_get_arg_type(&entries) == DBUS_TYPE_DICT_ENTRY) {
135 PyObject *key = NULL;
136 PyObject *value = NULL;
137 DBusMessageIter kv;
139 DBG("%s", "dict entry...");
141 dbus_message_iter_recurse(&entries, &kv);
143 key = _message_iter_get_pyobject(&kv, opts, 0);
144 if (!key) {
145 Py_DECREF(ret);
146 return NULL;
148 dbus_message_iter_next(&kv);
150 value = _message_iter_get_pyobject(&kv, opts, 0);
151 if (!value) {
152 Py_DECREF(key);
153 Py_DECREF(ret);
154 return NULL;
157 status = PyDict_SetItem(ret, key, value);
158 Py_DECREF(key);
159 Py_DECREF(value);
161 if (status < 0) {
162 Py_DECREF(ret);
163 return NULL;
165 dbus_message_iter_next(&entries);
168 return ret;
171 /* Returns a new reference. */
172 static PyObject *
173 _message_iter_get_pyobject(DBusMessageIter *iter,
174 Message_get_args_options *opts,
175 long variant_level)
177 union {
178 const char *s;
179 unsigned char y;
180 dbus_bool_t b;
181 double d;
182 float f;
183 dbus_uint16_t u16;
184 dbus_int16_t i16;
185 dbus_uint32_t u32;
186 dbus_int32_t i32;
187 #if defined(DBUS_HAVE_INT64) && defined(HAVE_LONG_LONG)
188 dbus_uint64_t u64;
189 dbus_int64_t i64;
190 #endif
191 } u;
192 int type = dbus_message_iter_get_arg_type(iter);
193 PyObject *args = NULL;
194 PyObject *kwargs = NULL;
195 PyObject *ret = NULL;
197 /* If the variant-level is >0, prepare a dict for the kwargs.
198 * For variant wrappers optimize slightly by skipping this.
200 if (variant_level > 0 && type != DBUS_TYPE_VARIANT) {
201 PyObject *variant_level_int;
203 variant_level_int = PyInt_FromLong(variant_level);
204 if (!variant_level_int) {
205 return NULL;
207 kwargs = PyDict_New();
208 if (!kwargs) {
209 Py_DECREF(variant_level_int);
210 return NULL;
212 if (PyDict_SetItem(kwargs, dbus_py_variant_level_const,
213 variant_level_int) < 0) {
214 Py_DECREF(variant_level_int);
215 Py_DECREF(kwargs);
216 return NULL;
218 Py_DECREF(variant_level_int);
220 /* From here down you need to break from the switch to exit, so the
221 * dict is freed if necessary
224 switch (type) {
225 case DBUS_TYPE_STRING:
226 DBG("%s", "found a string");
227 dbus_message_iter_get_basic(iter, &u.s);
228 if (opts->utf8_strings) {
229 args = Py_BuildValue("(s)", u.s);
230 if (!args) break;
231 ret = PyObject_Call((PyObject *)&DBusPyUTF8String_Type,
232 args, kwargs);
234 else {
235 args = Py_BuildValue("(O)", PyUnicode_DecodeUTF8(u.s,
236 strlen(u.s),
237 NULL));
238 if (!args) {
239 break;
241 ret = PyObject_Call((PyObject *)&DBusPyString_Type,
242 args, kwargs);
244 break;
246 case DBUS_TYPE_SIGNATURE:
247 DBG("%s", "found a signature");
248 dbus_message_iter_get_basic(iter, &u.s);
249 args = Py_BuildValue("(s)", u.s);
250 if (!args) break;
251 ret = PyObject_Call((PyObject *)&DBusPySignature_Type, args, kwargs);
252 break;
254 case DBUS_TYPE_OBJECT_PATH:
255 DBG("%s", "found an object path");
256 dbus_message_iter_get_basic(iter, &u.s);
257 args = Py_BuildValue("(s)", u.s);
258 if (!args) break;
259 ret = PyObject_Call((PyObject *)&DBusPyObjectPath_Type, args, kwargs);
260 break;
262 case DBUS_TYPE_DOUBLE:
263 DBG("%s", "found a double");
264 dbus_message_iter_get_basic(iter, &u.d);
265 args = Py_BuildValue("(f)", u.d);
266 if (!args) break;
267 ret = PyObject_Call((PyObject *)&DBusPyDouble_Type, args, kwargs);
268 break;
270 #ifdef WITH_DBUS_FLOAT32
271 case DBUS_TYPE_FLOAT:
272 DBG("%s", "found a float");
273 dbus_message_iter_get_basic(iter, &u.f);
274 args = Py_BuildValue("(f)", (double)u.f);
275 if (!args) break;
276 ret = PyObject_Call((PyObject *)&DBusPyFloat_Type, args, kwargs);
277 break;
278 #endif
280 case DBUS_TYPE_INT16:
281 DBG("%s", "found an int16");
282 dbus_message_iter_get_basic(iter, &u.i16);
283 args = Py_BuildValue("(i)", (int)u.i16);
284 if (!args) break;
285 ret = PyObject_Call((PyObject *)&DBusPyInt16_Type, args, kwargs);
286 break;
288 case DBUS_TYPE_UINT16:
289 DBG("%s", "found a uint16");
290 dbus_message_iter_get_basic(iter, &u.u16);
291 args = Py_BuildValue("(i)", (int)u.u16);
292 if (!args) break;
293 ret = PyObject_Call((PyObject *)&DBusPyUInt16_Type, args, kwargs);
294 break;
296 case DBUS_TYPE_INT32:
297 DBG("%s", "found an int32");
298 dbus_message_iter_get_basic(iter, &u.i32);
299 args = Py_BuildValue("(l)", (long)u.i32);
300 if (!args) break;
301 ret = PyObject_Call((PyObject *)&DBusPyInt32_Type, args, kwargs);
302 break;
304 case DBUS_TYPE_UINT32:
305 DBG("%s", "found a uint32");
306 dbus_message_iter_get_basic(iter, &u.u32);
307 args = Py_BuildValue("(k)", (unsigned long)u.u32);
308 if (!args) break;
309 ret = PyObject_Call((PyObject *)&DBusPyUInt32_Type, args, kwargs);
310 break;
312 #if defined(DBUS_HAVE_INT64) && defined(HAVE_LONG_LONG)
313 case DBUS_TYPE_INT64:
314 DBG("%s", "found an int64");
315 dbus_message_iter_get_basic(iter, &u.i64);
316 args = Py_BuildValue("(L)", (PY_LONG_LONG)u.i64);
317 if (!args) break;
318 ret = PyObject_Call((PyObject *)&DBusPyInt64_Type, args, kwargs);
319 break;
321 case DBUS_TYPE_UINT64:
322 DBG("%s", "found a uint64");
323 dbus_message_iter_get_basic(iter, &u.u64);
324 args = Py_BuildValue("(K)", (unsigned PY_LONG_LONG)u.u64);
325 if (!args) break;
326 ret = PyObject_Call((PyObject *)&DBusPyUInt64_Type, args, kwargs);
327 break;
328 #else
329 case DBUS_TYPE_INT64:
330 case DBUS_TYPE_UINT64:
331 PyErr_SetString(PyExc_NotImplementedError,
332 "64-bit integer types are not supported on "
333 "this platform");
334 break;
335 #endif
337 case DBUS_TYPE_BYTE:
338 DBG("%s", "found a byte");
339 dbus_message_iter_get_basic(iter, &u.y);
340 args = Py_BuildValue("(l)", (long)u.y);
341 if (!args)
342 break;
343 ret = PyObject_Call((PyObject *)&DBusPyByte_Type, args, kwargs);
344 break;
346 case DBUS_TYPE_BOOLEAN:
347 DBG("%s", "found a bool");
348 dbus_message_iter_get_basic(iter, &u.b);
349 args = Py_BuildValue("(l)", (long)u.b);
350 if (!args)
351 break;
352 ret = PyObject_Call((PyObject *)&DBusPyBoolean_Type, args, kwargs);
353 break;
355 case DBUS_TYPE_ARRAY:
356 DBG("%s", "found an array...");
357 /* Dicts are arrays of DBUS_TYPE_DICT_ENTRY on the wire.
358 Also, we special-case arrays of DBUS_TYPE_BYTE sometimes. */
359 type = dbus_message_iter_get_element_type(iter);
360 if (type == DBUS_TYPE_DICT_ENTRY) {
361 DBG("%s", "no, actually it's a dict...");
362 if (!kwargs) {
363 kwargs = PyDict_New();
364 if (!kwargs) break;
366 ret = _message_iter_get_dict(iter, opts, kwargs);
368 else if (opts->byte_arrays && type == DBUS_TYPE_BYTE) {
369 DBusMessageIter sub;
370 int n;
372 DBG("%s", "actually, a byte array...");
373 dbus_message_iter_recurse(iter, &sub);
374 dbus_message_iter_get_fixed_array(&sub,
375 (const unsigned char **)&u.s,
376 &n);
377 args = Py_BuildValue("(s#)", u.s, n);
378 if (!args) break;
379 ret = PyObject_Call((PyObject *)&DBusPyByteArray_Type,
380 args, kwargs);
382 else {
383 DBusMessageIter sub;
384 char *sig;
385 PyObject *sig_obj;
386 int status;
388 DBG("%s", "a normal array...");
389 if (!kwargs) {
390 kwargs = PyDict_New();
391 if (!kwargs) break;
393 dbus_message_iter_recurse(iter, &sub);
394 sig = dbus_message_iter_get_signature(&sub);
395 if (!sig) break;
396 sig_obj = PyObject_CallFunction((PyObject *)&DBusPySignature_Type,
397 "(s)", sig);
398 dbus_free(sig);
399 if (!sig_obj) break;
400 status = PyDict_SetItem(kwargs, dbus_py_signature_const, sig_obj);
401 Py_DECREF(sig_obj);
402 if (status < 0) break;
403 ret = PyObject_Call((PyObject *)&DBusPyArray_Type,
404 dbus_py_empty_tuple, kwargs);
405 if (!ret) break;
406 if (_message_iter_append_all_to_list(&sub, ret, opts) < 0) {
407 Py_DECREF(ret);
408 ret = NULL;
411 break;
413 case DBUS_TYPE_STRUCT:
415 DBusMessageIter sub;
416 PyObject *list = PyList_New(0);
417 PyObject *tuple;
419 DBG("%s", "found a struct...");
420 if (!list) break;
421 dbus_message_iter_recurse(iter, &sub);
422 if (_message_iter_append_all_to_list(&sub, list, opts) < 0) {
423 Py_DECREF(list);
424 break;
426 tuple = Py_BuildValue("(O)", list);
427 if (tuple) {
428 ret = PyObject_Call((PyObject *)&DBusPyStruct_Type, tuple, kwargs);
430 else {
431 ret = NULL;
433 /* whether successful or not, we take the same action: */
434 Py_DECREF(list);
435 Py_XDECREF(tuple);
437 break;
439 case DBUS_TYPE_VARIANT:
441 DBusMessageIter sub;
443 DBG("%s", "found a variant...");
444 dbus_message_iter_recurse(iter, &sub);
445 ret = _message_iter_get_pyobject(&sub, opts, variant_level+1);
447 break;
449 default:
450 PyErr_Format(PyExc_TypeError, "Unknown type '\\%x' in D-Bus "
451 "message", type);
454 if (args) {
455 Py_DECREF(args);
457 if (kwargs) {
458 Py_DECREF(kwargs);
460 return ret;
463 PyObject *
464 dbus_py_Message_get_args_list(Message *self, PyObject *args, PyObject *kwargs)
466 Message_get_args_options opts = { 0, 0 };
467 static char *argnames[] = { "byte_arrays", "utf8_strings", NULL };
468 PyObject *list;
469 DBusMessageIter iter;
471 #ifdef USING_DBG
472 fprintf(stderr, "DBG/%ld: called Message_get_args_list(self, *",
473 (long)getpid());
474 PyObject_Print(args, stderr, 0);
475 if (kwargs) {
476 fprintf(stderr, ", **");
477 PyObject_Print(kwargs, stderr, 0);
479 fprintf(stderr, ")\n");
480 #endif
482 if (PyTuple_Size(args) != 0) {
483 PyErr_SetString(PyExc_TypeError, "get_args_list takes no positional "
484 "arguments");
485 return NULL;
487 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ii:get_args_list",
488 argnames,
489 &(opts.byte_arrays),
490 &(opts.utf8_strings))) return NULL;
491 if (!self->msg) return DBusPy_RaiseUnusableMessage();
493 list = PyList_New(0);
494 if (!list) return NULL;
496 /* Iterate over args, if any, appending to list */
497 if (dbus_message_iter_init(self->msg, &iter)) {
498 if (_message_iter_append_all_to_list(&iter, list, &opts) < 0) {
499 Py_DECREF(list);
500 DBG_EXC("%s", "Message_get_args: appending all to list failed:");
501 return NULL;
505 #ifdef USING_DBG
506 fprintf(stderr, "DBG/%ld: message has args list ", (long)getpid());
507 PyObject_Print(list, stderr, 0);
508 fprintf(stderr, "\n");
509 #endif
511 return list;
514 /* vim:set ft=c cino< sw=4 sts=4 et: */