diff options
-rw-r--r-- | HACKING.txt | 35 | ||||
-rw-r--r-- | _dbus_bindings/bus-impl.h | 415 | ||||
-rw-r--r-- | _dbus_bindings/bytes-impl.h | 420 | ||||
-rw-r--r-- | _dbus_bindings/conn-impl.h | 551 | ||||
-rw-r--r-- | _dbus_bindings/conn-methods-impl.h | 653 | ||||
-rw-r--r-- | _dbus_bindings/containers-impl.h | 743 | ||||
-rw-r--r-- | _dbus_bindings/dbus_bindings.py | 27 | ||||
-rw-r--r-- | _dbus_bindings/debug-impl.h | 49 | ||||
-rw-r--r-- | _dbus_bindings/exceptions-impl.h | 71 | ||||
-rw-r--r-- | _dbus_bindings/generic-impl.h | 108 | ||||
-rw-r--r-- | _dbus_bindings/message-append-impl.h | 863 | ||||
-rw-r--r-- | _dbus_bindings/message-get-args-impl.h | 403 | ||||
-rw-r--r-- | _dbus_bindings/message-impl.h | 1064 | ||||
-rw-r--r-- | _dbus_bindings/module.c | 139 | ||||
-rw-r--r-- | _dbus_bindings/pending-call-impl.h | 230 | ||||
-rw-r--r-- | _dbus_bindings/signature-impl.h | 238 | ||||
-rw-r--r-- | _dbus_bindings/test.py | 30 | ||||
-rw-r--r-- | _dbus_bindings/types-impl.h | 635 | ||||
-rw-r--r-- | _dbus_bindings/validation-impl.h | 241 | ||||
-rw-r--r-- | _dbus_glib_bindings/module.c | 74 | ||||
-rw-r--r-- | include/dbus_bindings.h | 64 |
21 files changed, 7053 insertions, 0 deletions
diff --git a/HACKING.txt b/HACKING.txt new file mode 100644 index 0000000..61f9d7a --- /dev/null +++ b/HACKING.txt @@ -0,0 +1,35 @@ +========================================= +D-Bus Python bindings - notes for hackers +========================================= + +:Author: Simon McVittie +:Date: 2006-09-26 + +Code organisation +================= + +Python wants everything except the init function to be declared static, so the +whole of _dbus_bindings is a single translation unit - module.c #includes +various files *-impl.h, which define static (sometimes static inline) +functions. Even so, it compiles quicker than the old Pyrex implementation :-p + +Threading/locking model +======================= + +All Python functions must be called with the GIL (obviously). + +Before calling into any D-Bus function that can block, release the GIL; +as well as the usual "be nice to other threads", D-Bus does its own +locking and we don't want to deadlock with it. Most Connection methods +can block. + +Indentation and other holy wars +=============================== + +Python code is meant to follow PEP8, and has 4-space indentation, no hard tabs. + +C code is meant to follow what PEP7 as "Python 3000" style - 4-space +indentation, no hard tabs, otherwise consistent with historical Python 2.x +code. + +Docstrings etc. are reStructuredText. diff --git a/_dbus_bindings/bus-impl.h b/_dbus_bindings/bus-impl.h new file mode 100644 index 0000000..2864793 --- /dev/null +++ b/_dbus_bindings/bus-impl.h @@ -0,0 +1,415 @@ +/* Implementation of Bus, a subtype of Connection. + * + * Copyright (C) 2006 Collabora Ltd. + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +PyDoc_STRVAR(Bus_tp_doc, +"Bus([address: str or int])\n\n" +"If the address is an int it must be one of the constants BUS_SESSION,\n" +"BUS_SYSTEM, BUS_STARTER. The default is BUS_SESSION.\n" +); + +/* Bus definition =================================================== */ + +static PyTypeObject BusType; + +#define Bus_Check(ob) PyObject_TypeCheck(ob, &BusType) + +/* Bus methods ====================================================== */ + +static PyObject * +Bus_tp_new (PyTypeObject *cls, PyObject *args, PyObject *kwargs) +{ + PyObject *first = NULL; + DBusConnection *conn; + DBusError error; + Connection *self; + dbus_bool_t ret; + long type; + static char *argnames[] = {"address_or_type", NULL}; + + if (!PyArg_ParseTupleAndKeywords (args, kwargs, "|O", argnames, &first)) { + return NULL; + } + + dbus_error_init (&error); + + if (first && PyString_Check(first)) { + /* It's a custom address. First connect to it, then register. */ + + self = (Connection *)Connection_tp_new(cls, args, kwargs); + if (!self) return NULL; + + Py_BEGIN_ALLOW_THREADS + ret = dbus_bus_register (self->conn, &error); + Py_END_ALLOW_THREADS + if (!ret) { + DBusException_ConsumeError(&error); + Py_DECREF(self); + return NULL; + } + } + + /* If the first argument isn't a string, it must be an integer + representing one of the well-known bus types. */ + + if (first && !PyInt_Check (first)) { + PyErr_SetString(PyExc_TypeError, "A string address or an integer " + "bus type is required"); + return NULL; + } + if (first) { + type = PyInt_AsLong (first); + } + else { + type = DBUS_BUS_SESSION; + } + + if (type != DBUS_BUS_SESSION && type != DBUS_BUS_SYSTEM + && type != DBUS_BUS_STARTER) { + PyErr_Format(PyExc_ValueError, "Unknown bus type %d", (int)type); + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + conn = dbus_bus_get_private (type, &error); + Py_END_ALLOW_THREADS + + if (!conn) { + DBusException_ConsumeError (&error); + return NULL; + } + return Connection_NewConsumingDBusConnection(cls, conn); +} + +PyDoc_STRVAR(Bus_get_unique_name__doc__, +"get_unique_name() -> str\n\n" +"Return this application's unique name on this bus.\n"); +static PyObject * +Bus_get_unique_name (Connection *self, PyObject *args) +{ + const char *name; + + Py_BEGIN_ALLOW_THREADS + name = dbus_bus_get_unique_name (self->conn); + Py_END_ALLOW_THREADS + if (!name) { + /* shouldn't happen, but C subtypes could have done something stupid */ + PyErr_SetString (DBusException, "Unable to retrieve unique name"); + return NULL; + } + return PyString_FromString (name); +} + +PyDoc_STRVAR(Bus_get_unix_user__doc__, +"get_unix_user(bus_name: str) -> int\n" +"\n" +"Get the numeric uid of the process which owns the given bus name\n" +"on the connected bus daemon.\n" +"\n" +":Parameters:\n" +" `bus_name` : str\n" +" A bus name (may be either a unique name or a well-known name)\n" +); +static PyObject * +Bus_get_unix_user (Connection *self, PyObject *args) +{ + DBusError error; + unsigned long uid; + const char *bus_name; + + if (!PyArg_ParseTuple(args, "s:get_unix_user", &bus_name)) { + return NULL; + } + + dbus_error_init (&error); + Py_BEGIN_ALLOW_THREADS + uid = dbus_bus_get_unix_user (self->conn, bus_name, &error); + Py_END_ALLOW_THREADS + if (uid == (unsigned long)(-1)) return DBusException_ConsumeError (&error); + return PyLong_FromUnsignedLong (uid); +} + +PyDoc_STRVAR(Bus_start_service_by_name__doc__, +"start_service_by_name(bus_name: str) -> (True, int)\n\ +\n\ +Start a service which will implement the given bus name on this\n\ +Bus.\n\ +\n\ +:Parameters:\n\ + `bus_name` : str\n\ + The well-known bus name for which an implementation is required\n\ +\n\ +:Returns: A tuple of 2 elements. The first is always True, the second is\n\ + either START_REPLY_SUCCESS or START_REPLY_ALREADY_RUNNING.\n\ +\n\ +:Raises DBusException: if the service could not be started.\n\ +\n\ +FIXME: Fix return signature?\n\ +"); +static PyObject * +Bus_start_service_by_name (Connection *self, PyObject *args) +{ + DBusError error; + const char *bus_name; + dbus_uint32_t ret; + dbus_bool_t success; + + if (!PyArg_ParseTuple(args, "s:start_service_by_name", &bus_name)) { + return NULL; + } + dbus_error_init (&error); + Py_BEGIN_ALLOW_THREADS + success = dbus_bus_start_service_by_name (self->conn, bus_name, + 0 /* flags */, &ret, &error); + Py_END_ALLOW_THREADS + if (!success) { + return DBusException_ConsumeError (&error); + } + return Py_BuildValue ("(Ol)", Py_True, (long)ret); +} + +/* FIXME: signal IN_QUEUE, EXISTS by exception? */ +PyDoc_STRVAR(Bus_request_name__doc__, ""); +static PyObject * +Bus_request_name (Connection *self, PyObject *args) +{ + unsigned int flags = 0; + const char *bus_name; + int ret; + DBusError error; + + if (!PyArg_ParseTuple(args, "s|I:request_name", &bus_name, &flags)) { + return NULL; + } + if (!_validate_bus_name(bus_name, 0, 1)) return NULL; + + dbus_error_init (&error); + Py_BEGIN_ALLOW_THREADS + ret = dbus_bus_request_name(self->conn, bus_name, flags, &error); + Py_END_ALLOW_THREADS + if (ret == -1) return DBusException_ConsumeError (&error); + + return PyInt_FromLong(ret); +} + +PyDoc_STRVAR(Bus_release_name__doc__, ""); +static PyObject * +Bus_release_name (Connection *self, PyObject *args) +{ + const char *bus_name; + int ret; + DBusError error; + + if (!PyArg_ParseTuple(args, "s:release_name", &bus_name)) return NULL; + + dbus_error_init (&error); + Py_BEGIN_ALLOW_THREADS + ret = dbus_bus_release_name(self->conn, bus_name, &error); + Py_END_ALLOW_THREADS + if (ret == -1) return DBusException_ConsumeError (&error); + + return PyInt_FromLong(ret); +} + +PyDoc_STRVAR (Bus_name_has_owner__doc__, +"name_has_owner(bus_name: str) -> bool\n\n" +"Return True if and only if the given bus name has an owner on this bus.\n"); +static PyObject * +Bus_name_has_owner (Connection *self, PyObject *args) +{ + const char *bus_name; + int ret; + DBusError error; + + if (!PyArg_ParseTuple(args, "s:name_has_owner", &bus_name)) return NULL; + dbus_error_init (&error); + Py_BEGIN_ALLOW_THREADS + ret = dbus_bus_name_has_owner(self->conn, bus_name, &error); + Py_END_ALLOW_THREADS + if (dbus_error_is_set (&error)) { + return DBusException_ConsumeError (&error); + } + return PyBool_FromLong(ret); +} + +PyDoc_STRVAR (Bus_add_match_string__doc__, +"add_match_string(rule: str)\n\n" +"Arrange for this application to receive messages on the bus that match\n" +"the given rule. This version will block and raises DBusException on error.\n"); +static PyObject * +Bus_add_match_string (Connection *self, PyObject *args) +{ + const char *rule; + DBusError error; + + if (!PyArg_ParseTuple(args, "s:add_match", &rule)) return NULL; + dbus_error_init (&error); + Py_BEGIN_ALLOW_THREADS + dbus_bus_add_match (self->conn, rule, &error); + Py_END_ALLOW_THREADS + if (dbus_error_is_set (&error)) { + return DBusException_ConsumeError (&error); + } + Py_RETURN_NONE; +} + +PyDoc_STRVAR (Bus_add_match_string_non_blocking__doc__, +"add_match_string_non_blocking(rule: str)\n\n" +"Arrange for this application to receive messages on the bus that match\n" +"the given rule. This version does not block, but any errors will be\n" +"ignored.\n"); +static PyObject * +Bus_add_match_string_non_blocking (Connection *self, PyObject *args) +{ + const char *rule; + + if (!PyArg_ParseTuple(args, "s:add_match", &rule)) return NULL; + Py_BEGIN_ALLOW_THREADS + dbus_bus_add_match (self->conn, rule, NULL); + Py_END_ALLOW_THREADS + Py_RETURN_NONE; +} + +PyDoc_STRVAR (Bus_remove_match_string__doc__, +"remove_match_string_non_blocking(rule: str)\n\n" +"Remove the given match rule; if it has been added more than once,\n" +"remove one of the identical copies, leaving the others active.\n" +"This version blocks, and raises DBusException on error.\n"); +static PyObject * +Bus_remove_match_string (Connection *self, PyObject *args) +{ + const char *rule; + DBusError error; + + if (!PyArg_ParseTuple(args, "s:remove_match", &rule)) return NULL; + dbus_error_init (&error); + Py_BEGIN_ALLOW_THREADS + dbus_bus_remove_match (self->conn, rule, &error); + Py_END_ALLOW_THREADS + if (dbus_error_is_set (&error)) { + return DBusException_ConsumeError (&error); + } + Py_RETURN_NONE; +} + +PyDoc_STRVAR (Bus_remove_match_string_non_blocking__doc__, +"remove_match_string_non_blocking(rule: str)\n\n" +"Remove the given match rule; if it has been added more than once,\n" +"remove one of the identical copies, leaving the others active.\n" +"This version does not block, but causes any errors to be ignored.\n"); +static PyObject * +Bus_remove_match_string_non_blocking (Connection *self, PyObject *args) +{ + const char *rule; + + if (!PyArg_ParseTuple(args, "s:remove_match", &rule)) return NULL; + Py_BEGIN_ALLOW_THREADS + dbus_bus_remove_match (self->conn, rule, NULL); + Py_END_ALLOW_THREADS + Py_RETURN_NONE; +} + +/* Bus type object ================================================== */ + +static struct PyMethodDef Bus_tp_methods[] = { +#define ENTRY(name, flags) {#name, (PyCFunction)Bus_##name, flags, Bus_##name##__doc__}, + ENTRY(get_unique_name, METH_NOARGS) + ENTRY(get_unix_user, METH_VARARGS) + ENTRY(start_service_by_name, METH_VARARGS) + ENTRY(request_name, METH_VARARGS) + ENTRY(release_name, METH_VARARGS) + ENTRY(name_has_owner, METH_VARARGS) + ENTRY(name_has_owner, METH_VARARGS) + ENTRY(add_match_string, METH_VARARGS) + ENTRY(add_match_string_non_blocking, METH_VARARGS) + ENTRY(remove_match_string, METH_VARARGS) + ENTRY(remove_match_string_non_blocking, METH_VARARGS) +#undef ENTRY + {NULL}, +}; + +/* TODO: Call this dbus.Bus rather than _dbus_bindings._Bus if it ever gets + * all the functionality of the current dbus._dbus.Bus (mainly creation of + * proxies). + */ +static PyTypeObject BusType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_dbus_bindings._Bus", /*tp_name*/ + 0, /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)Connection_tp_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + Bus_tp_doc, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + Bus_tp_methods, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ + &ConnectionType, /*tp_base*/ + 0, /*tp_dict*/ + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + 0, /*tp_init*/ + 0, /*tp_alloc*/ + Bus_tp_new, /*tp_new*/ + 0, /*tp_free*/ + 0, /*tp_is_gc*/ +}; + +static inline int +init_bus_types (void) +{ + if (PyType_Ready (&BusType) < 0) return 0; + return 1; +} + +static inline int +insert_bus_types (PyObject *this_module) +{ + if (PyModule_AddObject (this_module, "_Bus", + (PyObject *)&BusType) < 0) return 0; + return 1; +} + +/* vim:set ft=c cino< sw=4 sts=4 et: */ diff --git a/_dbus_bindings/bytes-impl.h b/_dbus_bindings/bytes-impl.h new file mode 100644 index 0000000..c9753a3 --- /dev/null +++ b/_dbus_bindings/bytes-impl.h @@ -0,0 +1,420 @@ +/* D-Bus Byte and ByteArray types. + * + * Copyright (C) 2006 Collabora Ltd. + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +static PyTypeObject ByteType, ByteArrayType; + +static inline int Byte_Check(PyObject *o) +{ + return (o->ob_type == &ByteType) + || PyObject_IsInstance(o, (PyObject *)&ByteType); +} + +static inline int ByteArray_Check(PyObject *o) +{ + return (o->ob_type == &ByteArrayType) + || PyObject_IsInstance(o, (PyObject *)&ByteArrayType); +} + +PyDoc_STRVAR(Byte_tp_doc, +"Byte(integer or str of length 1)\n" +"\n" +"Byte is a subtype of str, restricted to length exactly 1.\n" +"\n" +"A Byte b also supports the following operations:\n" +"\n" +"* int(b) == long(b) == float(b) == ord(b)\n" +"\n" +"* oct(b), hex(b)\n" +); + +static inline unsigned char +Byte_as_uchar(PyObject *self) +{ + return (unsigned char) (PyString_AS_STRING (self)[0]); +} + +PyObject *allocated_bytes[256] = {NULL}; + +static PyObject * +Byte_from_uchar(unsigned char data) +{ + PyObject *ret; + ret = allocated_bytes[data]; + if (!ret) { + char c[] = { (char)data, '\0' }; + PyObject *tuple = Py_BuildValue("(s#)", c, 1); + DBG("Allocating a new Byte of value \\x%02x", data); + + if (!tuple) return NULL; + + ret = PyString_Type.tp_new(&ByteType, tuple, NULL); + Py_DECREF(tuple); + tuple = NULL; + +#ifdef USING_DBG + fprintf(stderr, "New Byte of value \\x%02x: ", data); + PyObject_Print(ret, stderr, 0); + fprintf(stderr, "\n"); +#endif + + Py_INCREF(ret); + allocated_bytes[data] = ret; + } + Py_INCREF(ret); + return ret; +} + +static PyObject * +Byte_new(PyTypeObject *cls, PyObject *args, PyObject *kwargs) +{ + PyObject *arg; + PyObject *string; + PyObject *tuple; + + if (PyTuple_Size(args) != 1 || (kwargs && PyDict_Size(kwargs) != 0)) { + PyErr_SetString(PyExc_TypeError, "Byte constructor takes exactly one " + "positional argument"); + return NULL; + } + + /* arg is only a borrowed ref for the moment */ + arg = PyTuple_GetItem(args, 0); + + if (PyString_Check(arg)) { + /* string of length 1, we hope */ + if (PyString_GET_SIZE(arg) != 1) { + goto bad_arg; + } + if (arg->ob_type == &ByteType) { + Py_INCREF(arg); + return arg; + } + if (cls == &ByteType) { + /* we know that the Byte type is the same as a string + * internally, so fast-track it */ + return Byte_from_uchar((unsigned char)(PyString_AS_STRING(arg)[0])); + } + /* only a borrowed reference so far, take ownership */ + Py_INCREF(arg); + } + else if (PyInt_Check(arg)) { + long i = PyInt_AS_LONG(arg); + + if (i < 0 || i > 255) goto bad_range; + /* make a byte object, which is a length-1 string - now it's a new + reference */ + arg = Byte_from_uchar((unsigned char)i); + if (!arg) return NULL; + /* if that's the type we wanted, there's nothing more to do */ + if (cls == &ByteType) return arg; + } + else { + goto bad_arg; + } + + /* so here's the complicated case: we're trying to instantiate a subclass + so we can't just allocate a Byte and go "it'll all be fine". + + By now, we do have a string-of-length-1 to use as an argument to the c'tor + */ + tuple = Py_BuildValue("(O)", arg); + if (!tuple) return NULL; + Py_DECREF(arg); + arg = NULL; + + string = PyString_Type.tp_new(cls, tuple, NULL); + Py_DECREF(tuple); + tuple = NULL; + return string; + +bad_arg: + PyErr_SetString(PyExc_TypeError, "Expected a string of length 1, " + "or an int in the range 0-255"); + return NULL; +bad_range: + PyErr_SetString(PyExc_ValueError, "Integer outside range 0-255"); + return NULL; +} + +static PyObject *Byte_nb_int (PyObject *self) +{ + return PyInt_FromLong(Byte_as_uchar(self)); +} + +static PyObject *Byte_nb_float (PyObject *self) +{ + return PyFloat_FromDouble(Byte_as_uchar(self)); +} + +static PyObject *Byte_nb_long (PyObject *self) +{ + return PyLong_FromLong(Byte_as_uchar(self)); +} + +static PyObject *Byte_nb_oct (PyObject *self) +{ + PyObject *i = PyInt_FromLong(Byte_as_uchar(self)); + if (!i) return NULL; + PyObject *s = (i->ob_type->tp_as_number->nb_oct) (i); + Py_XDECREF (i); + return s; +} + +static PyObject *Byte_nb_hex (PyObject *self) +{ + PyObject *i = PyInt_FromLong(Byte_as_uchar(self)); + if (!i) return NULL; + PyObject *s = (i->ob_type->tp_as_number->nb_hex) (i); + Py_XDECREF (i); + return s; +} + +static PyNumberMethods Byte_tp_as_number = { + 0, /* nb_add */ + 0, /* nb_subtract */ + 0, /* nb_multiply */ + 0, /* nb_divide */ + 0, /* nb_remainder */ + 0, /* nb_divmod */ + 0, /* nb_power */ + 0, /* nb_negative */ + 0, /* nb_positive */ + 0, /* nb_absolute */ + 0, /* nb_nonzero */ + 0, /* nb_invert */ + 0, /* nb_lshift */ + 0, /* nb_rshift */ + 0, /* nb_and */ + 0, /* nb_xor */ + 0, /* nb_or */ + 0, /* nb_coerce */ + (unaryfunc)Byte_nb_int, /* nb_int */ + (unaryfunc)Byte_nb_long, /* nb_long */ + (unaryfunc)Byte_nb_float, /* nb_float */ + (unaryfunc)Byte_nb_oct, /* nb_oct */ + (unaryfunc)Byte_nb_hex, /* nb_hex */ + 0, /* nb_inplace_add */ + 0, /* nb_inplace_subtract */ + 0, /* nb_inplace_multiply */ + 0, /* nb_inplace_divide */ + 0, /* nb_inplace_remainder */ + 0, /* nb_inplace_power */ + 0, /* nb_inplace_lshift */ + 0, /* nb_inplace_rshift */ + 0, /* nb_inplace_and */ + 0, /* nb_inplace_xor */ + 0, /* nb_inplace_or */ + 0, /* nb_floor_divide */ + 0, /* nb_true_divide */ + 0, /* nb_inplace_floor_divide */ + 0, /* nb_inplace_true_divide */ +}; + +static PyTypeObject ByteType = { + PyObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type)) + 0, + "dbus.Byte", + 0, + 0, + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + str_subclass_tp_repr, /* tp_repr */ + &Byte_tp_as_number, + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + Byte_tp_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + DEFERRED_ADDRESS(&PyString_Type), /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + Byte_new, /* tp_new */ +}; + +PyDoc_STRVAR(ByteArray_tp_doc, +"ByteArray(str)\n" +"\n" +"ByteArray is a subtype of str which can be used when you want an\n" +"efficient immutable representation of a D-Bus byte array (signature 'ay').\n" +"\n" +"The subscript operation byte_array[i] returns Byte instances. Otherwise,\n" +"it's just a string.\n" +); + +static inline unsigned char * +ByteArray_as_ucharptr(PyObject *self) +{ + return (unsigned char *)PyString_AsString(self); +} + +static inline PyObject * +ByteArray_from_uchars(const unsigned char *data, int size) +{ + return PyObject_CallFunction((PyObject *)&ByteArrayType, "(s#)", + (char *)data, size); +} + +static PyObject * +ByteArray_sq_item (PyObject *self, int i) +{ + unsigned char c; + if (i < 0 || i >= PyString_GET_SIZE(self)) { + PyErr_SetString(PyExc_IndexError, "ByteArray index out of range"); + return NULL; + } + c = ((unsigned char *)PyString_AS_STRING(self))[i]; + return Byte_from_uchar(c); +} + +static PyObject * +ByteArray_mp_subscript(PyObject *self, PyObject *other) +{ + long i; + if (PyInt_Check(other)) { + i = PyInt_AS_LONG(other); + if (i < 0) i += PyString_GET_SIZE (self); + return ByteArray_sq_item(self, i); + } + else if (PyLong_Check(other)) { + i = PyLong_AsLong(other); + if (i == -1 && PyErr_Occurred()) return NULL; + if (i < 0) i += PyString_GET_SIZE(self); + return ByteArray_sq_item(self, i); + } + else { + /* slices just return strings */ + return (PyString_Type.tp_as_mapping->mp_subscript)(self, other); + } +} + +static PyMappingMethods ByteArray_tp_as_mapping = { + 0, /* mp_length */ + ByteArray_mp_subscript, /* mp_subscript */ + 0, /* mp_ass_subscript */ +}; + +static PySequenceMethods ByteArray_tp_as_sequence = { + 0, /* sq_length */ + 0, /* sq_concat */ + 0, /* sq_repeat */ + ByteArray_sq_item, /* sq_item */ + 0, /* sq_slice */ + 0, /* sq_ass_item */ + 0, /* sq_ass_slice */ + 0, /* sq_contains */ +}; + +static PyTypeObject ByteArrayType = { + PyObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type)) + 0, + "dbus.ByteArray", + 0, + 0, + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + str_subclass_tp_repr, /* tp_repr */ + 0, /* tp_as_number */ + &ByteArray_tp_as_sequence, /* tp_as_sequence */ + &ByteArray_tp_as_mapping, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + ByteArray_tp_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + DEFERRED_ADDRESS(&PyString_Type), /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + +static inline int +init_byte_types(void) +{ + ByteType.tp_base = &PyString_Type; + if (PyType_Ready(&ByteType) < 0) return 0; + /* disable the tp_print copied from PyString_Type, so tp_repr gets called as + desired */ + ByteType.tp_print = NULL; + + ByteArrayType.tp_base = &PyString_Type; + if (PyType_Ready(&ByteArrayType) < 0) return 0; + ByteArrayType.tp_print = NULL; + + return 1; +} + +static inline int +insert_byte_types(PyObject *this_module) +{ + Py_INCREF(&ByteType); + if (PyModule_AddObject(this_module, "Byte", + (PyObject *)&ByteType) < 0) return 0; + Py_INCREF(&ByteArrayType); + if (PyModule_AddObject(this_module, "ByteArray", + (PyObject *)&ByteArrayType) < 0) return 0; + + return 1; +} + +/* vim:set ft=c cino< sw=4 sts=4 et: */ diff --git a/_dbus_bindings/conn-impl.h b/_dbus_bindings/conn-impl.h new file mode 100644 index 0000000..daf146f --- /dev/null +++ b/_dbus_bindings/conn-impl.h @@ -0,0 +1,551 @@ +/* Implementation of the _dbus_bindings Connection type, a Python wrapper + * for DBusConnection. See also conn-methods-impl.h. + * + * Copyright (C) 2006 Collabora Ltd. <http://www.collabora.co.uk/> + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* Connection definition ============================================ */ + +PyDoc_STRVAR(Connection_tp_doc, +"Connection(address: str)\n\n" +); + +typedef struct Connection { + PyObject_HEAD + DBusConnection *conn; + /* A list of filter callbacks. */ + PyObject *filters; + /* A dict mapping object paths to one of: + * - tuples (unregister_callback or None, message_callback) + * - None (meaning unregistration from libdbus is in progress and nobody + * should touch this entry til we're finished) + */ + PyObject *object_paths; + + PyObject *weaklist; +} Connection; + +static PyTypeObject ConnectionType; + +static inline int Connection_Check(PyObject *o) +{ + return PyObject_TypeCheck(o, &ConnectionType); +} + +/* Helpers ========================================================== */ + +static PyObject *Connection_ExistingFromDBusConnection(DBusConnection *); +static PyObject *Connection_GetObjectPathHandlers(Connection *, PyObject *); +static DBusHandlerResult Connection_HandleMessage(Connection *, Message *, + PyObject *); + +static void +_object_path_unregister(DBusConnection *conn, void *user_data) +{ + PyGILState_STATE gil = PyGILState_Ensure(); + PyObject *tuple = NULL; + Connection *conn_obj = NULL; + PyObject *callable; + + conn_obj = (Connection *)Connection_ExistingFromDBusConnection(conn); + if (!conn_obj) goto out; + + DBG("Connection at %p unregistering object path %s", + conn_obj, PyString_AS_STRING((PyObject *)user_data)); + tuple = Connection_GetObjectPathHandlers(conn_obj, (PyObject *)user_data); + if (!tuple) goto out; + if (tuple == Py_None) goto out; + + DBG("%s", "... yes we have handlers for that object path"); + + /* 0'th item is the unregisterer (if that's a word) */ + callable = PyTuple_GetItem(tuple, 0); + if (callable && callable != Py_None) { + DBG("%s", "... and we even have an unregisterer"); + /* any return from the unregisterer is ignored */ + Py_XDECREF(PyObject_CallFunctionObjArgs(callable, conn_obj, NULL)); + } +out: + Py_XDECREF(conn_obj); + Py_XDECREF(tuple); + /* the user_data (a Python str) is no longer ref'd by the DBusConnection */ + Py_XDECREF((PyObject *)user_data); + PyGILState_Release(gil); +} + +static DBusHandlerResult +_object_path_message(DBusConnection *conn, DBusMessage *message, + void *user_data) +{ + DBusHandlerResult ret; + PyGILState_STATE gil = PyGILState_Ensure(); + Connection *conn_obj = NULL; + PyObject *tuple = NULL; + Message *msg_obj; + PyObject *callable; /* borrowed */ + + dbus_message_ref(message); + msg_obj = (Message *)Message_ConsumeDBusMessage(message); + if (!msg_obj) { + ret = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto out; + } + + conn_obj = (Connection *)Connection_ExistingFromDBusConnection(conn); + if (!conn_obj) { + ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto out; + } + + DBG("Connection at %p messaging object path %s", + conn_obj, PyString_AS_STRING((PyObject *)user_data)); + DBG_DUMP_MESSAGE(message); + tuple = Connection_GetObjectPathHandlers(conn_obj, (PyObject *)user_data); + if (!tuple || tuple == Py_None) { + ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto out; + } + + DBG("%s", "... yes we have handlers for that object path"); + + /* 1st item (0-based) is the message callback */ + callable = PyTuple_GetItem(tuple, 1); + if (!callable) { + DBG("%s", "... error getting message handler from tuple"); + ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + else if (callable == Py_None) { + /* there was actually no handler after all */ + DBG("%s", "... but those handlers don't do messages"); + ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + else { + DBG("%s", "... and we have a message handler for that object path"); + ret = Connection_HandleMessage(conn_obj, msg_obj, callable); + } + +out: + Py_XDECREF(msg_obj); + Py_XDECREF(conn_obj); + Py_XDECREF(tuple); + PyGILState_Release(gil); + return ret; +} + +static const DBusObjectPathVTable _object_path_vtable = { + _object_path_unregister, + _object_path_message, +}; + +static DBusHandlerResult +_filter_message(DBusConnection *conn, DBusMessage *message, void *user_data) +{ + DBusHandlerResult ret; + PyGILState_STATE gil = PyGILState_Ensure(); + Connection *conn_obj = NULL; + PyObject *callable = NULL; + Message *msg_obj; +#ifndef DBUS_PYTHON_DISABLE_CHECKS + int i, size; +#endif + + dbus_message_ref(message); + msg_obj = (Message *)Message_ConsumeDBusMessage(message); + if (!msg_obj) { + DBG("%s", "OOM while trying to construct Message"); + ret = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto out; + } + + conn_obj = (Connection *)Connection_ExistingFromDBusConnection(conn); + if (!conn_obj) { + DBG("%s", "failed to traverse DBusConnection -> Connection weakref"); + ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto out; + } + + /* The user_data is a pointer to a Python object. To avoid + * cross-library reference cycles, the DBusConnection isn't allowed + * to reference it. However, as long as the Connection is still + * alive, its ->filters list owns a reference to the same Python + * object, so the object should also still be alive. + * + * To ensure that this works, be careful whenever manipulating the + * filters list! (always put things in the list *before* giving + * them to libdbus, etc.) + */ +#ifdef DBUS_PYTHON_DISABLE_CHECKS + callable = (PyObject *)user_data; +#else + size = PyList_GET_SIZE(conn_obj->filters); + for (i = 0; i < size; i++) { + callable = PyList_GET_ITEM(conn_obj->filters, i); + if (callable == user_data) { + Py_INCREF(callable); + } + else { + callable = NULL; + } + } + + if (!callable) { + DBG("... filter %p has vanished from ->filters, so not calling it", + user_data); + ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto out; + } +#endif + + ret = Connection_HandleMessage(conn_obj, msg_obj, callable); +out: + Py_XDECREF(msg_obj); + Py_XDECREF(conn_obj); + Py_XDECREF(callable); + PyGILState_Release(gil); + return ret; +} + +/* D-Bus Connection user data slot, containing an owned reference to either + * the Connection, or a weakref to the Connection. + */ + +static dbus_int32_t _connection_python_slot; + +/* C API for main-loop hooks ======================================== */ + +/* Return a borrowed reference to the DBusConnection which underlies this + * Connection. */ +static DBusConnection * +Connection_BorrowDBusConnection(PyObject *self) +{ + if (!Connection_Check(self)) { + PyErr_SetString(PyExc_TypeError, "A dbus.Connection is required"); + return NULL; + } + return ((Connection *)self)->conn; +} + +/* Internal C API =================================================== */ + +/* Pass a message through a handler. */ +static DBusHandlerResult +Connection_HandleMessage(Connection *conn, Message *msg, PyObject *callable) +{ + PyObject *obj = PyObject_CallFunctionObjArgs(callable, conn, msg, + NULL); + if (obj == Py_None) { + DBG("%p: OK, handler %p returned None", conn, callable); + Py_DECREF(obj); + return DBUS_HANDLER_RESULT_HANDLED; + } + else if (obj == Py_NotImplemented) { + DBG("%p: handler %p returned NotImplemented, continuing", + conn, callable); + Py_DECREF(obj); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + else if (!obj) { + if (PyErr_ExceptionMatches(PyExc_MemoryError)) { + DBG_EXC("%p: handler %p caused OOM", conn, callable); + PyErr_Clear(); + return DBUS_HANDLER_RESULT_NEED_MEMORY; + } + DBG_EXC("%p: handler %p raised exception", conn, callable); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + else { + long i = PyInt_AsLong(obj); + DBG("%p: handler %p returned %ld", conn, callable, i); + Py_DECREF(obj); + if (i == -1 && PyErr_Occurred()) { + PyErr_SetString(PyExc_TypeError, "Return from D-Bus message " + "handler callback should be None, " + "NotImplemented or integer"); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + else if (i == DBUS_HANDLER_RESULT_HANDLED || + i == DBUS_HANDLER_RESULT_NOT_YET_HANDLED || + i == DBUS_HANDLER_RESULT_NEED_MEMORY) { + return i; + } + else { + PyErr_Format(PyExc_ValueError, "Integer return from " + "D-Bus message handler callback should " + "be a DBUS_HANDLER_RESULT_... constant, " + "not %d", (int)i); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + } +} + +/* On KeyError or if unregistration is in progress, return None. */ +static PyObject * +Connection_GetObjectPathHandlers(Connection *self, PyObject *path) +{ + PyObject *callbacks = PyDict_GetItem(self->object_paths, path); + if (!callbacks) { + if (PyErr_ExceptionMatches(PyExc_KeyError)) { + PyErr_Clear(); + Py_RETURN_NONE; + } + } + Py_INCREF(callbacks); + return callbacks; +} + +/* Return a new reference to a Python Connection or subclass corresponding + * to the DBusConnection conn. For use in callbacks. + * + * Raises AssertionError if the DBusConnection does not have a Connection. + */ +static PyObject * +Connection_ExistingFromDBusConnection(DBusConnection *conn) +{ + PyObject *self, *ref; + + Py_BEGIN_ALLOW_THREADS + ref = (PyObject *)dbus_connection_get_data(conn, + _connection_python_slot); + Py_END_ALLOW_THREADS + if (ref) { + self = PyWeakref_GetObject(ref); /* still a borrowed ref */ + if (self && self != Py_None && Connection_Check(self)) { + Py_INCREF(self); + return self; + } + } + + PyErr_SetString(PyExc_AssertionError, + "D-Bus connection does not have a Connection " + "instance associated with it"); + return NULL; +} + +/* Return a new reference to a Python Connection or subclass (given by cls) + * corresponding to the DBusConnection conn, which must have been newly + * created. For use by the Connection and Bus constructors. + * + * Raises AssertionError if the DBusConnection already has a Connection. + */ +static PyObject * +Connection_NewConsumingDBusConnection(PyTypeObject *cls, DBusConnection *conn) +{ + Connection *self; + PyObject *ref; + dbus_bool_t ok; + + Py_BEGIN_ALLOW_THREADS + ref = (PyObject *)dbus_connection_get_data(conn, + _connection_python_slot); + Py_END_ALLOW_THREADS + if (ref) { + self = (Connection *)PyWeakref_GetObject(ref); + ref = NULL; + if (self && (PyObject *)self != Py_None) { + self = NULL; + PyErr_SetString(PyExc_AssertionError, + "Newly created D-Bus connection already has a " + "Connection instance associated with it"); + return NULL; + } + } + ref = NULL; + + DBG("Constructing Connection from DBusConnection at %p", conn); + + self = (Connection *)(cls->tp_alloc(cls, 0)); + if (!self) goto err; + + self->conn = NULL; + self->filters = PyList_New(0); + if (!self->filters) goto err; + self->object_paths = PyDict_New(); + if (!self->object_paths) goto err; + + ref = PyWeakref_NewRef((PyObject *)self, NULL); + if (!ref) goto err; + + Py_BEGIN_ALLOW_THREADS + ok = dbus_connection_set_data(conn, _connection_python_slot, + (void *)ref, + (DBusFreeFunction)Glue_TakeGILAndXDecref); + Py_END_ALLOW_THREADS + + if (!ok) { + PyErr_NoMemory(); + goto err; + } + + self->conn = conn; + + return (PyObject *)self; + +err: + DBG("Failed to construct Connection from DBusConnection at %p", conn); + Py_XDECREF(self); + Py_XDECREF(ref); + if (conn) { + Py_BEGIN_ALLOW_THREADS + dbus_connection_close(conn); + dbus_connection_unref(conn); + Py_END_ALLOW_THREADS + } + return NULL; +} + +/* Connection type-methods ========================================== */ + +/* "Constructor" (the real constructor is Connection_NewFromDBusConnection, + * to which this delegates). */ +static PyObject * +Connection_tp_new(PyTypeObject *cls, PyObject *args, PyObject *kwargs) +{ + DBusConnection *conn; + const char *address; + DBusError error; + PyObject *self; + static char *argnames[] = {"address", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", argnames, &address)) { + return NULL; + } + + dbus_error_init(&error); + + /* We always open a private connection (at the libdbus level). Sharing + * is done in Python, to keep things simple. */ + Py_BEGIN_ALLOW_THREADS + conn = dbus_connection_open_private(address, &error); + Py_END_ALLOW_THREADS + + if (!conn) { + DBusException_ConsumeError(&error); + return NULL; + } + self = Connection_NewConsumingDBusConnection(cls, conn); + + return self; +} + +/* Destructor */ +static void Connection_tp_dealloc(Connection *self) +{ + DBusConnection *conn = self->conn; + self->conn = NULL; + + DBG("Deallocating Connection at %p (DBusConnection at %p)", self, conn); + + if (conn) { + Py_BEGIN_ALLOW_THREADS + dbus_connection_close(conn); + Py_END_ALLOW_THREADS + + dbus_connection_unref(conn); + } + + Py_XDECREF(self->filters); + Py_XDECREF(self->object_paths); + (self->ob_type->tp_free)((PyObject *)self); +} + +/* Connection_tp_methods */ +#include "conn-methods-impl.h" + +/* Connection type object =========================================== */ + +static PyTypeObject ConnectionType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_dbus_bindings.Connection", /*tp_name*/ + sizeof(Connection), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)Connection_tp_dealloc, + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_WEAKREFS | Py_TPFLAGS_BASETYPE, + Connection_tp_doc, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + offsetof(Connection, weaklist), /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + Connection_tp_methods, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ + 0, /*tp_base*/ + 0, /*tp_dict*/ + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + 0, /*tp_init*/ + 0, /*tp_alloc*/ + Connection_tp_new, /*tp_new*/ + 0, /*tp_free*/ + 0, /*tp_is_gc*/ +}; + +static inline dbus_bool_t +init_conn_types(void) +{ + /* Get a slot to store our weakref on DBus Connections */ + _connection_python_slot = -1; + if (!dbus_connection_allocate_data_slot(&_connection_python_slot)) { + return 0; + } + + ConnectionType.tp_new = PyType_GenericNew; + if (PyType_Ready(&ConnectionType) < 0) return 0; + return 1; +} + +static inline dbus_bool_t +insert_conn_types(PyObject *this_module) +{ + PyObject *c_api; + static void *dbus_bindings_API[1]; + + dbus_bindings_API[0] = (void *)Connection_BorrowDBusConnection; + c_api = PyCObject_FromVoidPtr ((void *)dbus_bindings_API, NULL); + if (c_api) { + PyModule_AddObject(this_module, "_C_API", c_api); + } + + if (PyModule_AddObject(this_module, "Connection", + (PyObject *)&ConnectionType) < 0) return 0; + return 1; +} + +/* vim:set ft=c cino< sw=4 sts=4 et: */ diff --git a/_dbus_bindings/conn-methods-impl.h b/_dbus_bindings/conn-methods-impl.h new file mode 100644 index 0000000..f5b2125 --- /dev/null +++ b/_dbus_bindings/conn-methods-impl.h @@ -0,0 +1,653 @@ +/* Implementation of normal Python-accessible methods on the _dbus_bindings + * Connection type; separated out to keep the file size manageable. + * + * Copyright (C) 2006 Collabora Ltd. <http://www.collabora.co.uk/> + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +PyDoc_STRVAR(Connection_close__doc__, +"close()\n\n" +"Close the connection."); +static PyObject * +Connection_close (Connection *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":close")) return NULL; + /* Because the user explicitly asked to close the connection, we'll even + let them close shared connections. */ + if (self->conn) { + Py_BEGIN_ALLOW_THREADS + dbus_connection_close (self->conn); + Py_END_ALLOW_THREADS + } + Py_RETURN_NONE; +} + +PyDoc_STRVAR(Connection_get_is_connected__doc__, +"get_is_connected() -> bool\n\n" +"Return true if this Connection is connected.\n"); +static PyObject * +Connection_get_is_connected (Connection *self, PyObject *args) +{ + dbus_bool_t ret; + if (!PyArg_ParseTuple(args, ":get_is_connected")) return NULL; + Py_BEGIN_ALLOW_THREADS + ret = dbus_connection_get_is_connected (self->conn); + Py_END_ALLOW_THREADS + return PyBool_FromLong (ret); +} + +PyDoc_STRVAR(Connection_get_is_authenticated__doc__, +"get_is_authenticated() -> bool\n\n" +"Return true if this Connection was ever authenticated.\n"); +static PyObject * +Connection_get_is_authenticated (Connection *self, PyObject *args) +{ + dbus_bool_t ret; + if (!PyArg_ParseTuple(args, ":get_is_authenticated")) return NULL; + Py_BEGIN_ALLOW_THREADS + ret = dbus_connection_get_is_authenticated (self->conn); + Py_END_ALLOW_THREADS + return PyBool_FromLong (ret); +} + +PyDoc_STRVAR(Connection_set_exit_on_disconnect__doc__, +"set_exit_on_disconnect(bool)\n\n" +"Set whether the C function ``_exit`` will be called when this Connection\n" +"becomes disconnected. This will cause the program to exit without calling\n" +"any cleanup code or exit handlers.\n" +"\n" +"The default is for this feature to be disabled for Connections and enabled\n" +"for Buses.\n"); +static PyObject * +Connection_set_exit_on_disconnect (Connection *self, PyObject *args) +{ + int exit_on_disconnect; + if (!PyArg_ParseTuple(args, "i:set_exit_on_disconnect", + &exit_on_disconnect)) { + return NULL; + } + Py_BEGIN_ALLOW_THREADS + dbus_connection_set_exit_on_disconnect (self->conn, + exit_on_disconnect ? 1 : 0); + Py_END_ALLOW_THREADS + Py_RETURN_NONE; +} + +PyDoc_STRVAR(Connection__send__doc__, +"_send(msg: Message) -> long\n\n" +"Queue the given message for sending, and return the message serial number.\n" +); +static PyObject * +Connection__send (Connection *self, PyObject *args) +{ + dbus_bool_t ok; + PyObject *obj; + DBusMessage *msg; + dbus_uint32_t serial; + + if (!PyArg_ParseTuple(args, "O", &obj)) return NULL; + + msg = Message_BorrowDBusMessage(obj); + if (!msg) return NULL; + + Py_BEGIN_ALLOW_THREADS + ok = dbus_connection_send(self->conn, msg, &serial); + Py_END_ALLOW_THREADS + + if (!ok) { + return PyErr_NoMemory(); + } + + return PyLong_FromUnsignedLong(serial); +} + +/* The timeout is in seconds here, since that's conventional in Python. */ +PyDoc_STRVAR(Connection__send_with_reply__doc__, +"_send_with_reply(msg: Message, reply_handler: callable[, timeout_s: float])" +" -> PendingCall\n\n" +"Queue the message for sending; expect a reply via the returned PendingCall.\n" +"\n" +":Parameters:\n" +" `msg` : Message\n" +" The message to be sent\n" +" `reply_handler` : callable\n" +" Asynchronous reply handler: will be called with one positional\n" +" parameter, a Message instance representing the reply.\n" +" `timeout_s` : float\n" +" If the reply takes more than this many seconds, a timeout error\n" +" will be created locally and raised instead. If this timeout is\n" +" negative (default), a sane default (supplied by libdbus) is used.\n" +":Returns\n" +" A `PendingCall` instance which can be used to cancel the pending call.\n" +"\n" +); +static PyObject * +Connection__send_with_reply(Connection *self, PyObject *args) +{ + dbus_bool_t ok; + double timeout_s = -1.0; + int timeout_ms; + PyObject *obj, *callable; + DBusMessage *msg; + DBusPendingCall *pending; + + if (!PyArg_ParseTuple(args, "OO|f:send_with_reply", &obj, &callable, + &timeout_s)) { + return NULL; + } + + msg = Message_BorrowDBusMessage(obj); + if (!msg) return NULL; + + if (timeout_s < 0) { + timeout_ms = -1; + } + else { + if (timeout_s > ((double)INT_MAX) / 1000.0) { + PyErr_SetString(PyExc_ValueError, "Timeout too long"); + return NULL; + } + timeout_ms = (int)(timeout_s * 1000.0); + } + + Py_BEGIN_ALLOW_THREADS + ok = dbus_connection_send_with_reply(self->conn, msg, &pending, + timeout_ms); + Py_END_ALLOW_THREADS + + if (!ok) { + return PyErr_NoMemory(); + } + + return PendingCall_ConsumeDBusPendingCall(pending, callable); +} + +/* Again, the timeout is in seconds, since that's conventional in Python. */ +PyDoc_STRVAR(Connection__send_with_reply_and_block__doc__, +"_send_with_reply_and_block(msg: Message, [, timeout_s: float])" +" -> Message\n\n" +"Send the message and block while waiting for a reply.\n" +"\n" +"This does not re-enter the main loop, so it can lead to a deadlock, if\n" +"the called method tries to make a synchronous call to a method in this\n" +"application. As such, it's probably a bad idea.\n" +"\n" +":Parameters:\n" +" `msg` : Message\n" +" The message to be sent\n" +" `timeout_s` : float\n" +" If the reply takes more than this many seconds, a timeout error\n" +" will be created locally and raised instead. If this timeout is\n" +" negative (default), a sane default (supplied by libdbus) is used.\n" +":Returns\n" +" A `Message` instance (probably a `MethodReturnMessage`) on success\n" +":Raises\n" +" A `DBusException` on error (including if the reply arrives but is an\n" +" error message)\n" +"\n" +); +static PyObject * +Connection__send_with_reply_and_block(Connection *self, PyObject *args) +{ + double timeout_s = -1.0; + int timeout_ms; + PyObject *obj; + DBusMessage *msg, *reply; + DBusError error; + + if (!PyArg_ParseTuple(args, "O|f:_send_with_reply_and_block", &obj, + &timeout_s)) { + return NULL; + } + + msg = Message_BorrowDBusMessage(obj); + if (!msg) return NULL; + + if (timeout_s < 0) { + timeout_ms = -1; + } + else { + if (timeout_s > ((double)INT_MAX) / 1000.0) { + PyErr_SetString(PyExc_ValueError, "Timeout too long"); + return NULL; + } + timeout_ms = (int)(timeout_s * 1000.0); + } + + dbus_error_init(&error); + Py_BEGIN_ALLOW_THREADS + reply = dbus_connection_send_with_reply_and_block(self->conn, msg, + timeout_ms, &error); + Py_END_ALLOW_THREADS + + if (!reply) { + return DBusException_ConsumeError(&error); + } + return Message_ConsumeDBusMessage(reply); +} + +PyDoc_STRVAR(Connection_flush__doc__, +"flush()\n\n" +"Block until the outgoing message queue is empty.\n"); +static PyObject * +Connection_flush (Connection *self, PyObject *args) +{ + Py_BEGIN_ALLOW_THREADS + dbus_connection_flush (self->conn); + Py_END_ALLOW_THREADS + Py_RETURN_NONE; +} + +/* Unsupported: + * dbus_connection_preallocate_send + * dbus_connection_free_preallocated_send + * dbus_connection_send_preallocated + * dbus_connection_borrow_message + * dbus_connection_return_message + * dbus_connection_steal_borrowed_message + * dbus_connection_pop_message + */ + +/* Non-main-loop handling not yet implemented: */ + /* dbus_connection_read_write_dispatch */ + /* dbus_connection_read_write */ + +/* Main loop handling not yet implemented: */ + /* dbus_connection_get_dispatch_status */ + /* dbus_connection_dispatch */ + /* dbus_connection_set_watch_functions */ + /* dbus_connection_set_timeout_functions */ + /* dbus_connection_set_wakeup_main_function */ + /* dbus_connection_set_dispatch_status_function */ + +/* Normally in Python this would be called fileno(), but I don't want to + * encourage people to select() on it */ +PyDoc_STRVAR(Connection_get_unix_fd__doc__, +"get_unix_fd() -> int or None\n\n" +"Get the connection's UNIX file descriptor, if any.\n\n" +"This can be used for SELinux access control checks with ``getpeercon()``\n" +"for example. **Do not** read or write to the file descriptor, or try to\n" +"``select()`` on it.\n"); +static PyObject * +Connection_get_unix_fd (Connection *self, PyObject *unused) +{ + int fd; + dbus_bool_t ok; + + Py_BEGIN_ALLOW_THREADS + ok = dbus_connection_get_unix_fd (self->conn, &fd); + Py_END_ALLOW_THREADS + if (!ok) Py_RETURN_NONE; + return PyInt_FromLong(fd); +} + +PyDoc_STRVAR(Connection_get_peer_unix_user__doc__, +"get_peer_unix_user() -> long or None\n\n" +"Get the UNIX user ID at the other end of the connection, if it has been\n" +"authenticated. Return None if this is a non-UNIX platform or the\n" +"connection has not been authenticated.\n"); +static PyObject * +Connection_get_peer_unix_user (Connection *self, PyObject *unused) +{ + unsigned long uid; + dbus_bool_t ok; + + Py_BEGIN_ALLOW_THREADS + ok = dbus_connection_get_unix_user (self->conn, &uid); + Py_END_ALLOW_THREADS + if (!ok) Py_RETURN_NONE; + return PyLong_FromUnsignedLong (uid); +} + +PyDoc_STRVAR(Connection_get_peer_unix_process_id__doc__, +"get_peer_unix_process_id() -> long or None\n\n" +"Get the UNIX process ID at the other end of the connection, if it has been\n" +"authenticated. Return None if this is a non-UNIX platform or the\n" +"connection has not been authenticated.\n"); +static PyObject * +Connection_get_peer_unix_process_id (Connection *self, PyObject *unused) +{ + unsigned long pid; + dbus_bool_t ok; + + Py_BEGIN_ALLOW_THREADS + ok = dbus_connection_get_unix_process_id (self->conn, &pid); + Py_END_ALLOW_THREADS + if (!ok) Py_RETURN_NONE; + return PyLong_FromUnsignedLong (pid); +} + +/* TODO: wrap dbus_connection_set_unix_user_function Pythonically */ + +PyDoc_STRVAR(Connection__add_filter__doc__, +"_add_filter(callable)\n\n" +"Add the given message filter to the internal list.\n\n" +"Filters are handlers that are run on all incoming messages, prior to the\n" +"objects registered with `_register_object_path`.\n" +"Filters are run in the order that they were added. The same handler can\n" +"be added as a filter more than once, in which case it will be run more\n" +"than once. Filters added during a filter callback won't be run on the\n" +"message being processed.\n" +); +static PyObject * +Connection__add_filter(Connection *self, PyObject *callable) +{ + dbus_bool_t ok; + + /* The callable must be referenced by ->filters *before* it is + * given to libdbus, which does not own a reference to it. + */ + if (PyList_Append(self->filters, callable) < 0) { + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + ok = dbus_connection_add_filter(self->conn, _filter_message, callable, + NULL); + Py_END_ALLOW_THREADS + + if (!ok) { + Py_XDECREF(PyObject_CallMethod(self->filters, "remove", "(O)", + callable)); + PyErr_NoMemory(); + return NULL; + } + Py_RETURN_NONE; +} + +PyDoc_STRVAR(Connection__remove_filter__doc__, +"_remove_filter(callable)\n\n" +"Remove the given message filter (see `_add_filter` for details).\n"); +static PyObject * +Connection__remove_filter(Connection *self, PyObject *callable) +{ + PyObject *obj; + + /* It's safe to do this before removing it from libdbus, because + * the presence of callable in our arguments means we have a ref + * to it. */ + obj = PyObject_CallMethod(self->filters, "remove", "(O)", callable); + if (!obj) return NULL; + Py_DECREF(obj); + + Py_BEGIN_ALLOW_THREADS + dbus_connection_remove_filter(self->conn, _filter_message, callable); + Py_END_ALLOW_THREADS + + Py_RETURN_NONE; +} + +/* object exports not yet implemented: */ + +PyDoc_STRVAR(Connection__register_object_path__doc__, +"_register_object_path(path: str, on_message: callable[, on_unregister: " +"callable][, **kwargs])\n\n" +"Keyword arguments accepted:\n" +"fallback: bool (default False): if True, when a message arrives for a\n" +"'subdirectory' of the given path and there is no more specific handler,\n" +"use this handler. Normally this handler is only run if the paths match\n" +"exactly.\n" +); +static PyObject * +Connection__register_object_path(Connection *self, PyObject *args, + PyObject *kwargs) +{ + dbus_bool_t ok; + int fallback = 0; + PyObject *callbacks, *path, *tuple, *on_message, *on_unregister = Py_None; + static char *argnames[] = {"path", "on_message", "on_unregister", + "fallback", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, + "OO|Oi:_register_object_path", + argnames, + &path, + &on_message, &on_unregister, + &fallback)) return NULL; + + /* Take a reference to path, which we give away to libdbus in a moment. + + Also, path needs to be a string (not a subclass which could do something + mad) to preserve the desirable property that the DBusConnection can + never strongly reference the Connection, even indirectly. + We make an exception for ObjectPaths because they're equally simple, + are known to have the same __eq__ and __hash__, and are what calling code + ought to be using. */ + if (PyString_CheckExact(path) || path->ob_type == &ObjectPathType) { + Py_INCREF(path); + } + else if (PyUnicode_Check(path)) { + path = PyUnicode_AsUTF8String(path); + if (!path) return NULL; + } + else if (PyString_Check(path)) { + path = PyString_FromString(PyString_AS_STRING(path)); + if (!path) return NULL; + } + + if (!_validate_object_path(PyString_AS_STRING(path))) { + Py_DECREF(path); + return NULL; + } + + tuple = Py_BuildValue("(OO)", on_unregister, on_message); + if (!tuple) { + Py_DECREF(path); + return NULL; + } + + /* Guard against registering a handler that already exists. */ + callbacks = PyDict_GetItem(self->object_paths, path); + if (callbacks && callbacks != Py_None) { + PyErr_Format(PyExc_KeyError, "Can't register the object-path " + "handler for '%s': there is already a handler", + PyString_AS_STRING(path)); + Py_DECREF(tuple); + Py_DECREF(path); + return NULL; + } + + /* Pre-allocate a slot in the dictionary, so we know we'll be able + * to replace it with the callbacks without OOM. + * This ensures we can keep libdbus' opinion of whether those + * paths are handled in sync with our own. */ + if (PyDict_SetItem(self->object_paths, path, Py_None) < 0) { + Py_DECREF(tuple); + Py_DECREF(path); + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + if (fallback) { + ok = dbus_connection_register_fallback(self->conn, + PyString_AS_STRING(path), + &_object_path_vtable, + path); + } + else { + ok = dbus_connection_register_object_path(self->conn, + PyString_AS_STRING(path), + &_object_path_vtable, + path); + } + Py_END_ALLOW_THREADS + + if (ok) { + if (PyDict_SetItem(self->object_paths, path, tuple) < 0) { + /* That shouldn't have happened, we already allocated enough + memory for it. Oh well, try to undo the registration to keep + things in sync. If this fails too, we've leaked a bit of + memory in libdbus, but tbh we should never get here anyway. */ + Py_BEGIN_ALLOW_THREADS + ok = dbus_connection_unregister_object_path(self->conn, + PyString_AS_STRING(path)); + Py_END_ALLOW_THREADS + return NULL; + } + /* don't DECREF path: libdbus owns a ref now */ + Py_DECREF(tuple); + Py_RETURN_NONE; + } + else { + /* Oops, OOM. Tidy up, if we can, ignoring any error. */ + PyDict_DelItem(self->object_paths, path); + PyErr_Clear(); + Py_DECREF(tuple); + Py_DECREF(path); + PyErr_NoMemory(); + return NULL; + } +} + +PyDoc_STRVAR(Connection__unregister_object_path__doc__, +"_unregister_object_path(path: str)\n\n" +"" +); +static PyObject * +Connection__unregister_object_path(Connection *self, PyObject *args, + PyObject *kwargs) +{ + dbus_bool_t ok; + PyObject *path; + PyObject *callbacks; + static char *argnames[] = {"path", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, + "O!:_unregister_object_path", + argnames, + &PyString_Type, &path)) return NULL; + + /* Take a ref to the path. Same comments as for _register_object_path. */ + if (PyString_CheckExact(path) || path->ob_type == &ObjectPathType) { + Py_INCREF(path); + } + else { + path = PyString_FromString(PyString_AS_STRING(path)); + if (!path) return NULL; + } + + /* Guard against unregistering a handler that doesn't, in fact, exist, + or whose unregistration is already in progress. */ + callbacks = PyDict_GetItem(self->object_paths, path); + if (!callbacks || callbacks == Py_None) { + PyErr_Format(PyExc_KeyError, "Can't unregister the object-path " + "handler for '%s': there is no such handler", + PyString_AS_STRING(path)); + Py_DECREF(path); + return NULL; + } + + /* Hang on to a reference to the callbacks for the moment. */ + Py_INCREF(callbacks); + + /* Get rid of the object-path while we still have the GIL, to + guard against unregistering twice from different threads (which + causes undefined behaviour in libdbus). + + Because deletion would make it possible for the re-insertion below + to fail, we instead set the handler to None as a placeholder. + */ + if (PyDict_SetItem(self->object_paths, path, Py_None) < 0) { + /* If that failed, there's no need to be paranoid as below - the + callbacks are still set, so we failed, but at least everything + is in sync. */ + Py_DECREF(callbacks); + Py_DECREF(path); + return NULL; + } + + /* BEGIN PARANOIA + This is something of a critical section - the dict of object-paths + and libdbus' internal structures are out of sync for a bit. We have + to be able to cope with that. + + It's really annoying that dbus_connection_unregister_object_path + can fail, *and* has undefined behaviour if the object path has + already been unregistered. Either/or would be fine. + */ + + Py_BEGIN_ALLOW_THREADS + ok = dbus_connection_unregister_object_path(self->conn, + PyString_AS_STRING(path)); + Py_END_ALLOW_THREADS + + if (ok) { + Py_DECREF(callbacks); + PyDict_DelItem(self->object_paths, path); + /* END PARANOIA on successful code path */ + /* The above can't fail unless by some strange trickery the key is no + longer present. Ignore any errors. */ + Py_DECREF(path); + PyErr_Clear(); + Py_RETURN_NONE; + } + else { + /* Oops, OOM. Put the callbacks back in the dict so + * we'll have another go if/when the user frees some memory + * and tries calling this method again. */ + PyDict_SetItem(self->object_paths, path, callbacks); + /* END PARANOIA on failing code path */ + /* If the SetItem failed, there's nothing we can do about it - but + since we know it's an existing entry, it shouldn't be able to fail + anyway. */ + Py_DECREF(path); + Py_DECREF(callbacks); + return PyErr_NoMemory(); + } +} + + /* dbus_connection_get_object_path_data - not useful to Python, + * the object path data is just a PyString containing the path */ + /* dbus_connection_list_registered could be useful, though */ + +/* dbus_connection_set_change_sigpipe - sets global state */ + +/* Maxima. Does Python code ever need to manipulate these? + * OTOH they're easy to wrap */ + /* dbus_connection_set_max_message_size */ + /* dbus_connection_get_max_message_size */ + /* dbus_connection_set_max_received_size */ + /* dbus_connection_get_max_received_size */ + +/* dbus_connection_get_outgoing_size - almost certainly unneeded */ + +static struct PyMethodDef Connection_tp_methods[] = { +#define ENTRY(name, flags) {#name, (PyCFunction)Connection_##name, flags, Connection_##name##__doc__} + ENTRY(close, METH_NOARGS), + ENTRY(flush, METH_NOARGS), + ENTRY(get_is_connected, METH_NOARGS), + ENTRY(get_is_authenticated, METH_NOARGS), + ENTRY(set_exit_on_disconnect, METH_VARARGS), + ENTRY(get_unix_fd, METH_NOARGS), + ENTRY(get_peer_unix_user, METH_NOARGS), + ENTRY(get_peer_unix_process_id, METH_NOARGS), + ENTRY(_add_filter, METH_O), + ENTRY(_register_object_path, METH_VARARGS|METH_KEYWORDS), + ENTRY(_remove_filter, METH_O), + ENTRY(_send, METH_VARARGS), + ENTRY(_send_with_reply, METH_VARARGS), + ENTRY(_send_with_reply_and_block, METH_VARARGS), + ENTRY(_unregister_object_path, METH_VARARGS|METH_KEYWORDS), + {NULL}, +#undef ENTRY +}; + +/* vim:set ft=c cino< sw=4 sts=4 et: */ diff --git a/_dbus_bindings/containers-impl.h b/_dbus_bindings/containers-impl.h new file mode 100644 index 0000000..d0d8a1a --- /dev/null +++ b/_dbus_bindings/containers-impl.h @@ -0,0 +1,743 @@ +/* D-Bus container types: Variant, Array and Dict. + * + * Copyright (C) 2006 Collabora Ltd. + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <stdint.h> + +/* IntNN, UIntNN ==================================================== */ + +static PyTypeObject VariantType, ArrayType, DictType; + +#define DEFINE_CHECK(type) \ +static inline int type##_Check (PyObject *o) \ +{ \ + return (o->ob_type == &type##Type) \ + || PyObject_IsInstance(o, (PyObject *)&type##Type); \ +} +DEFINE_CHECK(Variant) +DEFINE_CHECK(Array) +DEFINE_CHECK(Dict) +#undef DEFINE_CHECK + +PyDoc_STRVAR(Array_tp_doc, +"Array([iterable, ][signature=Signature(...)])\n\n" +"An array of similar items, implemented as a subtype of list.\n" +"\n" +"As currently implemented, an Array behaves just like a list, but\n" +"with the addition of a ``signature`` property set by the constructor;\n" +"conversion of its items to D-Bus types is only done when it's sent in\n" +"a Message. This may change in future so validation is done earlier.\n" +"\n" +"The signature may be None, in which case when the Array is sent over\n" +"D-Bus, the item signature will be guessed from the first element.\n"); + +typedef struct { + PyListObject super; + PyObject *signature; +} Array; + +static struct PyMemberDef Array_tp_members[] = { + {"signature", T_OBJECT, offsetof(Array, signature), READONLY, + "The D-Bus signature of each element of this Array (a Signature " + "instance)"}, + {NULL}, +}; + +static void +Array_tp_dealloc (Array *self) +{ + Py_XDECREF(self->signature); + self->signature = NULL; + (PyList_Type.tp_dealloc)((PyObject *)self); +} + +static PyObject * +Array_tp_repr(Array *self) +{ + PyObject *parent_repr = (PyList_Type.tp_repr)((PyObject *)self); + PyObject *sig_repr = PyObject_Repr(self->signature); + PyObject *my_repr = NULL; + + if (!parent_repr) goto finally; + if (!sig_repr) goto finally; + my_repr = PyString_FromFormat("%s(%s, signature=%s)", + self->super.ob_type->tp_name, + PyString_AS_STRING(parent_repr), + PyString_AS_STRING(sig_repr)); +finally: + Py_XDECREF(parent_repr); + Py_XDECREF(sig_repr); + return my_repr; +} + +static PyObject * +Array_tp_new (PyTypeObject *cls, PyObject *args, PyObject *kwargs) +{ + Array *self = (Array *)(PyList_Type.tp_new)(cls, args, kwargs); + if (!self) return NULL; + Py_INCREF(Py_None); + self->signature = Py_None; + return (PyObject *)self; +} + +static int +Array_tp_init (Array *self, PyObject *args, PyObject *kwargs) +{ + PyObject *obj = empty_tuple; + PyObject *signature = NULL; + PyObject *tuple; + static char *argnames[] = {"iterable", "signature", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OO:__init__", argnames, + &obj, &signature)) { + return -1; + } + + /* convert signature from a borrowed ref of unknown type to an owned ref + of type Signature (or None) */ + if (!signature) signature = Py_None; + if (signature == Py_None + || PyObject_IsInstance(signature, (PyObject *)&SignatureType)) { + Py_INCREF(signature); + } + else { + signature = PyObject_CallFunction((PyObject *)&SignatureType, "(O)", + signature); + if (!signature) return -1; + } + + tuple = Py_BuildValue("(O)", obj); + if (!tuple) { + Py_DECREF(signature); + return -1; + } + if ((PyList_Type.tp_init)((PyObject *)self, tuple, NULL) < 0) { + Py_DECREF(tuple); + Py_DECREF(signature); + return -1; + } + Py_DECREF(tuple); + + Py_XDECREF(self->signature); + self->signature = signature; + return 0; +} + +static PyTypeObject ArrayType = { + PyObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type)) + 0, + "dbus.Array", + sizeof(Array), + 0, + (destructor)Array_tp_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)Array_tp_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + Array_tp_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + Array_tp_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)Array_tp_init, /* tp_init */ + 0, /* tp_alloc */ + Array_tp_new, /* tp_new */ +}; + +PyDoc_STRVAR(Dict_tp_doc, +"Dictionary([mapping_or_iterable, ][signature=Signature(...)])\n\n" +"An mapping whose keys are similar and whose values are similar,\n" +"implemented as a subtype of dict.\n" +"\n" +"As currently implemented, a Dictionary behaves just like a dict, but\n" +"with the addition of a ``signature`` property set by the constructor;\n" +"conversion of its items to D-Bus types is only done when it's sent in\n" +"a Message. This may change in future so validation is done earlier.\n" +"\n" +"The signature may be None, in which case when the Dictionary is sent over\n" +"D-Bus, the key and value signatures will be guessed from some arbitrary.\n" +"element.\n"); + +typedef struct { + PyDictObject super; + PyObject *signature; +} Dict; + +static struct PyMemberDef Dict_tp_members[] = { + {"signature", T_OBJECT, offsetof(Dict, signature), READONLY, + "The D-Bus signature of each key in this Dictionary, followed by " + "that of each value in this Dictionary, as a Signature instance."}, + {NULL}, +}; + +static void +Dict_tp_dealloc (Dict *self) +{ + Py_XDECREF(self->signature); + self->signature = NULL; + (PyDict_Type.tp_dealloc)((PyObject *)self); +} + +static PyObject * +Dict_tp_repr(Dict *self) +{ + PyObject *parent_repr = (PyDict_Type.tp_repr)((PyObject *)self); + PyObject *sig_repr = PyObject_Repr(self->signature); + PyObject *my_repr = NULL; + + if (!parent_repr) goto finally; + if (!sig_repr) goto finally; + my_repr = PyString_FromFormat("%s(%s, signature=%s)", + self->super.ob_type->tp_name, + PyString_AS_STRING(parent_repr), + PyString_AS_STRING(sig_repr)); +finally: + Py_XDECREF(parent_repr); + Py_XDECREF(sig_repr); + return my_repr; +} + +static PyObject * +Dict_tp_new(PyTypeObject *cls, PyObject *args, PyObject *kwargs) +{ + Dict *self = (Dict *)(PyDict_Type.tp_new)(cls, args, kwargs); + if (!self) return NULL; + Py_INCREF(Py_None); + self->signature = Py_None; + return (PyObject *)self; +} + +static int +Dict_tp_init(Dict *self, PyObject *args, PyObject *kwargs) +{ + PyObject *obj = empty_tuple; + PyObject *signature = NULL; + PyObject *tuple; + static char *argnames[] = {"mapping_or_iterable", "signature", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OO:__init__", argnames, + &obj, &signature)) { + return -1; + } + + /* convert signature from a borrowed ref of unknown type to an owned ref + of type Signature (or None) */ + if (!signature) signature = Py_None; + if (signature == Py_None + || PyObject_IsInstance(signature, (PyObject *)&SignatureType)) { + Py_INCREF(signature); + } + else { + signature = PyObject_CallFunction((PyObject *)&SignatureType, "(O)", + signature); + if (!signature) return -1; + } + + tuple = Py_BuildValue("(O)", obj); + if (!tuple) { + Py_DECREF(signature); + return -1; + } + + if ((PyDict_Type.tp_init((PyObject *)self, tuple, NULL)) < 0) { + Py_DECREF(tuple); + Py_DECREF(signature); + return -1; + } + Py_DECREF(tuple); + + Py_XDECREF(self->signature); + self->signature = signature; + return 0; +} + +static PyTypeObject DictType = { + PyObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type)) + 0, + "dbus.Dictionary", + sizeof(Dict), + 0, + (destructor)Dict_tp_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)Dict_tp_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + Dict_tp_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + Dict_tp_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)Dict_tp_init, /* tp_init */ + 0, /* tp_alloc */ + Dict_tp_new, /* tp_new */ +}; + +PyDoc_STRVAR(Variant_tp_doc, +"Variant(object[, signature=Signature(...)])\n" +"\n" +"A container for D-Bus types. This is an immutable 'value object' like str\n" +"and int.\n" +"\n" +"The signature may be omitted or None, in which case it will be guessed \n" +"at construction time from the wrapped object, with the same algorithm\n" +"used when objects are appended to a message.\n" +"\n" +"A Variant v supports the following operations:\n" +"\n" +"* v(), v.object (the contained object)\n\n" +"* v.signature (the signature as a dbus.Signature)\n\n" +"* v == w if and only if v, w are Variants with the same signature and\n" +" the objects they contain compare equal\n\n" +"* bool(v) == bool(v.object)\n" +"* str(v) == str(v.object)\n\n" +"* repr(v) returns something like \"Variant(123, signature='i')\"\n\n" +"* int(v) == int(v.object), if supported; ditto long(v), float(v),\n" +" hex(v), oct(v)\n\n" +"* iter(v) iterates over the contained object, if it's iterable\n\n" +); + +typedef struct { + PyObject_HEAD + PyObject *object; + PyObject *signature; +} Variant; + +static struct PyMemberDef Variant_tp_members[] = { + {"object", T_OBJECT, offsetof(Variant, object), READONLY, + "The wrapped object"}, + {"signature", T_OBJECT, offsetof(Variant, signature), READONLY, + "The D-Bus signature of the contained object (a Signature instance)"}, + {NULL}, +}; + +static void +Variant_tp_dealloc (Variant *self) +{ + Py_XDECREF(self->object); + self->object = NULL; + Py_XDECREF(self->signature); + self->signature = NULL; + (self->ob_type->tp_free)(self); +} + +/* forward declaration */ +static PyObject *Message_guess_signature(PyObject *unused, PyObject *args); + +static PyObject * +Variant_tp_new (PyTypeObject *cls, PyObject *args, PyObject *kwargs) +{ + Variant *self; + PyObject *obj; + PyObject *signature = NULL; + static char *argnames[] = {"object", "signature", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O:__new__", argnames, + &obj, &signature)) { + return NULL; + } + + /* convert signature from a borrowed ref of unknown type to an owned ref + of type Signature, somehow... */ + if (!signature || signature == Py_None) { + /* There's no signature, we'll have to guess what the user wanted */ + PyObject *tuple = Py_BuildValue("(O)", obj); + + if (!tuple) return NULL; + signature = Message_guess_signature(NULL, tuple); + Py_DECREF(tuple); + if (!signature) return NULL; + } + else if (PyObject_IsInstance(signature, (PyObject *)&SignatureType)) { + /* Already a Signature, so that's easy */ + Py_INCREF(signature); + } + else { + /* See what the Signature constructor thinks of it */ + signature = PyObject_CallFunction((PyObject *)&SignatureType, "(O)", + signature); + if (!signature) return NULL; + } + + self = (Variant *)(cls->tp_alloc)(cls, 0); + if (!self) { + Py_DECREF(signature); + return NULL; + } + + self->signature = signature; + Py_INCREF (obj); + self->object = obj; + return (PyObject *)self; +} + +static PyObject * +Variant_tp_repr (Variant *self) +{ + PyObject *my_repr = NULL; + PyObject *obj_repr; + PyObject *sig_repr; + + if (!self->object) { + return PyString_FromString("<invalid dbus.Variant with no object>"); + } + + obj_repr = PyObject_Repr(self->object); + sig_repr = PyObject_Repr(self->signature); + if (obj_repr && sig_repr) { + my_repr = PyString_FromFormat("%s(%s, signature=%s)", + self->ob_type->tp_name, + PyString_AS_STRING(obj_repr), + PyString_AS_STRING(sig_repr)); + } + /* whether my_repr is NULL or not: */ + Py_XDECREF(obj_repr); + Py_XDECREF(sig_repr); + return my_repr; +} + +static PyObject * +Variant_tp_iter(Variant *self) +{ + return PyObject_GetIter(self->object); +} + +static PyObject * +Variant_tp_call(Variant *self, PyObject *args, PyObject *kwargs) +{ + if ((args && PyObject_Size(args)) || (kwargs && PyObject_Size(kwargs))) { + PyErr_SetString(PyExc_TypeError, "Variant.__call__ takes no arguments"); + return NULL; + } + Py_INCREF(self->object); + return self->object; +} + +static long +Variant_tp_hash(Variant *self) +{ + return PyObject_Hash(self->object); +} + +static PyObject * +Variant_tp_richcompare(Variant *self, PyObject *other, int opid) +{ + PyObject *ret; + + if (opid == Py_EQ || opid == Py_NE) { + if (Variant_Check(other)) { + Variant *vo = (Variant *)other; + ret = PyObject_RichCompare(self->signature, vo->signature, opid); + if (!ret) return NULL; + if (!PyObject_IsTrue(ret)) { + if (opid == Py_EQ) { + /* Signatures are unequal, we require equal. Fail. */ + return ret; + } + } + else { + if (opid == Py_NE) { + /* Signatures are unequal, succeed. */ + return ret; + } + } + Py_DECREF(ret); + ret = PyObject_RichCompare(self->object, vo->object, opid); + if (!ret) return NULL; + if (!PyObject_IsTrue(ret)) { + if (opid == Py_EQ) { + /* Objects are unequal, we require equal. Fail. */ + return ret; + } + } + else { + if (opid == Py_NE) { + /* Objects are unequal, succeed. */ + return ret; + } + } + Py_DECREF(ret); + if (opid == Py_NE) { + Py_RETURN_FALSE; + } + else { + Py_RETURN_TRUE; + } + } + /* Types are dissimilar */ + if (opid == Py_NE) { + Py_RETURN_TRUE; + } + else { + Py_RETURN_FALSE; + } + } + else { + PyErr_SetString(PyExc_TypeError, "Variant objects do not support " + "ordered comparison (<, >, <=, >=)"); + return NULL; + } +} + +static PyObject * +Variant_tp_str(Variant *self) +{ + if (PyString_Check(self->object)) { + Py_INCREF(self->object); + return self->object; + } + return PyObject_Str(self->object); +} + +static int +Variant_tp_nonzero(Variant *self) +{ + return PyObject_IsTrue(self->object); +} + +static PyObject * +Variant_nb_int(Variant *self) +{ + if (PyInt_Check(self->object) || PyLong_Check(self->object)) { + Py_INCREF(self->object); + return self->object; + } + return PyNumber_Int(self->object); +} + +static PyObject * +Variant_nb_oct(Variant *self) +{ + PyObject *ret; + PyObject *i = Variant_nb_int(self); + + if (!i) return NULL; + if (!PyNumber_Check(i) || !i->ob_type->tp_as_number->nb_oct) { + PyErr_SetString(PyExc_TypeError, "Value stored in Variant does not " + "support conversion to octal"); + Py_DECREF(i); + return NULL; + } + ret = (i->ob_type->tp_as_number->nb_oct)(i); + Py_DECREF(i); + return ret; +} + +static PyObject * +Variant_nb_hex(Variant *self) +{ + PyObject *ret; + PyObject *i = Variant_nb_int(self); + + if (!i) return NULL; + if (!PyNumber_Check(i) || !i->ob_type->tp_as_number->nb_hex) { + PyErr_SetString(PyExc_TypeError, "Value stored in Variant does not " + "support conversion to hexadecimal"); + Py_DECREF(i); + return NULL; + } + ret = (i->ob_type->tp_as_number->nb_hex)(i); + Py_DECREF(i); + return ret; +} + +static PyObject * +Variant_nb_long(Variant *self) +{ + if (PyLong_Check(self->object)) { + Py_INCREF(self->object); + return self->object; + } + return PyNumber_Long(self->object); +} + +static PyObject * +Variant_nb_float(Variant *self) +{ + if (PyFloat_Check(self->object)) { + Py_INCREF(self->object); + return self->object; + } + return PyNumber_Float(self->object); +} + +static PyNumberMethods Variant_tp_as_number = { + NULL, /* nb_add */ + NULL, /* nb_subtract */ + NULL, /* nb_multiply */ + NULL, /* nb_divide */ + NULL, /* nb_remainder */ + NULL, /* nb_divmod */ + NULL, /* nb_power */ + NULL, /* nb_negative */ + NULL, /* tp_positive */ + NULL, /* tp_absolute */ + (inquiry)Variant_tp_nonzero, /* tp_nonzero */ + NULL, /* nb_invert */ + NULL, /* nb_lshift */ + NULL, /* nb_rshift */ + NULL, /* nb_and */ + NULL, /* nb_xor */ + NULL, /* nb_or */ + NULL, /* nb_coerce */ + (unaryfunc)Variant_nb_int, + (unaryfunc)Variant_nb_long, + (unaryfunc)Variant_nb_float, + (unaryfunc)Variant_nb_oct, + (unaryfunc)Variant_nb_hex, + NULL, /* nb_inplace_add */ + NULL, /* nb_inplace_subtract */ + NULL, /* nb_inplace_multiply */ + NULL, /* nb_inplace_divide */ + NULL, /* nb_inplace_remainder */ + NULL, /* nb_inplace_power */ + NULL, /* nb_inplace_lshift */ + NULL, /* nb_inplace_rshift */ + NULL, /* nb_inplace_and */ + NULL, /* nb_inplace_xor */ + NULL, /* nb_inplace_or */ + NULL, /* nb_floor_divide */ + NULL, /* nb_true_divide */ + NULL, /* nb_inplace_floor_divide */ + NULL, /* nb_inplace_true_divide */ +}; + +static PyTypeObject VariantType = { + PyObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type)) + 0, + "dbus.Variant", + sizeof(Variant), + 0, + (destructor)Variant_tp_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)Variant_tp_repr, /* tp_repr */ + &Variant_tp_as_number, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc)Variant_tp_hash, /* tp_hash */ + (ternaryfunc)Variant_tp_call, /* tp_call */ + (reprfunc)Variant_tp_str, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + Variant_tp_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + (richcmpfunc)Variant_tp_richcompare, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)Variant_tp_iter, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + Variant_tp_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + Variant_tp_new, /* tp_new */ +}; + +static inline int +init_container_types(void) +{ + ArrayType.tp_base = &PyList_Type; + if (PyType_Ready(&ArrayType) < 0) return 0; + ArrayType.tp_print = NULL; + + DictType.tp_base = &PyDict_Type; + if (PyType_Ready(&DictType) < 0) return 0; + DictType.tp_print = NULL; + + if (PyType_Ready(&VariantType) < 0) return 0; + + return 1; +} + +static inline int +insert_container_types(PyObject *this_module) +{ + Py_INCREF(&ArrayType); + if (PyModule_AddObject(this_module, "Array", + (PyObject *)&ArrayType) < 0) return 0; + + Py_INCREF(&DictType); + if (PyModule_AddObject(this_module, "Dictionary", + (PyObject *)&DictType) < 0) return 0; + + Py_INCREF(&VariantType); + if (PyModule_AddObject(this_module, "Variant", + (PyObject *)&VariantType) < 0) return 0; + + return 1; +} + +/* vim:set ft=c cino< sw=4 sts=4 et: */ + diff --git a/_dbus_bindings/dbus_bindings.py b/_dbus_bindings/dbus_bindings.py new file mode 100644 index 0000000..cd4139a --- /dev/null +++ b/_dbus_bindings/dbus_bindings.py @@ -0,0 +1,27 @@ +# Backwards-compatibility with the old dbus_bindings. + +# Exceptions +from _dbus_bindings import DBusException +class ConnectionError(Exception): pass + +# Types +from _dbus_bindings import Int16, UInt16, Int32, UInt32, Int64, UInt64,\ + Variant, ObjectPath, Signature, Byte, ByteArray +# Don't bother importing SignatureIter, it can't be instantiated +# These used to be specialized subclasses, but these are unambiguous +String = unicode +Boolean = bool +Array = list # FIXME: c'tor params +Double = float +Struct = tuple +Dictionary = dict # FIXME: c'tor params + +# Messages +from _dbus_bindings import Message, SignalMessage as Signal,\ + MethodCallMessage as MethodCall,\ + MethodReturnMessage as MethodReturn,\ + ErrorMessage as Error +# MessageIter has gone away, thankfully + +# Connection +from _dbus_bindings import Connection diff --git a/_dbus_bindings/debug-impl.h b/_dbus_bindings/debug-impl.h new file mode 100644 index 0000000..ae87794 --- /dev/null +++ b/_dbus_bindings/debug-impl.h @@ -0,0 +1,49 @@ +/* Debug code for _dbus_bindings. + * + * Copyright (C) 2006 Collabora Ltd. + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#if 0 +# include <sys/types.h> +# include <unistd.h> + +# define USING_DBG +# define DBG(format, ...) fprintf(stderr, "DEBUG: " format "\n",\ + __VA_ARGS__) +static void _dbg_exc(void) +{ + PyObject *c, *v, *t; + /* This is a little mad. We want to get the traceback without + clearing the error indicator. */ + PyErr_Fetch(&c, &v, &t); /* 3 new refs */ + Py_XINCREF(c); Py_XINCREF(v); Py_XINCREF(t); /* now we own 6 refs */ + PyErr_Restore(c, v, t); /* steals 3 refs */ + PyErr_Print(); + PyErr_Restore(c, v, t); /* steals another 3 refs */ +} +# define DBG_EXC(format, ...) do {DBG(format, __VA_ARGS__); \ + _dbg_exc();} while (0) +#else +# undef USING_DBG +# define DBG(format, ...) do {} while (0) +# define DBG_EXC(format, ...) do {} while (0) +#endif diff --git a/_dbus_bindings/exceptions-impl.h b/_dbus_bindings/exceptions-impl.h new file mode 100644 index 0000000..6ba0c19 --- /dev/null +++ b/_dbus_bindings/exceptions-impl.h @@ -0,0 +1,71 @@ +/* D-Bus exception base classes. + * + * Copyright (C) 2006 Collabora Ltd. + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +static PyObject *DBusException; + +PyDoc_STRVAR(DBusException__doc__, "Represents any D-Bus-related error."); + +static inline PyObject * +DBusException_ConsumeError (DBusError *error) +{ + PyErr_Format (DBusException, "%s: %s", error->name, error->message); + dbus_error_free(error); + return NULL; +} + +static inline PyObject * +DBusException_UnusableMessage (void) +{ + PyErr_SetString (DBusException, + "Message object is uninitialized, or has become unusable " + "due to error while appending arguments"); + return NULL; +} + +static inline int +init_exception_types (void) +{ + PyObject *docstring; + + /* We call it dbus.DBusException because that's where you should import it + from. */ + DBusException = PyErr_NewException("dbus.DBusException", NULL, NULL); + if (!DBusException) return 0; + docstring = PyString_FromString(DBusException__doc__); + if (!docstring) return 0; + if (PyObject_SetAttrString (DBusException, "__doc__", docstring)) return 0; + Py_DECREF (docstring); + return 1; +} + +static inline int +insert_exception_types (PyObject *this_module) +{ + if (PyModule_AddObject(this_module, "DBusException", DBusException) < 0) { + return 0; + } + return 1; +} + +/* vim:set ft=c cino< sw=4 sts=4 et: */ diff --git a/_dbus_bindings/generic-impl.h b/_dbus_bindings/generic-impl.h new file mode 100644 index 0000000..71fbafe --- /dev/null +++ b/_dbus_bindings/generic-impl.h @@ -0,0 +1,108 @@ +/* General Python glue code, used in _dbus_bindings but not actually anything + * to do with D-Bus. + * + * Copyright (C) 2006 Collabora Ltd. + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define DEFERRED_ADDRESS(ADDR) 0 + +static PyObject *empty_tuple = NULL; + +/* A generic repr() implementation for int subclasses, returning something + * like 'dbus.Int16(123)'. + * Equivalent to the following Python: + * def __repr__(self): + * return '%s(%r)' % (self.__class__, int.__repr__(self)) + */ +static PyObject * +int_subclass_tp_repr (PyObject *self) +{ + PyObject *parent_repr = (PyInt_Type.tp_repr)(self); + PyObject *my_repr; + + if (!parent_repr) return NULL; + my_repr = PyString_FromFormat("%s(%s)", self->ob_type->tp_name, + PyString_AS_STRING(parent_repr)); + /* whether my_repr is NULL or not: */ + Py_DECREF(parent_repr); + return my_repr; +} + +/* A generic repr() implementation for long subclasses, returning something + * like 'dbus.Int64(123L)'. + * Equivalent to the following Python: + * def __repr__(self): + * return '%s(%r)' % (self.__class__, long.__repr__(self)) + */ +static PyObject * +long_subclass_tp_repr(PyObject *self) +{ + PyObject *parent_repr = (PyLong_Type.tp_repr)(self); + PyObject *my_repr; + + if (!parent_repr) return NULL; + my_repr = PyString_FromFormat("%s(%s)", self->ob_type->tp_name, + PyString_AS_STRING(parent_repr)); + /* whether my_repr is NULL or not: */ + Py_DECREF(parent_repr); + return my_repr; +} + +/* A generic repr() implementation for str subclasses, returning something + * like "dbus.ObjectPath('/foo/bar')". + * Equivalent to the following Python: + * def __repr__(self): + * return '%s(%r)' % (self.__class__, str.__repr__(self)) + */ +static PyObject * +str_subclass_tp_repr(PyObject *self) +{ + PyObject *parent_repr = (PyString_Type.tp_repr)(self); + PyObject *my_repr; + + if (!parent_repr) return NULL; + my_repr = PyString_FromFormat("%s(%s)", self->ob_type->tp_name, + PyString_AS_STRING(parent_repr)); + /* whether my_repr is NULL or not: */ + Py_DECREF(parent_repr); + return my_repr; +} + +/* Take the global interpreter lock and decrement the reference count. + * Suitable for calling from a C callback. */ +static void +Glue_TakeGILAndXDecref(PyObject *obj) +{ + PyGILState_STATE gil = PyGILState_Ensure(); + Py_XDECREF(obj); + PyGILState_Release(gil); +} + +static inline int +init_generic(void) +{ + empty_tuple = PyTuple_New(0); + if (!empty_tuple) return 0; + return 1; +} + +/* vim:set ft=c cino< sw=4 sts=4 et: */ diff --git a/_dbus_bindings/message-append-impl.h b/_dbus_bindings/message-append-impl.h new file mode 100644 index 0000000..99b8dea --- /dev/null +++ b/_dbus_bindings/message-append-impl.h @@ -0,0 +1,863 @@ +/* D-Bus Message serialization. This contains all the logic to map from + * Python objects to D-Bus types. + * + * Copyright (C) 2006 Collabora Ltd. + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +PyDoc_STRVAR(Message_append__doc__, +"set_args(*args[, **kwargs])\n\n" +"Set the message's arguments from the positional parameter, according to\n" +"the signature given by the ``signature`` keyword parameter.\n" +"\n" +"The following type conversions are supported:\n\n" +"------------------------------- ---------------------------\n" +"D-Bus (in signature) Python\n" +"=============================== ===========================\n" +"boolean (b) any object (via bool())\n" +"byte (y) string of length 1\n" +" any integer\n" +"any integer type any integer\n" +"double (d) any float\n" +"variant Variant\n" +" any object (guess type as below)\n" +"string, signature, object path str (must be UTF-8) or unicode\n" +"dict (a{...}) any mapping\n" +"array (a...) any iterable over appropriate objects\n" +"struct ((...)) any iterable over appropriate objects\n" +"------------------------------- ---------------------------\n" +"\n" +"Here 'any integer' means anything on which int() or long()\n" +"(as appropriate) will work, except for basestring subclasses.\n" +"'Any float' means anything on which float() will work, except\n" +"for basestring subclasses.\n" +"\n" +"If there is no signature, guess from the arguments using\n" +"the static method `Message.guess_signature`.\n" +); + +PyDoc_STRVAR(Message_guess_signature__doc__, +"guess_signature(*args) -> Signature [static method]\n\n" +"Guess a D-Bus signature which should be used to encode the given\n" +"Python objects.\n" +"\n" +"The signature is constructed as follows:\n\n" +"------------------------------- ---------------------------\n" +"Python D-Bus\n" +"=============================== ===========================\n" +"bool boolean (y)\n" +"dbus.Int16, etc. the corresponding type\n" +"any other int subclass int32 (i) (FIXME: make this error?)\n" +"long or a subclass int64 (x) (FIXME: make this error?)\n" +"float or a subclass double (d)\n" +"dbus.ObjectPath, dbus.Signature the corresponding type (o, g)\n" +"dbus.ByteArray or subclass array of byte (ay)\n" +"dbus.Byte or subclass byte (y)\n" +"str or any other subclass string (s)\n" +"unicode or a subclass string (s)\n" +"dbus.Variant or subclass variant (v), guess contents' type\n" +"tuple or a subclass struct ((...)), guess contents' types\n" +"list or a subclass array (a...), guess contents' type\n" +" according to type of first item\n" +"dict or a subclass dict (a{...}), guess key, value type\n" +" according to types for an arbitrary item\n" +"anything else raise TypeError\n" +"------------------------------- ---------------------------\n" +); + +/* Return a new reference. */ +static PyObject * +_signature_string_from_pyobject(PyObject *obj) +{ + /* Ordering is important: some of these are subclasses of each other. */ + if (obj == Py_True || obj == Py_False) + return PyString_FromString(DBUS_TYPE_BOOLEAN_AS_STRING); + else if (PyInt_Check(obj)) { + if (Int16_Check(obj)) + return PyString_FromString(DBUS_TYPE_INT16_AS_STRING); + else if (Int32_Check(obj)) + return PyString_FromString(DBUS_TYPE_INT32_AS_STRING); + else if (UInt16_Check(obj)) + return PyString_FromString(DBUS_TYPE_UINT16_AS_STRING); + else + return PyString_FromString(DBUS_TYPE_INT32_AS_STRING); + } + else if (PyLong_Check(obj)) { + if (Int64_Check(obj)) + return PyString_FromString(DBUS_TYPE_INT64_AS_STRING); + else if (UInt32_Check (obj)) + return PyString_FromString(DBUS_TYPE_UINT32_AS_STRING); + else if (UInt64_Check (obj)) + return PyString_FromString(DBUS_TYPE_UINT64_AS_STRING); + else + return PyString_FromString(DBUS_TYPE_INT64_AS_STRING); + } + else if (PyUnicode_Check(obj)) + return PyString_FromString(DBUS_TYPE_STRING_AS_STRING); + else if (PyFloat_Check(obj)) + return PyString_FromString(DBUS_TYPE_DOUBLE_AS_STRING); + else if (Variant_Check(obj)) { + return PyString_FromString(DBUS_TYPE_VARIANT_AS_STRING); + } + else if (PyString_Check(obj)) { + if (Byte_Check(obj)) + return PyString_FromString(DBUS_TYPE_BYTE_AS_STRING); + else if (ObjectPath_Check(obj)) + return PyString_FromString(DBUS_TYPE_OBJECT_PATH_AS_STRING); + else if (Signature_Check(obj)) + return PyString_FromString(DBUS_TYPE_SIGNATURE_AS_STRING); + else if (ByteArray_Check(obj)) + return PyString_FromString(DBUS_TYPE_ARRAY_AS_STRING + DBUS_TYPE_BYTE_AS_STRING); + else + return PyString_FromString(DBUS_TYPE_STRING_AS_STRING); + } + else if (PyTuple_Check (obj)) { + int len = PyTuple_GET_SIZE(obj); + PyObject *list = PyList_New(len + 2); /* new ref */ + PyObject *item; /* temporary new ref */ + PyObject *empty_str; /* temporary new ref */ + PyObject *ret; + int i; + + if (!list) return NULL; + if (len == 0) { + PyErr_SetString(PyExc_ValueError, "D-Bus structs cannot be empty"); + Py_DECREF (list); + return NULL; + } + /* Set the first and last elements of list to be the parentheses */ + item = PyString_FromString(DBUS_STRUCT_BEGIN_CHAR_AS_STRING); + if (PyList_SetItem(list, 0, item) < 0) { + Py_DECREF(list); + return NULL; + } + item = PyString_FromString(DBUS_STRUCT_END_CHAR_AS_STRING); + if (PyList_SetItem(list, len + 1, item) < 0) { + Py_DECREF(list); + return NULL; + } + if (!item || !PyList_GET_ITEM(list, 0)) { + Py_DECREF(list); + return NULL; + } + item = NULL; + + for (i = 0; i < len; i++) { + item = PyTuple_GetItem(obj, i); + if (!item) { + Py_DECREF(list); + return NULL; + } + item = _signature_string_from_pyobject(item); + if (!item) { + Py_DECREF(list); + return NULL; + } + if (PyList_SetItem(list, i + 1, item) < 0) { + Py_DECREF(list); + return NULL; + } + item = NULL; + } + empty_str = PyString_FromString(""); + if (!empty_str) { + /* really shouldn't happen */ + Py_DECREF(list); + return NULL; + } + ret = PyObject_CallMethod(empty_str, "join", "(O)", list); /* new ref */ + /* whether ret is NULL or not, */ + Py_DECREF(empty_str); + Py_DECREF(list); + return ret; + } + else if (PyList_Check(obj)) { + PyObject *tmp; + PyObject *ret = PyString_FromString(DBUS_TYPE_ARRAY_AS_STRING); + if (!ret) return NULL; + if (PyList_GET_SIZE(obj) == 0) { + /* No items, so fail. Or should we guess "av"? */ + PyErr_SetString (PyExc_ValueError, "Unable to guess signature " + "from an empty list"); + return NULL; + } + tmp = PyList_GetItem(obj, 0); + tmp = _signature_string_from_pyobject(tmp); + if (!tmp) return NULL; + PyString_ConcatAndDel (&ret, tmp); + return ret; + } + else if (PyDict_Check(obj)) { + PyObject *key, *value, *keysig, *valuesig; + int pos = 0; + PyObject *ret = NULL; + + if (!PyDict_Next(obj, &pos, &key, &value)) { + /* No items, so fail. Or should we guess "a{vv}"? */ + PyErr_SetString(PyExc_ValueError, "Unable to guess signature " + "from an empty dict"); + return NULL; + } + keysig = _signature_string_from_pyobject(key); + valuesig = _signature_string_from_pyobject(value); + if (keysig && valuesig) { + ret = PyString_FromFormat ((DBUS_TYPE_ARRAY_AS_STRING + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + "%s%s" + DBUS_DICT_ENTRY_END_CHAR_AS_STRING), + PyString_AS_STRING(keysig), + PyString_AS_STRING(valuesig)); + } + Py_XDECREF(keysig); + Py_XDECREF(valuesig); + return ret; + } + else { + PyErr_Format(PyExc_TypeError, "Don't know how which D-Bus type " + "to use to encode type \"%s\"", + obj->ob_type->tp_name); + return NULL; + } +} + +static PyObject * +Message_guess_signature(PyObject *unused, PyObject *args) +{ + PyObject *tmp, *ret = NULL; + + if (!args) { + if (!PyErr_Occurred()) { + PyErr_BadInternalCall(); + } + return NULL; + } + +#ifdef USING_DBG + fprintf(stderr, "DBG/%ld: called Message_guess_signature(*", (long)getpid()); + PyObject_Print(args, stderr, 0); + fprintf(stderr, ")\n"); +#endif + + if (!PyTuple_Check(args)) { + DBG("%s", "Message_guess_signature: args not a tuple"); + PyErr_BadInternalCall(); + return NULL; + } + + /* if there were no args, easy */ + if (PyTuple_GET_SIZE(args) == 0) { + DBG("%s", "Message_guess_signature: no args, so return Signature('')"); + return PyObject_CallFunction((PyObject *)&SignatureType, "(s)", ""); + } + + /* if there were args, the signature we want is, by construction, + * exactly the signature we get for the tuple args, except that we don't + * want the parentheses. */ + tmp = _signature_string_from_pyobject(args); + if (!tmp) { + DBG("%s", "Message_guess_signature: failed"); + return NULL; + } + if (!PyString_Check(tmp) || PyString_GET_SIZE(tmp) < 2) { + PyErr_SetString(PyExc_RuntimeError, "Internal error: " + "_signature_string_from_pyobject returned " + "a bad result"); + Py_DECREF(tmp); + return NULL; + } + ret = PyObject_CallFunction((PyObject *)&SignatureType, "(s#)", + PyString_AS_STRING (tmp) + 1, + PyString_GET_SIZE (tmp) - 2); + DBG("Message_guess_signature: returning Signature at %p", ret); + Py_DECREF (tmp); + return ret; +} + +static int _message_iter_append_pyobject(DBusMessageIter *appender, + DBusSignatureIter *sig_iter, + PyObject *obj); + +static int +_message_iter_append_string(DBusMessageIter *appender, + int sig_type, PyObject *obj) +{ + char *s; + + if (PyString_Check (obj)) { + /* Raise TypeError if the string has embedded NULs */ + if (PyString_AsStringAndSize (obj, &s, NULL) < 0) return -1; + /* Surely there's a faster stdlib way to validate UTF-8... */ + PyObject *unicode = PyUnicode_DecodeUTF8 (s, PyString_GET_SIZE (obj), + NULL); + if (!unicode) { + PyErr_SetString (PyExc_UnicodeError, "String parameters " + "to be sent over D-Bus must be valid UTF-8"); + return -1; + } + Py_DECREF (unicode); + unicode = NULL; + + DBG("Performing actual append: string %s", s); + if (!dbus_message_iter_append_basic (appender, sig_type, + &s)) { + PyErr_NoMemory(); + return -1; + } + } + else if (PyUnicode_Check (obj)) { + PyObject *utf8 = PyUnicode_AsUTF8String (obj); + if (!utf8) return -1; + /* Raise TypeError if the string has embedded NULs */ + if (PyString_AsStringAndSize (utf8, &s, NULL) < 0) return -1; + DBG("Performing actual append: string (from unicode) %s", s); + if (!dbus_message_iter_append_basic (appender, sig_type, &s)) { + PyErr_NoMemory(); + return -1; + } + Py_DECREF (utf8); + } + else { + PyErr_SetString (PyExc_TypeError, + "Expected a string or unicode object"); + return -1; + } + return 0; +} + +static int +_message_iter_append_byte(DBusMessageIter *appender, PyObject *obj) +{ + unsigned char y; + + if (PyString_Check(obj)) { + if (PyString_GET_SIZE(obj) != 1) { + PyErr_Format(PyExc_ValueError, "Expected a string of " + "length 1 byte, but found %d bytes", + PyString_GET_SIZE (obj)); + return -1; + } + y = *(unsigned char *)PyString_AS_STRING(obj); + } + else { + long i = PyInt_AsLong (obj); + + if (i == -1 && PyErr_Occurred()) return -1; + if (i < 0 || i > 0xff) { + PyErr_Format(PyExc_ValueError, "%d outside range for a " + "byte value", (int)i); + return -1; + } + y = i; + } + DBG("Performing actual append: byte (from unicode) \\x%02x", (unsigned)y); + if (!dbus_message_iter_append_basic(appender, DBUS_TYPE_BYTE, &y)) { + PyErr_NoMemory(); + return -1; + } + return 0; +} + +static int +_message_iter_append_dictentry(DBusMessageIter *appender, + DBusSignatureIter *sig_iter, + PyObject *dict, PyObject *key) +{ + DBusSignatureIter sub_sig_iter; + DBusMessageIter sub; + int ret = -1; + PyObject *value = PyObject_GetItem(dict, key); + + if (!value) return -1; + +#ifdef USING_DBG + fprintf(stderr, "Append dictentry: "); + PyObject_Print(key, stderr, 0); + fprintf(stderr, " => "); + PyObject_Print(value, stderr, 0); + fprintf(stderr, "\n"); +#endif + + DBG("Recursing signature iterator %p -> %p", sig_iter, &sub_sig_iter); + dbus_signature_iter_recurse(sig_iter, &sub_sig_iter); +#ifdef USING_DBG + { + char *s; + s = dbus_signature_iter_get_signature(sig_iter); + DBG("Signature of parent iterator %p is %s", sig_iter, s); + dbus_free(s); + s = dbus_signature_iter_get_signature(&sub_sig_iter); + DBG("Signature of sub-iterator %p is %s", &sub_sig_iter, s); + dbus_free(s); + } +#endif + + DBG("%s", "Opening DICT_ENTRY container"); + if (!dbus_message_iter_open_container(appender, DBUS_TYPE_DICT_ENTRY, + NULL, &sub)) { + PyErr_NoMemory(); + goto out; + } + ret = _message_iter_append_pyobject(&sub, &sub_sig_iter, key); + if (ret == 0) { + ret = _message_iter_append_pyobject(&sub, &sub_sig_iter, value); + } + DBG("%s", "Closing DICT_ENTRY container"); + if (!dbus_message_iter_close_container(appender, &sub)) { + PyErr_NoMemory(); + ret = -1; + } +out: + Py_DECREF(value); + return ret; +} + +static int +_message_iter_append_multi(DBusMessageIter *appender, + const DBusSignatureIter *sig_iter, + int mode, PyObject *obj) +{ + DBusMessageIter sub_appender; + DBusSignatureIter sub_sig_iter; + PyObject *contents; + int ret; + PyObject *iterator = PyObject_GetIter(obj); + char *sig = NULL; + int container = mode; + +#ifdef USING_DBG + fprintf(stderr, "Appending multiple: "); + PyObject_Print(obj, stderr, 0); + fprintf(stderr, "\n"); +#endif + + if (!iterator) return -1; + if (mode == DBUS_TYPE_DICT_ENTRY) container = DBUS_TYPE_ARRAY; + + DBG("Recursing signature iterator %p -> %p", sig_iter, &sub_sig_iter); + dbus_signature_iter_recurse(sig_iter, &sub_sig_iter); +#ifdef USING_DBG + { + char *s; + s = dbus_signature_iter_get_signature(sig_iter); + DBG("Signature of parent iterator %p is %s", sig_iter, s); + dbus_free(s); + s = dbus_signature_iter_get_signature(&sub_sig_iter); + DBG("Signature of sub-iterator %p is %s", &sub_sig_iter, s); + dbus_free(s); + } +#endif + + if (mode == DBUS_TYPE_ARRAY || mode == DBUS_TYPE_DICT_ENTRY) { + sig = dbus_signature_iter_get_signature(&sub_sig_iter); + if (!sig) { + PyErr_NoMemory(); + ret = -1; + goto out; + } + } + /* else leave sig set to NULL. */ + + DBG("Opening %c container", container); + if (!dbus_message_iter_open_container(appender, container, + sig, &sub_appender)) { + PyErr_NoMemory(); + ret = -1; + goto out; + } + ret = 0; + while ((contents = PyIter_Next(iterator))) { + + if (mode == DBUS_TYPE_ARRAY || mode == DBUS_TYPE_DICT_ENTRY) { + DBG("Recursing signature iterator %p -> %p", sig_iter, &sub_sig_iter); + dbus_signature_iter_recurse(sig_iter, &sub_sig_iter); +#ifdef USING_DBG + { + char *s; + s = dbus_signature_iter_get_signature(sig_iter); + DBG("Signature of parent iterator %p is %s", sig_iter, s); + dbus_free(s); + s = dbus_signature_iter_get_signature(&sub_sig_iter); + DBG("Signature of sub-iterator %p is %s", &sub_sig_iter, s); + dbus_free(s); + } +#endif + } + + if (mode == DBUS_TYPE_DICT_ENTRY) { + ret = _message_iter_append_dictentry(&sub_appender, &sub_sig_iter, + obj, contents); + } + else { + ret = _message_iter_append_pyobject(&sub_appender, &sub_sig_iter, + contents); + } + Py_DECREF(contents); + if (ret < 0) { + break; + } + } + if (PyErr_Occurred()) ret = -1; + /* This must be run as cleanup, even on failure. */ + DBG("Closing %c container", container); + if (!dbus_message_iter_close_container(appender, &sub_appender)) { + PyErr_NoMemory(); + ret = -1; + } + +out: + Py_XDECREF(iterator); + dbus_free(sig); + return ret; +} + +static int +_message_iter_append_string_as_byte_array (DBusMessageIter *appender, + PyObject *obj) +{ + /* a bit of a faster path for byte arrays that are strings */ + int len = PyString_GET_SIZE(obj); + const char *s; + DBusMessageIter sub; + int ret; + + s = PyString_AS_STRING(obj); + DBG("Opening ARRAY container", 0); + if (!dbus_message_iter_open_container(appender, DBUS_TYPE_ARRAY, + DBUS_TYPE_BYTE_AS_STRING, &sub)) { + PyErr_NoMemory(); + return -1; + } + DBG("Appending fixed array of %d bytes", len); + if (dbus_message_iter_append_fixed_array(&sub, DBUS_TYPE_BYTE, &s, len)) { + ret = 0; + } + else { + PyErr_NoMemory(); + ret = -1; + } + DBG("Clossing ARRAY container", 0); + if (!dbus_message_iter_close_container(appender, &sub)) { + PyErr_NoMemory(); + return -1; + } + return ret; +} + +/* Encode some Python object into a D-Bus variant slot. */ +static int +_message_iter_append_variant(DBusMessageIter *appender, PyObject *obj) +{ + DBusMessageIter sub; + DBusSignatureIter sig_iter; + const char *sig_str; + PyObject *sig, *guess_args; + int ret; + + if (Variant_Check (obj)) { + /* make sig an owned reference */ + sig = ((Variant *)obj)->signature; + if (!sig) { + PyErr_SetString(PyExc_RuntimeError, "Internal error: " + "broken Variant"); + return -1; + } + Py_INCREF(sig); + /* obj is still a borrowed reference though */ + obj = ((Variant *)obj)->object; + } + else { + /* Either it's a Variant with no explicit signature, or it's a + value. */ + guess_args = Py_BuildValue("(O)", obj); + if (!guess_args) return -1; + sig = Message_guess_signature (NULL, guess_args); + Py_DECREF(guess_args); + if (!sig) return -1; + } + + sig_str = PyString_AsString(sig); + if (!sig_str) return -1; + + dbus_signature_iter_init (&sig_iter, sig_str); + + DBG("Opening VARIANT container", 0); + if (!dbus_message_iter_open_container (appender, DBUS_TYPE_VARIANT, + sig_str, &sub)) { + PyErr_NoMemory(); + ret = -1; + goto out; + } + ret = _message_iter_append_pyobject(&sub, &sig_iter, obj); + DBG("Closing VARIANT container", 0); + if (!dbus_message_iter_close_container (appender, &sub)) { + PyErr_NoMemory(); + ret = -1; + } +out: + Py_XDECREF (sig); + return ret; +} + +static int +_message_iter_append_pyobject(DBusMessageIter *appender, + DBusSignatureIter *sig_iter, + PyObject *obj) +{ + int sig_type = dbus_signature_iter_get_current_type (sig_iter); + union { + dbus_bool_t b; + double d; + dbus_uint16_t uint16; + dbus_int16_t int16; + dbus_uint32_t uint32; + dbus_int32_t int32; +#if defined(DBUS_HAVE_INT64) && defined(HAVE_LONG_LONG) + dbus_uint64_t uint64; + dbus_int64_t int64; +#endif + } u; + int ret = -1; + +#ifdef USING_DBG + fprintf(stderr, "Appending object: "); + PyObject_Print(obj, stderr, 0); + fprintf(stderr, ", dbus wants type %c\n", sig_type); +#endif + + switch (sig_type) { + /* The numeric types are relatively simple to deal with, so are + * inlined here. */ + + case DBUS_TYPE_BOOLEAN: + if (PyObject_IsTrue(obj)) { + u.b = 1; + } + else { + u.b = 0; + } + DBG("Performing actual append: bool(%ld)", (long)u.b); + if (!dbus_message_iter_append_basic (appender, sig_type, &u.b)) { + PyErr_NoMemory(); + ret = -1; + break; + } + ret = 0; + break; + + case DBUS_TYPE_DOUBLE: + u.d = PyFloat_AsDouble (obj); + if (PyErr_Occurred()) { + ret = -1; + break; + } + DBG("Performing actual append: double(%f)", u.d); + if (!dbus_message_iter_append_basic(appender, sig_type, &u.d)) { + PyErr_NoMemory(); + ret = -1; + break; + } + ret = 0; + break; + + /* The integer types are all basically the same - we delegate to + intNN_range_check() */ +#define PROCESS_INTEGER(size) \ + u.size = size##_range_check (obj);\ + if (u.size == (dbus_##size##_t)(-1) && PyErr_Occurred()) {\ + ret = -1; \ + break; \ + }\ + DBG("Performing actual append: " #size "(%lld)", (long long)u.size); \ + if (!dbus_message_iter_append_basic(appender, sig_type, &u.size)) {\ + PyErr_NoMemory();\ + ret = -1;\ + break;\ + } \ + ret = 0; + + case DBUS_TYPE_INT16: + PROCESS_INTEGER(int16) + break; + case DBUS_TYPE_UINT16: + PROCESS_INTEGER(uint16) + break; + case DBUS_TYPE_INT32: + PROCESS_INTEGER(int32) + break; + case DBUS_TYPE_UINT32: + PROCESS_INTEGER(uint32) + break; +#if defined(DBUS_HAVE_INT64) && defined(HAVE_LONG_LONG) + case DBUS_TYPE_INT64: + PROCESS_INTEGER(int64) + break; + case DBUS_TYPE_UINT64: + PROCESS_INTEGER(uint64) + break; +#else + case DBUS_TYPE_INT64: + case DBUS_TYPE_UINT64: + PyErr_SetString (PyExc_RuntimeError, "64-bit integers not " + "supported on this platform"); + ret = -1; + break; +#endif +#undef PROCESS_INTEGER + + /* Now the more complicated cases, which are delegated to helper + * functions (although in practice, the compiler will hopefully + * inline them anyway). */ + + case DBUS_TYPE_STRING: + case DBUS_TYPE_SIGNATURE: + case DBUS_TYPE_OBJECT_PATH: + ret = _message_iter_append_string(appender, sig_type, obj); + break; + + case DBUS_TYPE_BYTE: + ret = _message_iter_append_byte(appender, obj); + break; + + case DBUS_TYPE_ARRAY: + /* 3 cases - it might actually be a dict, or it might be a byte array + * being copied from a string (for which we have a faster path), + * or it might be a generic array. */ + + sig_type = dbus_signature_iter_get_element_type(sig_iter); + if (sig_type == DBUS_TYPE_DICT_ENTRY) + ret = _message_iter_append_multi(appender, sig_iter, + DBUS_TYPE_DICT_ENTRY, obj); + else if (sig_type == DBUS_TYPE_BYTE && PyString_Check(obj)) + ret = _message_iter_append_string_as_byte_array(appender, obj); + else + ret = _message_iter_append_multi(appender, sig_iter, + DBUS_TYPE_ARRAY, obj); + DBG("_message_iter_append_multi(): %d", ret); + break; + + case DBUS_TYPE_STRUCT: + ret = _message_iter_append_multi(appender, sig_iter, sig_type, obj); + break; + + case DBUS_TYPE_VARIANT: + ret = _message_iter_append_variant(appender, obj); + break; + + case DBUS_TYPE_INVALID: + PyErr_SetString(PyExc_TypeError, "Fewer items found in D-Bus " + "signature than in Python arguments"); + ret = -1; + break; + + default: + PyErr_Format(PyExc_TypeError, "Unknown type '\\x%x' in D-Bus " + "signature", sig_type); + ret = -1; + break; + } + if (ret < 0) return -1; + + DBG("Advancing signature iter at %p", sig_iter); +#ifdef USING_DBG + { dbus_bool_t b = +#endif + dbus_signature_iter_next(sig_iter); +#ifdef USING_DBG + DBG("- result: %ld", (long)b); + } +#endif + return 0; +} + + +static PyObject * +Message_append(Message *self, PyObject *args, PyObject *kwargs) +{ + const char *signature = NULL; + PyObject *signature_obj = NULL; + DBusSignatureIter sig_iter; + DBusMessageIter appender; + int i; + static char *argnames[] = {"signature", NULL}; + + if (!self->msg) return DBusException_UnusableMessage (); + +#ifdef USING_DBG + fprintf(stderr, "DBG/%ld: called Message_append(*", (long)getpid()); + PyObject_Print(args, stderr, 0); + if (kwargs) { + fprintf(stderr, ", **"); + PyObject_Print(kwargs, stderr, 0); + } + fprintf(stderr, ")\n"); +#endif + + /* only use kwargs for this step: deliberately ignore args for now */ + if (!PyArg_ParseTupleAndKeywords(empty_tuple, kwargs, "|z:append", + argnames, &signature)) return NULL; + + if (!signature) { + DBG("%s", "No signature for message, guessing..."); + signature_obj = Message_guess_signature(NULL, args); + if (!signature_obj) return NULL; + signature = PyString_AS_STRING (signature_obj); + } + /* from here onwards, you have to do a goto rather than returning NULL + to make sure signature_obj gets freed */ + + /* iterate over args and the signature, together */ + if (!dbus_signature_validate(signature, NULL)) { + PyErr_SetString(PyExc_ValueError, "Corrupt type signature"); + goto err; + } + dbus_signature_iter_init(&sig_iter, signature); + dbus_message_iter_init_append(self->msg, &appender); + for (i = 0; i < PyTuple_GET_SIZE(args); i++) { + if (_message_iter_append_pyobject(&appender, &sig_iter, + PyTuple_GET_ITEM (args, i)) < 0) { + goto hosed; + } + } + if (dbus_signature_iter_get_current_type(&sig_iter) + != DBUS_TYPE_INVALID) { + PyErr_SetString(PyExc_TypeError, "More items found in D-Bus " + "signature than in Python arguments"); + goto hosed; + } + + /* success! */ + Py_XDECREF(signature_obj); + Py_RETURN_NONE; + +hosed: + /* "If appending any of the arguments fails due to lack of memory, + * generally the message is hosed and you have to start over" -libdbus docs + * Enforce this by throwing away the message structure. + */ + dbus_message_unref(self->msg); + self->msg = NULL; +err: + Py_XDECREF(signature_obj); + return NULL; +} + +/* vim:set ft=c cino< sw=4 sts=4 et: */ diff --git a/_dbus_bindings/message-get-args-impl.h b/_dbus_bindings/message-get-args-impl.h new file mode 100644 index 0000000..ccd52ce --- /dev/null +++ b/_dbus_bindings/message-get-args-impl.h @@ -0,0 +1,403 @@ +/* D-Bus Message unserialization. This contains all the logic to map from + * D-Bus types to Python objects. + * + * Copyright (C) 2006 Collabora Ltd. + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +PyDoc_STRVAR(Message_get_args__doc__, +"get_args(**kwargs) -> tuple\n\n" +"Return the message's arguments. Keyword arguments control the translation\n" +"of D-Bus types to Python:\n" +"\n" +":Parameters:\n" +" `integer_bytes` : bool\n" +" If true, return D-Bus bytes as Python ints (loses type information).\n" +" If false (default and recommended), return D-Bus bytes as Byte\n" +" objects, a subclass of str with length always 1.\n" +" `byte_arrays` : bool\n" +" If true, convert arrays of byte (signature 'ay') into ByteArray,\n" +" a str subclass whose subscript operator returns Byte objects.\n" +" If false (default), convert them like any other array (into a\n" +" list of Bytes, or a list of ints if integer_bytes is true)." +" `untyped_integers` : bool\n" +" If true, fold all intNN, uintNN types into Python int or long (loses\n" +" type information).\n" +" If false (default), convert them into the appropriate subclasses.\n" +" `variant_unpack_level` : int\n" +" If >1, unpack e.g. a variant containing a variant containing a\n" +" string as if it was a string (loses type information).\n" +" If 1, unpack it as a Variant containing a string (i.e. remove\n" +" exactly one level of \"variantness\".\n" +" If 0 (default), unpack it as a Variant containing a Variant\n" +" containing a string, to avoid ambiguity completely.\n" +" `utf8_strings` : bool\n" +" If true, return D-Bus strings as Python 8-bit strings (of UTF-8).\n" +" If false (default), return D-Bus strings as Python unicode objects.\n" +"\n" +"Most of the type mappings should be fairly obvious:\n" +"\n" +"--------------- ------------\n" +"D-Bus Python\n" +"=============== ============\n" +"byte (y) Byte (int if integer_bytes set)\n" +"bool (b) bool\n" +"Signature (g) Signature (str subclass)\n" +"intNN, uintNN IntNN, UIntNN (int/long subclass)\n" +" (int/long if untyped_integers set)\n" +"double (d) float\n" +"string (s) unicode (str if utf8_strings set)\n" +"Object path (o) ObjectPath (str subclass)\n" +"dict (a{...}) dict\n" +"array (a...) list of appropriate types\n" +"byte array (ay) ByteArray (str subclass) if byte_arrays set; or\n" +" list of int, if integer_bytes set; or\n" +" list of Byte\n" +"variant (v) Variant containing appropriate type\n" +"struct ((...)) tuple of appropriate types\n" +"--------------- ------------\n" +); + +typedef struct { + int integer_bytes; + int byte_arrays; + int untyped_integers; + int variant_unpack_level; + int utf8_strings; +} Message_get_args_options; + +static PyObject *_message_iter_get_pyobject(DBusMessageIter *iter, + Message_get_args_options *opts, + int top_level); + +/* Append all the items iterated over to the given Python list object. + * Return 0 on success/-1 with exception on failure. */ +static int +_message_iter_append_all_to_list(DBusMessageIter *iter, PyObject *list, + Message_get_args_options *opts, + int top_level) +{ + int ret, type; + while ((type = dbus_message_iter_get_arg_type (iter)) + != DBUS_TYPE_INVALID) { + PyObject *item; + DBG("type == %d '%c'", type, type); + + item = _message_iter_get_pyobject(iter, opts, top_level); + if (!item) return -1; +#ifdef USING_DBG + fprintf(stderr, "DBG/%ld: appending to list: %p == ", (long)getpid(), item); + PyObject_Print(item, stderr, 0); + fprintf(stderr, " of type %p (Byte is %p)\n", item->ob_type, + &ByteType); +#endif + ret = PyList_Append(list, item); + Py_DECREF(item); + item = NULL; + if (ret < 0) return -1; +#ifdef USING_DBG + fprintf(stderr, "DBG/%ld: list now contains: ", (long)getpid()); + PyObject_Print(list, stderr, 0); + fprintf(stderr, "\n"); +#endif + dbus_message_iter_next(iter); + } + return 0; +} + +/* Returns a new reference. */ +static PyObject * +_message_iter_get_pyobject(DBusMessageIter *iter, + Message_get_args_options *opts, int top_level) +{ + union { + const char *s; + unsigned char y; + dbus_bool_t b; + double d; + dbus_uint16_t u16; + dbus_int16_t i16; + dbus_uint32_t u32; + dbus_int32_t i32; +#if defined(DBUS_HAVE_INT64) && defined(HAVE_LONG_LONG) + dbus_uint64_t u64; + dbus_int64_t i64; +#endif + } u; + + int type = dbus_message_iter_get_arg_type(iter); + + switch (type) { + case DBUS_TYPE_STRING: + DBG("%s", "found a string"); + dbus_message_iter_get_basic(iter, &u.s); + if (opts->utf8_strings) + return PyString_FromString(u.s); + else + return PyUnicode_DecodeUTF8(u.s, strlen(u.s), NULL); + + case DBUS_TYPE_SIGNATURE: + DBG("%s", "found a signature"); + dbus_message_iter_get_basic(iter, &u.s); + return PyObject_CallFunction((PyObject *)&SignatureType, "(s)", u.s); + + case DBUS_TYPE_OBJECT_PATH: + DBG("%s", "found an object path"); + dbus_message_iter_get_basic(iter, &u.s); + return PyObject_CallFunction((PyObject *)&ObjectPathType, "(s)", u.s); + + case DBUS_TYPE_DOUBLE: + DBG("%s", "found a double"); + dbus_message_iter_get_basic(iter, &u.d); + return PyFloat_FromDouble(u.d); + + case DBUS_TYPE_INT16: + DBG("%s", "found an int16"); + dbus_message_iter_get_basic(iter, &u.i16); + if (opts->untyped_integers) + return PyInt_FromLong(u.i16); + else + return PyObject_CallFunction((PyObject *)&Int16Type, "(i)", (int)u.i16); + + case DBUS_TYPE_UINT16: + DBG("%s", "found a uint16"); + dbus_message_iter_get_basic(iter, &u.u16); + if (opts->untyped_integers) + return PyInt_FromLong(u.u16); + else + return PyObject_CallFunction((PyObject *)&UInt16Type, "(i)", (int)u.u16); + + case DBUS_TYPE_INT32: + DBG("%s", "found an int32"); + dbus_message_iter_get_basic(iter, &u.i32); + if (opts->untyped_integers) + return PyInt_FromLong(u.i32); + else + return PyObject_CallFunction((PyObject *)&Int32Type, "(l)", (long)u.i32); + + case DBUS_TYPE_UINT32: + DBG("%s", "found a uint32"); + dbus_message_iter_get_basic(iter, &u.u32); + if (opts->untyped_integers) + return PyLong_FromUnsignedLong(u.u32); + else + return PyObject_CallFunction((PyObject *)&UInt32Type, "(k)", + (unsigned long)u.u32); + +#if defined(DBUS_HAVE_INT64) && defined(HAVE_LONG_LONG) + case DBUS_TYPE_INT64: + DBG("%s", "found an int64"); + dbus_message_iter_get_basic(iter, &u.i64); + if (opts->untyped_integers) + return PyLong_FromLongLong(u.i64); + else + return PyObject_CallFunction((PyObject *)&Int64Type, "(L)", + (long long)u.i64); + + case DBUS_TYPE_UINT64: + DBG("%s", "found a uint64"); + dbus_message_iter_get_basic(iter, &u.u64); + if (opts->untyped_integers) + return PyLong_FromUnsignedLongLong(u.u64); + else + return PyObject_CallFunction((PyObject *)&UInt64Type, "(K)", + (unsigned long long)u.u64); +#else + case DBUS_TYPE_INT64: + case DBUS_TYPE_UINT64: + PyErr_SetString (PyExc_RuntimeError, "64-bit integers not " + "supported on this platform"); + return NULL; +#endif + + case DBUS_TYPE_BYTE: + DBG("%s", "found a byte"); + dbus_message_iter_get_basic(iter, &u.y); + if (opts->integer_bytes) + return PyInt_FromLong(u.y); + else + return Byte_from_uchar(u.y); + + case DBUS_TYPE_BOOLEAN: + DBG("%s", "found a bool"); + dbus_message_iter_get_basic(iter, &u.b); + return PyBool_FromLong(u.b); + + case DBUS_TYPE_ARRAY: + DBG("%s", "found an array..."); + /* Dicts are arrays of DBUS_TYPE_DICT_ENTRY on the wire. + Also, we special-case arrays of DBUS_TYPE_BYTE sometimes. */ + type = dbus_message_iter_get_element_type(iter); + if (type == DBUS_TYPE_DICT_ENTRY) { + DBusMessageIter entries, kv; + PyObject *key, *value, *dict; + + DBG("%s", "no, actually it's a dict..."); + dict = PyObject_CallObject((PyObject *)&DictType, + empty_tuple); + if (!dict) return NULL; + + dbus_message_iter_recurse(iter, &entries); + while ((type = dbus_message_iter_get_arg_type(&entries)) + == DBUS_TYPE_DICT_ENTRY) { + DBG("%s", "dict entry..."); + key = NULL; + value = NULL; + dbus_message_iter_recurse(&entries, &kv); + key = _message_iter_get_pyobject(&kv, opts, 1); + if (key) { + dbus_message_iter_next(&kv); + value = _message_iter_get_pyobject(&kv, opts, 1); + if (value && PyDict_SetItem(dict, key, value) == 0) { + Py_XDECREF(key); + Py_XDECREF(value); + dbus_message_iter_next(&entries); + continue; + } + } + /* error */ + Py_XDECREF(key); + Py_XDECREF(value); + Py_XDECREF(dict); + return NULL; + } + return dict; + } + else if (opts->byte_arrays && type == DBUS_TYPE_BYTE) { + int n; + DBG("%s", "actually, a byte array..."); + dbus_message_iter_get_fixed_array(iter, + (const unsigned char **)&u.s, + &n); + return ByteArray_from_uchars((const unsigned char *)u.s, n); + } + else { + DBusMessageIter sub; + PyObject *list = PyObject_CallObject((PyObject *)&ArrayType, + empty_tuple); + DBG("%s", "a normal array..."); + if (!list) return NULL; + dbus_message_iter_recurse(iter, &sub); + if (_message_iter_append_all_to_list(&sub, list, opts, 1) < 0) { + Py_DECREF(list); + return NULL; + } + return list; + } + + case DBUS_TYPE_STRUCT: + { + PyObject *tuple; + DBusMessageIter sub; + PyObject *list = PyList_New(0); + DBG("%s", "found a struct..."); + if (!list) return NULL; + dbus_message_iter_recurse(iter, &sub); + if (_message_iter_append_all_to_list (&sub, list, opts, 1) < 0) { + Py_DECREF(list); + return NULL; + } + tuple = PyList_AsTuple(list); + /* whether tuple is NULL or not, we take the same action: */ + Py_DECREF(list); + return tuple; + } + + case DBUS_TYPE_VARIANT: + { + DBusMessageIter sub; + + DBG("%s", "found a variant..."); + dbus_message_iter_recurse(iter, &sub); + if (opts->variant_unpack_level > 1) { + /* throw away arbitrarily many layers of Variant wrapper */ + return _message_iter_get_pyobject(&sub, opts, 1); + } + else { + /* recurse into the object, and this time don't throw away + * Variant wrappers */ + PyObject *object = _message_iter_get_pyobject(&sub, opts, 0); + PyObject *ret; + if (!object) return NULL; + if (top_level && opts->variant_unpack_level == 1) return object; + ret = PyObject_CallFunction((PyObject *)&VariantType, + "(Os)", object, + dbus_message_iter_get_signature (&sub)); + /* whether ret is NULL or not, we take the same action: */ + Py_DECREF(object); + return ret; + } + } + + default: + PyErr_Format(PyExc_TypeError, "Unknown type '\\%x' in D-Bus " + "message", type); + return NULL; + } +} + +static PyObject * +Message_get_args(Message *self, PyObject *args, PyObject *kwargs) +{ + Message_get_args_options opts = { 0, 0, 0, 0, 0 }; + static char *argnames[] = { "integer_bytes", "byte_arrays", + "untyped_integers", + "variant_unpack_level", + "utf8_strings", NULL }; + PyObject *list, *tuple = NULL; + DBusMessageIter iter; + + if (PyTuple_Size(args) != 0) { + PyErr_SetString(PyExc_TypeError, "get_args takes no positional " + "arguments"); + return NULL; + } + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|iiiii:get_args", argnames, + &(opts.integer_bytes), &(opts.byte_arrays), + &(opts.untyped_integers), + &(opts.variant_unpack_level), + &(opts.utf8_strings))) return NULL; + if (!self->msg) return DBusException_UnusableMessage(); + + list = PyList_New(0); + if (!list) return NULL; + + /* Iterate over args, if any, appending to list */ + if (dbus_message_iter_init(self->msg, &iter)) { + if (_message_iter_append_all_to_list(&iter, list, &opts, 1) < 0) { + Py_DECREF(list); + return NULL; + } + } + +#ifdef USING_DBG + fprintf(stderr, "DBG/%ld: message has args list ", (long)getpid()); + PyObject_Print(list, stderr, 0); + fprintf(stderr, "\n"); +#endif + + tuple = PyList_AsTuple(list); + /* whether tuple is NULL or not, we take the same action: */ + Py_DECREF(list); + return tuple; +} + +/* vim:set ft=c cino< sw=4 sts=4 et: */ diff --git a/_dbus_bindings/message-impl.h b/_dbus_bindings/message-impl.h new file mode 100644 index 0000000..f480ada --- /dev/null +++ b/_dbus_bindings/message-impl.h @@ -0,0 +1,1064 @@ +/* Implementation of D-Bus Message and subclasses (but see message-get-args.h + * and message-append.h for unserialization and serialization code). + * + * Copyright (C) 2006 Collabora Ltd. + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifdef USING_DBG +static void _dbg_dump_message(DBusMessage *message) +{ + const char *s; + fprintf(stderr, "DBusMessage at %p\n", message); + + s = dbus_message_get_destination(message); + if (!s) s = "(null)"; + fprintf(stderr, "\tdestination %s\n", s); + + s = dbus_message_get_interface(message); + if (!s) s = "(null)"; + fprintf(stderr, "\tinterface %s\n", s); + + s = dbus_message_get_member(message); + if (!s) s = "(null)"; + fprintf(stderr, "\tmember %s\n", s); + + s = dbus_message_get_path(message); + if (!s) s = "(null)"; + fprintf(stderr, "\tpath %s\n", s); +} +# define DBG_DUMP_MESSAGE(x) _dbg_dump_message(x) +#else +# define DBG_DUMP_MESSAGE(x) do {} while(0) +#endif + +static PyTypeObject MessageType, SignalMessageType, ErrorMessageType; +static PyTypeObject MethodReturnMessageType, MethodCallMessageType; + +static inline int Message_Check (PyObject *o) +{ + return (o->ob_type == &MessageType) + || PyObject_IsInstance(o, (PyObject *)&MessageType); +} + +typedef struct { + PyObject_HEAD + DBusMessage *msg; +} Message; + +PyDoc_STRVAR(Message_tp_doc, +"A message to be sent or received over a D-Bus Connection.\n"); + +static void Message_tp_dealloc (Message *self) +{ + if (self->msg) { + dbus_message_unref (self->msg); + } + self->ob_type->tp_free ((PyObject *)self); +} + +static PyObject * +Message_tp_new (PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + Message *self; + + self = (Message *)type->tp_alloc (type, 0); + if (!self) return NULL; + self->msg = NULL; + return (PyObject *)self; +} + +PyDoc_STRVAR(MethodCallMessage_tp_doc, "A method-call message.\n\n" +"MethodCallMessage(destination: str or None, path: str,\n" +" interface: str or None, method: str)\n"); +static int +MethodCallMessage_tp_init (Message *self, PyObject *args, PyObject *kwargs) +{ + const char *destination, *path, *interface, *method; + static char *kwlist[] = {"destination", "path", "interface", "method", NULL}; + + if (!PyArg_ParseTupleAndKeywords (args, kwargs, "zszs:__init__", kwlist, + &destination, &path, &interface, + &method)) { + return -1; + } + if (destination && !_validate_bus_name(destination, 1, 1)) return -1; + if (!_validate_object_path(path)) return -1; + if (interface && !_validate_interface_name(interface)) return -1; + if (!_validate_member_name(method)) return -1; + if (self->msg) { + dbus_message_unref (self->msg); + self->msg = NULL; + } + self->msg = dbus_message_new_method_call (destination, path, interface, + method); + if (!self->msg) { + PyErr_NoMemory(); + return -1; + } + return 0; +} + +PyDoc_STRVAR(MethodReturnMessage_tp_doc, "A method-return message.\n\n" +"MethodReturnMessage(method_call: MethodCallMessage)\n"); +static int +MethodReturnMessage_tp_init (Message *self, PyObject *args, PyObject *kwargs) +{ + Message *other; + static char *kwlist[] = {"method_call", NULL}; + + if (!PyArg_ParseTupleAndKeywords (args, kwargs, "O!:__init__", kwlist, + &MessageType, &other)) { + return -1; + } + if (self->msg) { + dbus_message_unref (self->msg); + self->msg = NULL; + } + self->msg = dbus_message_new_method_return (other->msg); + if (!self->msg) { + PyErr_NoMemory(); + return -1; + } + return 0; +} + +PyDoc_STRVAR(SignalMessage_tp_doc, "A signal message.\n\n" +"SignalMessage(path: str, interface: str, method: str)\n"); +static int +SignalMessage_tp_init (Message *self, PyObject *args, PyObject *kwargs) +{ + const char *path, *interface, *name; + static char *kwlist[] = {"path", "interface", "name", NULL}; + + if (!PyArg_ParseTupleAndKeywords (args, kwargs, "sss:__init__", kwlist, + &path, &interface, &name)) { + return -1; + } + if (!_validate_object_path(path)) return -1; + if (!_validate_interface_name(interface)) return -1; + if (!_validate_member_name(name)) return -1; + if (self->msg) { + dbus_message_unref (self->msg); + self->msg = NULL; + } + self->msg = dbus_message_new_signal (path, interface, name); + if (!self->msg) { + PyErr_NoMemory(); + return -1; + } + return 0; +} + +PyDoc_STRVAR(ErrorMessage_tp_doc, "An error message.\n\n" +"ErrorMessage(reply_to: Message, error_name: str,\n" +" error_message: str or None)\n"); +static int +ErrorMessage_tp_init (Message *self, PyObject *args, PyObject *kwargs) +{ + Message *reply_to; + const char *error_name, *error_message; + static char *kwlist[] = {"reply_to", "error_name", "error_message", NULL}; + + if (!PyArg_ParseTupleAndKeywords (args, kwargs, "O!sz:__init__", kwlist, + &MessageType, &reply_to, &error_name, + &error_message)) { + return -1; + } + if (!_validate_error_name(error_name)) return -1; + if (self->msg) { + dbus_message_unref (self->msg); + self->msg = NULL; + } + self->msg = dbus_message_new_error (reply_to->msg, error_name, error_message); + if (!self->msg) { + PyErr_NoMemory(); + return -1; + } + return 0; +} + +static DBusMessage * +Message_BorrowDBusMessage (PyObject *msg) +{ + if (!Message_Check (msg)) { + PyErr_SetString (PyExc_TypeError, + "A _dbus_bindings.Message instance is required"); + return NULL; + } + if (!((Message *)msg)->msg) { + DBusException_UnusableMessage(); + return NULL; + } + return ((Message *)msg)->msg; +} + +static PyObject * +Message_ConsumeDBusMessage (DBusMessage *msg) +{ + PyTypeObject *type; + Message *self; + + switch (dbus_message_get_type (msg)) { + case DBUS_MESSAGE_TYPE_METHOD_CALL: + type = &MethodCallMessageType; + break; + case DBUS_MESSAGE_TYPE_METHOD_RETURN: + type = &MethodReturnMessageType; + break; + case DBUS_MESSAGE_TYPE_ERROR: + type = &ErrorMessageType; + break; + case DBUS_MESSAGE_TYPE_SIGNAL: + type = &SignalMessageType; + break; + default: + type = &MessageType; + } + + self = (Message *)(type->tp_new) (type, empty_tuple, NULL); + if (!self) { + dbus_message_unref(msg); + return NULL; + } + self->msg = msg; + return (PyObject *)self; +} + +PyDoc_STRVAR(Message_copy__doc__, +"message.copy() -> Message (or subclass)\n" +"Deep-copy the message, resetting the serial number to zero.\n"); +static PyObject * +Message_copy (Message *self, PyObject *args) +{ + DBusMessage *msg; + if (!self->msg) return DBusException_UnusableMessage(); + msg = dbus_message_copy(self->msg); + if (!msg) return PyErr_NoMemory(); + return Message_ConsumeDBusMessage(msg); +} + +PyDoc_STRVAR(Message_get_auto_start__doc__, +"message.get_auto_start() -> bool\n" +"Return true if this message will cause an owner for the destination name\n" +"to be auto-started.\n"); +static PyObject * +Message_get_auto_start (Message *self, PyObject *unused) +{ + if (!self->msg) return DBusException_UnusableMessage(); + return PyBool_FromLong (dbus_message_get_auto_start (self->msg)); +} + +PyDoc_STRVAR(Message_set_auto_start__doc__, +"message.set_auto_start(bool) -> None\n" +"Set whether this message will cause an owner for the destination name\n" +"to be auto-started.\n"); +static PyObject * +Message_set_auto_start (Message *self, PyObject *args) +{ + int value; + if (!PyArg_ParseTuple (args, "i", &value)) return NULL; + if (!self->msg) return DBusException_UnusableMessage(); + dbus_message_set_auto_start (self->msg, value ? TRUE : FALSE); + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(Message_get_no_reply__doc__, +"message.get_no_reply() -> bool\n" +"Return true if this message need not be replied to.\n"); +static PyObject * +Message_get_no_reply (Message *self, PyObject *unused) +{ + if (!self->msg) return DBusException_UnusableMessage(); + return PyBool_FromLong (dbus_message_get_no_reply (self->msg)); +} + +PyDoc_STRVAR(Message_set_no_reply__doc__, +"message.set_no_reply(bool) -> None\n" +"Set whether no reply to this message is required.\n"); +static PyObject * +Message_set_no_reply (Message *self, PyObject *args) +{ + int value; + if (!PyArg_ParseTuple (args, "i", &value)) return NULL; + if (!self->msg) return DBusException_UnusableMessage(); + dbus_message_set_no_reply (self->msg, value ? TRUE : FALSE); + Py_RETURN_NONE; +} + +PyDoc_STRVAR(Message_get_reply_serial__doc__, +"message.get_reply_serial() -> long\n" +"Returns the serial that the message is a reply to or 0 if none.\n"); +static PyObject * +Message_get_reply_serial (Message *self, PyObject *unused) +{ + if (!self->msg) return DBusException_UnusableMessage(); + return PyLong_FromUnsignedLong (dbus_message_get_reply_serial (self->msg)); +} + +PyDoc_STRVAR(Message_set_reply_serial__doc__, +"message.set_reply_serial(bool) -> None\n" +"Set the serial that this message is a reply to.\n"); +static PyObject * +Message_set_reply_serial (Message *self, PyObject *args) +{ + dbus_uint32_t value; + + if (!PyArg_ParseTuple (args, "k", &value)) return NULL; + if (!self->msg) return DBusException_UnusableMessage(); + if (!dbus_message_set_reply_serial (self->msg, value)) { + return PyErr_NoMemory(); + } + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(Message_get_type__doc__, +"message.get_type() -> int\n\n" +"Returns the type of the message.\n"); +static PyObject * +Message_get_type (Message *self, PyObject *unused) +{ + if (!self->msg) return DBusException_UnusableMessage(); + return PyInt_FromLong (dbus_message_get_type (self->msg)); +} + +PyDoc_STRVAR(Message_get_serial__doc__, +"message.get_serial() -> long\n" +"Returns the serial of a message or 0 if none has been specified.\n" +"\n" +"The message's serial number is provided by the application sending the\n" +"message and is used to identify replies to this message. All messages\n" +"received on a connection will have a serial, but messages you haven't\n" +"sent yet may return 0.\n"); +static PyObject * +Message_get_serial (Message *self, PyObject *unused) +{ + if (!self->msg) return DBusException_UnusableMessage(); + return PyLong_FromUnsignedLong (dbus_message_get_serial (self->msg)); +} + +PyDoc_STRVAR(Message_is_method_call__doc__, +"is_method_call(interface: str, member: str) -> bool"); +static PyObject * +Message_is_method_call (Message *self, PyObject *args) +{ + const char *interface, *method; + + if (!PyArg_ParseTuple(args, "ss:is_method_call", &interface, &method)) { + return NULL; + } + if (!self->msg) return DBusException_UnusableMessage(); + return PyBool_FromLong (dbus_message_is_method_call (self->msg, interface, + method)); +} + +PyDoc_STRVAR(Message_is_error__doc__, +"is_error(error: str) -> bool"); +static PyObject * +Message_is_error (Message *self, PyObject *args) +{ + const char *error_name; + + if (!PyArg_ParseTuple(args, "s:is_error", &error_name)) { + return NULL; + } + if (!self->msg) return DBusException_UnusableMessage(); + return PyBool_FromLong (dbus_message_is_error (self->msg, error_name)); +} + +PyDoc_STRVAR(Message_is_signal__doc__, +"is_signal(interface: str, member: str) -> bool"); +static PyObject * +Message_is_signal (Message *self, PyObject *args) +{ + const char *interface, *signal_name; + + if (!PyArg_ParseTuple(args, "ss:is_signal", &interface, &signal_name)) { + return NULL; + } + if (!self->msg) return DBusException_UnusableMessage(); + return PyBool_FromLong (dbus_message_is_signal (self->msg, interface, + signal_name)); +} + +PyDoc_STRVAR(Message_get_member__doc__, +"get_member() -> str or None"); +static PyObject * +Message_get_member (Message *self, PyObject *unused) +{ + const char *c_str; + + if (!self->msg) return DBusException_UnusableMessage(); + c_str = dbus_message_get_member (self->msg); + if (!c_str) { + Py_RETURN_NONE; + } + return PyString_FromString(c_str); +} + +PyDoc_STRVAR(Message_has_member__doc__, +"has_member(name: str or None) -> bool"); +static PyObject * +Message_has_member (Message *self, PyObject *args) +{ + const char *name; + + if (!PyArg_ParseTuple(args, "z:has_member", &name)) { + return NULL; + } + if (!self->msg) return DBusException_UnusableMessage(); + return PyBool_FromLong (dbus_message_has_member(self->msg, name)); +} + +PyDoc_STRVAR(Message_set_member__doc__, +"set_member(unique_name: str or None)"); +static PyObject * +Message_set_member (Message *self, PyObject *args) +{ + const char *name; + + if (!PyArg_ParseTuple(args, "z:set_member", &name)) { + return NULL; + } + if (!self->msg) return DBusException_UnusableMessage(); + if (!_validate_member_name(name)) return NULL; + if (!dbus_message_set_member (self->msg, name)) return PyErr_NoMemory(); + Py_RETURN_NONE; +} + +PyDoc_STRVAR(Message_get_path__doc__, +"get_path() -> ObjectPath or None\n\n" +"Return the message's destination object path (if it's a method call) or\n" +"source object path (if it's a method reply or a signal) or None (if it\n" +"has no path).\n"); +static PyObject * +Message_get_path (Message *self, PyObject *unused) +{ + const char *c_str; + + if (!self->msg) return DBusException_UnusableMessage(); + c_str = dbus_message_get_path (self->msg); + if (!c_str) { + Py_RETURN_NONE; + } + return PyObject_CallFunction((PyObject *)&ObjectPathType, "(s)", c_str); +} + +PyDoc_STRVAR(Message_get_path_decomposed__doc__, +"get_path_decomposed() -> list of str, or None\n\n" +"Return a list of path components (e.g. /foo/bar -> ['foo','bar'], / -> [])\n" +"or None if the message has no associated path.\n"); +static PyObject * +Message_get_path_decomposed (Message *self, PyObject *unused) +{ + char **paths, **ptr; + PyObject *ret = PyList_New(0); + + if (!ret) return NULL; + if (!self->msg) { + Py_DECREF (ret); + return DBusException_UnusableMessage (); + } + if (!dbus_message_get_path_decomposed (self->msg, &paths)) { + Py_DECREF (ret); + return PyErr_NoMemory (); + } + if (!paths) { + Py_DECREF(ret); + Py_RETURN_NONE; + } + for (ptr = paths; *ptr; ptr++) { + PyObject *str = PyString_FromString (*ptr); + + if (!str) { + Py_DECREF (ret); + ret = NULL; + break; + } + if (PyList_Append (ret, str) < 0) { + Py_DECREF (ret); + ret = NULL; + break; + } + Py_DECREF (str); + str = NULL; + } + dbus_free_string_array (paths); + return ret; +} + +PyDoc_STRVAR(Message_has_path__doc__, +"has_path(name: str or None) -> bool"); +static PyObject * +Message_has_path (Message *self, PyObject *args) +{ + const char *name; + + if (!PyArg_ParseTuple(args, "z:has_path", &name)) { + return NULL; + } + if (!self->msg) return DBusException_UnusableMessage(); + return PyBool_FromLong (dbus_message_has_path(self->msg, name)); +} + +PyDoc_STRVAR(Message_set_path__doc__, +"set_path(name: str or None)"); +static PyObject * +Message_set_path (Message *self, PyObject *args) +{ + const char *name; + + if (!PyArg_ParseTuple(args, "z:set_path", &name)) return NULL; + if (!self->msg) return DBusException_UnusableMessage(); + if (!dbus_message_has_path(self->msg, name)) return PyErr_NoMemory(); + Py_RETURN_NONE; +} + +PyDoc_STRVAR(Message_get_signature__doc__, +"get_signature() -> Signature or None"); +static PyObject * +Message_get_signature (Message *self, PyObject *unused) +{ + const char *c_str; + + if (!self->msg) return DBusException_UnusableMessage(); + c_str = dbus_message_get_signature (self->msg); + if (!c_str) { + return PyObject_CallFunction((PyObject *)&SignatureType, "(s)", ""); + } + return PyObject_CallFunction((PyObject *)&SignatureType, "(s)", c_str); +} + +PyDoc_STRVAR(Message_has_signature__doc__, +"has_signature(signature: str) -> bool"); +static PyObject * +Message_has_signature (Message *self, PyObject *args) +{ + const char *name; + + if (!PyArg_ParseTuple(args, "s:has_signature", &name)) { + return NULL; + } + if (!self->msg) return DBusException_UnusableMessage(); + return PyBool_FromLong (dbus_message_has_signature (self->msg, name)); +} + +PyDoc_STRVAR(Message_get_sender__doc__, +"get_sender() -> str or None\n\n" +"Return the message's sender unique name, or None if none.\n"); +static PyObject * +Message_get_sender (Message *self, PyObject *unused) +{ + const char *c_str; + + if (!self->msg) return DBusException_UnusableMessage(); + c_str = dbus_message_get_sender (self->msg); + if (!c_str) { + Py_RETURN_NONE; + } + return PyString_FromString(c_str); +} + +PyDoc_STRVAR(Message_has_sender__doc__, +"has_sender(unique_name: str) -> bool"); +static PyObject * +Message_has_sender (Message *self, PyObject *args) +{ + const char *name; + + if (!PyArg_ParseTuple(args, "s:has_sender", &name)) { + return NULL; + } + if (!self->msg) return DBusException_UnusableMessage(); + return PyBool_FromLong (dbus_message_has_sender (self->msg, name)); +} + +PyDoc_STRVAR(Message_set_sender__doc__, +"set_sender(unique_name: str or None)"); +static PyObject * +Message_set_sender (Message *self, PyObject *args) +{ + const char *name; + + if (!PyArg_ParseTuple(args, "z:set_sender", &name)) { + return NULL; + } + if (!self->msg) return DBusException_UnusableMessage(); + if (!_validate_bus_name(name, 1, 0)) return NULL; + if (!dbus_message_set_sender (self->msg, name)) return PyErr_NoMemory(); + Py_RETURN_NONE; +} + +PyDoc_STRVAR(Message_get_destination__doc__, +"get_destination() -> str or None\n\n" +"Return the message's destination bus name, or None if none.\n"); +static PyObject * +Message_get_destination(Message *self, PyObject *unused) +{ + const char *c_str; + + if (!self->msg) return DBusException_UnusableMessage(); + c_str = dbus_message_get_destination(self->msg); + if (!c_str) { + Py_RETURN_NONE; + } + return PyString_FromString(c_str); +} + +PyDoc_STRVAR(Message_has_destination__doc__, +"has_destination(bus_name: str) -> bool"); +static PyObject * +Message_has_destination (Message *self, PyObject *args) +{ + const char *name; + + if (!PyArg_ParseTuple(args, "s:has_destination", &name)) { + return NULL; + } + if (!self->msg) return DBusException_UnusableMessage(); + return PyBool_FromLong (dbus_message_has_destination (self->msg, name)); +} + +PyDoc_STRVAR(Message_set_destination__doc__, +"set_destination(bus_name: str or None)"); +static PyObject * +Message_set_destination (Message *self, PyObject *args) +{ + const char *name; + + if (!PyArg_ParseTuple(args, "z:set_destination", &name)) { + return NULL; + } + if (!self->msg) return DBusException_UnusableMessage(); + if (!_validate_bus_name(name, 1, 1)) return NULL; + if (!dbus_message_set_destination (self->msg, name)) return PyErr_NoMemory(); + Py_RETURN_NONE; +} + +PyDoc_STRVAR(Message_get_interface__doc__, +"get_interface() -> str or None"); +static PyObject * +Message_get_interface (Message *self, PyObject *unused) +{ + const char *c_str; + + if (!self->msg) return DBusException_UnusableMessage(); + c_str = dbus_message_get_interface (self->msg); + if (!c_str) { + Py_RETURN_NONE; + } + return PyString_FromString(c_str); +} + +PyDoc_STRVAR(Message_has_interface__doc__, +"has_interface(interface: str or None) -> bool"); +static PyObject * +Message_has_interface(Message *self, PyObject *args) +{ + const char *name; + + if (!PyArg_ParseTuple(args, "z:has_interface", &name)) { + return NULL; + } + if (!self->msg) return DBusException_UnusableMessage(); + return PyBool_FromLong(dbus_message_has_interface (self->msg, name)); +} + +PyDoc_STRVAR(Message_set_interface__doc__, +"set_interface(name: str or None)"); +static PyObject * +Message_set_interface (Message *self, PyObject *args) +{ + const char *name; + + if (!PyArg_ParseTuple(args, "z:set_interface", &name)) { + return NULL; + } + if (!self->msg) return DBusException_UnusableMessage(); + if (!_validate_interface_name(name)) return NULL; + if (!dbus_message_set_interface (self->msg, name)) return PyErr_NoMemory(); + Py_RETURN_NONE; +} + +PyDoc_STRVAR(Message_get_error_name__doc__, +"get_error_name() -> str or None"); +static PyObject * +Message_get_error_name (Message *self, PyObject *unused) +{ + const char *c_str; + + if (!self->msg) return DBusException_UnusableMessage(); + c_str = dbus_message_get_error_name (self->msg); + if (!c_str) { + Py_RETURN_NONE; + } + return PyString_FromString(c_str); +} + +PyDoc_STRVAR(Message_set_error_name__doc__, +"set_error_name(name: str or None)"); +static PyObject * +Message_set_error_name(Message *self, PyObject *args) +{ + const char *name; + + if (!PyArg_ParseTuple(args, "z:set_error_name", &name)) { + return NULL; + } + if (!self->msg) return DBusException_UnusableMessage(); + if (!_validate_error_name(name)) return NULL; + if (!dbus_message_set_error_name(self->msg, name)) return PyErr_NoMemory(); + Py_RETURN_NONE; +} + +#include "message-append-impl.h" +#include "message-get-args-impl.h" + +static PyMethodDef Message_tp_methods[] = { + {"copy", (PyCFunction)Message_copy, + METH_NOARGS, Message_copy__doc__}, + {"is_method_call", (PyCFunction)Message_is_method_call, + METH_VARARGS, Message_is_method_call__doc__}, + {"is_signal", (PyCFunction)Message_is_signal, + METH_VARARGS, Message_is_signal__doc__}, + {"is_error", (PyCFunction)Message_is_error, + METH_VARARGS, Message_is_error__doc__}, + + {"get_args", (PyCFunction)Message_get_args, + METH_VARARGS|METH_KEYWORDS, Message_get_args__doc__}, + {"guess_signature", (PyCFunction)Message_guess_signature, + METH_VARARGS|METH_STATIC, Message_guess_signature__doc__}, + {"append", (PyCFunction)Message_append, + METH_VARARGS|METH_KEYWORDS, Message_append__doc__}, + + {"get_auto_start", (PyCFunction)Message_get_auto_start, + METH_NOARGS, Message_get_auto_start__doc__}, + {"set_auto_start", (PyCFunction)Message_set_auto_start, + METH_VARARGS, Message_set_auto_start__doc__}, + {"get_destination", (PyCFunction)Message_get_destination, + METH_NOARGS, Message_get_destination__doc__}, + {"set_destination", (PyCFunction)Message_set_destination, + METH_VARARGS, Message_set_destination__doc__}, + {"has_destination", (PyCFunction)Message_has_destination, + METH_VARARGS, Message_has_destination__doc__}, + {"get_error_name", (PyCFunction)Message_get_error_name, + METH_NOARGS, Message_get_error_name__doc__}, + {"set_error_name", (PyCFunction)Message_set_error_name, + METH_VARARGS, Message_set_error_name__doc__}, + {"get_interface", (PyCFunction)Message_get_interface, + METH_NOARGS, Message_get_interface__doc__}, + {"set_interface", (PyCFunction)Message_set_interface, + METH_VARARGS, Message_set_interface__doc__}, + {"has_interface", (PyCFunction)Message_has_interface, + METH_VARARGS, Message_has_interface__doc__}, + {"get_member", (PyCFunction)Message_get_member, + METH_NOARGS, Message_get_member__doc__}, + {"set_member", (PyCFunction)Message_set_member, + METH_VARARGS, Message_set_member__doc__}, + {"has_member", (PyCFunction)Message_has_member, + METH_VARARGS, Message_has_member__doc__}, + {"get_path", (PyCFunction)Message_get_path, + METH_NOARGS, Message_get_path__doc__}, + {"get_path_decomposed", (PyCFunction)Message_get_path_decomposed, + METH_NOARGS, Message_get_path_decomposed__doc__}, + {"set_path", (PyCFunction)Message_set_path, + METH_VARARGS, Message_set_path__doc__}, + {"has_path", (PyCFunction)Message_has_path, + METH_VARARGS, Message_has_path__doc__}, + {"get_no_reply", (PyCFunction)Message_get_no_reply, + METH_NOARGS, Message_get_no_reply__doc__}, + {"set_no_reply", (PyCFunction)Message_set_no_reply, + METH_VARARGS, Message_set_no_reply__doc__}, + {"get_reply_serial", (PyCFunction)Message_get_reply_serial, + METH_NOARGS, Message_get_reply_serial__doc__}, + {"set_reply_serial", (PyCFunction)Message_set_reply_serial, + METH_VARARGS, Message_set_reply_serial__doc__}, + {"get_sender", (PyCFunction)Message_get_sender, + METH_NOARGS, Message_get_sender__doc__}, + {"set_sender", (PyCFunction)Message_set_sender, + METH_VARARGS, Message_set_sender__doc__}, + {"has_sender", (PyCFunction)Message_has_sender, + METH_VARARGS, Message_has_sender__doc__}, + {"get_serial", (PyCFunction)Message_get_serial, + METH_NOARGS, Message_get_serial__doc__}, + {"get_signature", (PyCFunction)Message_get_signature, + METH_NOARGS, Message_get_signature__doc__}, + {"has_signature", (PyCFunction)Message_has_signature, + METH_VARARGS, Message_has_signature__doc__}, + {"get_type", (PyCFunction)Message_get_type, + METH_NOARGS, Message_get_type__doc__}, + {NULL, NULL, 0, NULL} +}; + +static PyTypeObject MessageType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_dbus_bindings.Message", /*tp_name*/ + sizeof(Message), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)Message_tp_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + Message_tp_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + Message_tp_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + Message_tp_new, /* tp_new */ +}; + +static PyTypeObject MethodCallMessageType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_dbus_bindings.MethodCallMessage", /*tp_name*/ + 0, /*tp_basicsize*/ + 0, /*tp_itemsize*/ + 0, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + MethodCallMessage_tp_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + DEFERRED_ADDRESS(&MessageType), /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)MethodCallMessage_tp_init, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + +static PyTypeObject MethodReturnMessageType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_dbus_bindings.MethodReturnMessage", /*tp_name*/ + 0, /*tp_basicsize*/ + 0, /*tp_itemsize*/ + 0, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + MethodReturnMessage_tp_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + DEFERRED_ADDRESS(&MessageType), /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)MethodReturnMessage_tp_init, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + +static PyTypeObject SignalMessageType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_dbus_bindings.SignalMessage", /*tp_name*/ + 0, /*tp_basicsize*/ + 0, /*tp_itemsize*/ + 0, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + SignalMessage_tp_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + DEFERRED_ADDRESS(&MessageType), /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)SignalMessage_tp_init, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + +static PyTypeObject ErrorMessageType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_dbus_bindings.ErrorMessage", /*tp_name*/ + 0, /*tp_basicsize*/ + 0, /*tp_itemsize*/ + 0, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + ErrorMessage_tp_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + DEFERRED_ADDRESS(&MessageType), /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)ErrorMessage_tp_init, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + +static inline int +init_message_types(void) +{ + if (PyType_Ready(&MessageType) < 0) return 0; + + MethodCallMessageType.tp_base = &MessageType; + if (PyType_Ready(&MethodCallMessageType) < 0) return 0; + + MethodReturnMessageType.tp_base = &MessageType; + if (PyType_Ready(&MethodReturnMessageType) < 0) return 0; + + SignalMessageType.tp_base = &MessageType; + if (PyType_Ready(&SignalMessageType) < 0) return 0; + + ErrorMessageType.tp_base = &MessageType; + if (PyType_Ready(&ErrorMessageType) < 0) return 0; + + return 1; +} + +static inline int +insert_message_types(PyObject *this_module) +{ + if (PyModule_AddObject(this_module, "Message", + (PyObject *)&MessageType) < 0) return 0; + + if (PyModule_AddObject(this_module, "MethodCallMessage", + (PyObject *)&MethodCallMessageType) < 0) return 0; + + if (PyModule_AddObject(this_module, "MethodReturnMessage", + (PyObject *)&MethodReturnMessageType) < 0) return 0; + + if (PyModule_AddObject(this_module, "ErrorMessage", + (PyObject *)&ErrorMessageType) < 0) return 0; + + if (PyModule_AddObject(this_module, "SignalMessage", + (PyObject *)&SignalMessageType) < 0) return 0; + + return 1; +} + +/* vim:set ft=c cino< sw=4 sts=4 et: */ diff --git a/_dbus_bindings/module.c b/_dbus_bindings/module.c new file mode 100644 index 0000000..801f23b --- /dev/null +++ b/_dbus_bindings/module.c @@ -0,0 +1,139 @@ +/* Main module source for the _dbus_bindings extension. + * + * Copyright (C) 2006 Collabora Ltd. + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <Python.h> +#include <structmember.h> + +#define INSIDE_DBUS_BINDINGS +#include "dbus_bindings.h" + +PyDoc_STRVAR(module_doc, ""); + +#include "debug-impl.h" /* DBG, USING_DBG, DBG_EXC */ +#include "generic-impl.h" /* Non D-Bus support code */ +#include "validation-impl.h" /* Interface name, etc., validation */ +#include "exceptions-impl.h" /* Exception base classes */ +#include "signature-impl.h" /* Signature and its custom iterator */ +#include "types-impl.h" /* IntNN, UIntNN, ObjectPath */ +#include "containers-impl.h" /* Array, Dict, Variant */ +#include "bytes-impl.h" /* Byte, ByteArray */ +#include "message-impl.h" /* Message and subclasses */ +#include "pending-call-impl.h" /* PendingCall */ +#include "conn-impl.h" /* Connection */ +#include "bus-impl.h" /* Bus */ + +static PyMethodDef module_functions[] = { + {NULL, NULL, 0, NULL} +}; + +PyMODINIT_FUNC +init_dbus_bindings (void) +{ + PyObject *this_module; + + if (!init_generic ()) return; + if (!init_exception_types ()) return; + if (!init_signature ()) return; + if (!init_types ()) return; + if (!init_container_types ()) return; + if (!init_byte_types ()) return; + if (!init_message_types ()) return; + if (!init_pending_call ()) return; + if (!init_conn_types ()) return; + if (!init_bus_types ()) return; + + this_module = Py_InitModule3 ("_dbus_bindings", module_functions, module_doc); + if (!this_module) return; + + if (!insert_exception_types (this_module)) return; + if (!insert_signature (this_module)) return; + if (!insert_types (this_module)) return; + if (!insert_container_types (this_module)) return; + if (!insert_byte_types (this_module)) return; + if (!insert_message_types (this_module)) return; + if (!insert_pending_call (this_module)) return; + if (!insert_conn_types (this_module)) return; + if (!insert_bus_types (this_module)) return; + +#define ADD_CONST_VAL(x, v) \ + if (PyModule_AddIntConstant (this_module, x, v) < 0) abort(); +#define ADD_CONST_PREFIXED(x) ADD_CONST_VAL(#x, DBUS_##x) +#define ADD_CONST(x) ADD_CONST_VAL(#x, x) + + ADD_CONST(DBUS_START_REPLY_SUCCESS) + ADD_CONST(DBUS_START_REPLY_ALREADY_RUNNING) + + ADD_CONST_PREFIXED(RELEASE_NAME_REPLY_RELEASED) + ADD_CONST_PREFIXED(RELEASE_NAME_REPLY_NON_EXISTENT) + ADD_CONST_PREFIXED(RELEASE_NAME_REPLY_NOT_OWNER) + + ADD_CONST_PREFIXED(REQUEST_NAME_REPLY_PRIMARY_OWNER) + ADD_CONST_PREFIXED(REQUEST_NAME_REPLY_IN_QUEUE) + ADD_CONST_PREFIXED(REQUEST_NAME_REPLY_EXISTS) + ADD_CONST_PREFIXED(REQUEST_NAME_REPLY_ALREADY_OWNER) + + ADD_CONST_PREFIXED(NAME_FLAG_ALLOW_REPLACEMENT) + ADD_CONST_PREFIXED(NAME_FLAG_REPLACE_EXISTING) + ADD_CONST_PREFIXED(NAME_FLAG_DO_NOT_QUEUE) + + ADD_CONST_PREFIXED(BUS_SESSION) + ADD_CONST_PREFIXED(BUS_SYSTEM) + ADD_CONST_PREFIXED(BUS_STARTER) + + ADD_CONST_PREFIXED(MESSAGE_TYPE_INVALID) + ADD_CONST_PREFIXED(MESSAGE_TYPE_METHOD_CALL) + ADD_CONST_PREFIXED(MESSAGE_TYPE_METHOD_RETURN) + ADD_CONST_PREFIXED(MESSAGE_TYPE_ERROR) + ADD_CONST_PREFIXED(MESSAGE_TYPE_SIGNAL) + + ADD_CONST_PREFIXED(MESSAGE_TYPE_SIGNAL) + + ADD_CONST_PREFIXED(TYPE_INVALID) + ADD_CONST_PREFIXED(TYPE_BYTE) + ADD_CONST_PREFIXED(TYPE_BOOLEAN) + ADD_CONST_PREFIXED(TYPE_INT16) + ADD_CONST_PREFIXED(TYPE_UINT16) + ADD_CONST_PREFIXED(TYPE_INT32) + ADD_CONST_PREFIXED(TYPE_UINT32) + ADD_CONST_PREFIXED(TYPE_INT64) + ADD_CONST_PREFIXED(TYPE_UINT64) + ADD_CONST_PREFIXED(TYPE_DOUBLE) + ADD_CONST_PREFIXED(TYPE_STRING) + ADD_CONST_PREFIXED(TYPE_OBJECT_PATH) + ADD_CONST_PREFIXED(TYPE_SIGNATURE) + ADD_CONST_PREFIXED(TYPE_ARRAY) + ADD_CONST_PREFIXED(TYPE_STRUCT) + ADD_CONST_VAL("STRUCT_BEGIN", DBUS_STRUCT_BEGIN_CHAR) + ADD_CONST_VAL("STRUCT_END", DBUS_STRUCT_END_CHAR) + ADD_CONST_PREFIXED(TYPE_VARIANT) + ADD_CONST_PREFIXED(TYPE_DICT_ENTRY) + ADD_CONST_VAL("DICT_ENTRY_BEGIN", DBUS_DICT_ENTRY_BEGIN_CHAR) + ADD_CONST_VAL("DICT_ENTRY_END", DBUS_DICT_ENTRY_END_CHAR) + + ADD_CONST_PREFIXED(HANDLER_RESULT_HANDLED) + ADD_CONST_PREFIXED(HANDLER_RESULT_NOT_YET_HANDLED) + ADD_CONST_PREFIXED(HANDLER_RESULT_NEED_MEMORY) +} + +/* vim:set ft=c cino< sw=4 sts=4 et: */ diff --git a/_dbus_bindings/pending-call-impl.h b/_dbus_bindings/pending-call-impl.h new file mode 100644 index 0000000..c0bca56 --- /dev/null +++ b/_dbus_bindings/pending-call-impl.h @@ -0,0 +1,230 @@ +/* Implementation of PendingCall helper type for D-Bus bindings. + * + * Copyright (C) 2006 Collabora Ltd. + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +PyDoc_STRVAR(PendingCall_tp_doc, +"Object representing a pending D-Bus call, returned by\n" +"Connection._send_with_reply(). Cannot be instantiated directly.\n" +); + +static PyTypeObject PendingCallType; + +static inline int PendingCall_Check (PyObject *o) +{ + return (o->ob_type == &PendingCallType) + || PyObject_IsInstance(o, (PyObject *)&PendingCallType); +} + +typedef struct { + PyObject_HEAD + DBusPendingCall *pc; +} PendingCall; + +PyDoc_STRVAR(PendingCall_cancel__doc__, +"cancel()\n\n" +"Cancel this pending call. Its reply will be ignored and the associated\n" +"reply handler will never be called.\n"); +static PyObject * +PendingCall_cancel(PendingCall *self, PyObject *unused) +{ + Py_BEGIN_ALLOW_THREADS + dbus_pending_call_cancel(self->pc); + Py_END_ALLOW_THREADS + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PendingCall__block__doc__, +"_block()\n\n" +"Block until this pending call has completed and the associated\n" +"reply handler has been called.\n" +"\n" +"This can lead to a deadlock, if the called method tries to make a\n" +"synchronous call to a method in this application. As a result, it's\n" +"probably a bad idea.\n"); +static PyObject * +PendingCall__block(PendingCall *self, PyObject *unused) +{ + Py_BEGIN_ALLOW_THREADS + dbus_pending_call_block(self->pc); + Py_END_ALLOW_THREADS + Py_RETURN_NONE; +} + +static void +_pending_call_notify_function(DBusPendingCall *pc, + PyObject *handler) +{ + PyGILState_STATE gil = PyGILState_Ensure(); + DBusMessage *msg = dbus_pending_call_steal_reply(pc); + Message *msg_obj; + + if (!msg) { + /* omg, what happened here? the notify should only get called + * when we have a reply */ + PyErr_Warn(PyExc_UserWarning, "D-Bus notify function was called " + "for an incomplete pending call (shouldn't happen)"); + } else { + msg_obj = (Message *)Message_ConsumeDBusMessage(msg); + if (msg_obj) { + Py_XDECREF(PyObject_CallFunctionObjArgs(handler, msg_obj, NULL)); + } + /* else OOM has happened - not a lot we can do about that, + * except possibly making it fatal (FIXME?) */ + } + + PyGILState_Release(gil); +} + +PyDoc_STRVAR(PendingCall_get_completed__doc__, +"get_completed() -> bool\n\n" +"Return true if this pending call has completed.\n\n" +"If so, its associated reply handler has been called and it is no\n" +"longer meaningful to cancel it.\n"); +static PyObject * +PendingCall_get_completed(PendingCall *self, PyObject *unused) +{ + dbus_bool_t ret; + + Py_BEGIN_ALLOW_THREADS + ret = dbus_pending_call_get_completed(self->pc); + Py_END_ALLOW_THREADS + return PyBool_FromLong(ret); +} + +/* Steals the reference to the pending call. */ +static PyObject * +PendingCall_ConsumeDBusPendingCall (DBusPendingCall *pc, PyObject *callable) +{ + dbus_bool_t ret; + PendingCall *self = PyObject_New(PendingCall, &PendingCallType); + + if (!self) { + Py_BEGIN_ALLOW_THREADS + dbus_pending_call_cancel(pc); + dbus_pending_call_unref(pc); + Py_END_ALLOW_THREADS + return NULL; + } + + Py_INCREF(callable); + + Py_BEGIN_ALLOW_THREADS + ret = dbus_pending_call_set_notify(pc, + (DBusPendingCallNotifyFunction)_pending_call_notify_function, + (void *)callable, (DBusFreeFunction)Glue_TakeGILAndXDecref); + Py_END_ALLOW_THREADS + + if (!ret) { + PyErr_NoMemory(); + Py_DECREF(callable); + Py_DECREF(self); + Py_BEGIN_ALLOW_THREADS + dbus_pending_call_cancel(pc); + dbus_pending_call_unref(pc); + Py_END_ALLOW_THREADS + return NULL; + } + self->pc = pc; + return (PyObject *)self; +} + +static void +PendingCall_tp_dealloc (PendingCall *self) +{ + if (self->pc) { + Py_BEGIN_ALLOW_THREADS + dbus_pending_call_unref(self->pc); + Py_END_ALLOW_THREADS + } + PyObject_Del (self); +} + +static PyMethodDef PendingCall_tp_methods[] = { + {"_block", (PyCFunction)PendingCall__block, METH_NOARGS, + PendingCall__block__doc__}, + {"cancel", (PyCFunction)PendingCall_cancel, METH_NOARGS, + PendingCall_cancel__doc__}, + {"get_completed", (PyCFunction)PendingCall_get_completed, METH_NOARGS, + PendingCall_get_completed__doc__}, + {NULL, NULL, 0, NULL} +}; + +static PyTypeObject PendingCallType = { + PyObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type)) + 0, + "_dbus_bindings.PendingCall", + sizeof(PendingCall), + 0, + (destructor)PendingCall_tp_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + PendingCall_tp_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PendingCall_tp_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + /* deliberately not callable! Use PendingCall_ConsumeDBusPendingCall */ + 0, /* tp_new */ +}; + +static inline int +init_pending_call (void) +{ + if (PyType_Ready (&PendingCallType) < 0) return 0; + return 1; +} + +static inline int +insert_pending_call (PyObject *this_module) +{ + if (PyModule_AddObject (this_module, "PendingCall", + (PyObject *)&PendingCallType) < 0) return 0; + return 1; +} + +/* vim:set ft=c cino< sw=4 sts=4 et: */ diff --git a/_dbus_bindings/signature-impl.h b/_dbus_bindings/signature-impl.h new file mode 100644 index 0000000..eda3177 --- /dev/null +++ b/_dbus_bindings/signature-impl.h @@ -0,0 +1,238 @@ +/* Implementation of Signature type for D-Bus bindings. + * + * Copyright (C) 2006 Collabora Ltd. + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +PyDoc_STRVAR(Signature_tp_doc, +"Signature(str) -> Signature\n" +"\n" +"Signature is a string subclass whose values are restricted to valid D-Bus\n" +"signatures. When iterated over, instead of individual characters it\n" +"produces Signature instances representing single complete types.\n" +); + +static PyTypeObject SignatureType; + +static inline int Signature_Check(PyObject *o) +{ + return (o->ob_type == &SignatureType) + || PyObject_IsInstance(o, (PyObject *)&SignatureType); +} + +typedef struct { + PyObject_HEAD + PyObject *string; + DBusSignatureIter iter; +} SignatureIter; + +static void +SignatureIter_tp_dealloc (SignatureIter *self) +{ + Py_XDECREF(self->string); + self->string = NULL; + PyObject_Del(self); +} + +static PyObject * +SignatureIter_tp_iternext (SignatureIter *self) +{ + char *sig; + PyObject *obj; + + /* Stop immediately if finished or not correctly initialized */ + if (!self->string) return NULL; + + sig = dbus_signature_iter_get_signature(&(self->iter)); + if (!sig) return PyErr_NoMemory(); + obj = PyObject_CallFunction((PyObject *)&SignatureType, "s", sig); + dbus_free(sig); + if (!obj) return NULL; + + if (!dbus_signature_iter_next(&(self->iter))) { + /* mark object as having been finished with */ + Py_DECREF(self->string); + self->string = NULL; + } + + return obj; +} + +static PyObject * +SignatureIter_tp_iter(PyObject *self) +{ + Py_INCREF(self); + return self; +} + +static PyTypeObject SignatureIterType = { + PyObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type)) + 0, + "_dbus_bindings._SignatureIter", + sizeof(SignatureIter), + 0, + (destructor)SignatureIter_tp_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + SignatureIter_tp_iter, /* tp_iter */ + (iternextfunc)SignatureIter_tp_iternext, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + /* deliberately not callable! Use iter(Signature) instead */ + 0, /* tp_new */ + 0, /* tp_free */ +}; + +static PyObject * +Signature_tp_iter (PyObject *self) +{ + SignatureIter *iter = PyObject_New(SignatureIter, &SignatureIterType); + if (!iter) return NULL; + + if (PyString_AS_STRING (self)[0]) { + Py_INCREF(self); + iter->string = self; + dbus_signature_iter_init(&(iter->iter), PyString_AS_STRING(self)); + } + else { + /* this is a null string, make a null iterator */ + iter->string = NULL; + } + return (PyObject *)iter; +} + +static PyObject * +Signature_tp_new (PyTypeObject *cls, PyObject *args, PyObject *kwargs) +{ + PyObject *tuple, *self; + const char *str = NULL; + static char *argnames[] = {"object_path", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s:__new__", argnames, + &str)) return NULL; + if (!dbus_signature_validate(str, NULL)) { + PyErr_SetString(PyExc_ValueError, "Corrupt type signature"); + return NULL; + } + tuple = Py_BuildValue("(s)", str); + if (!tuple) return NULL; + self = PyString_Type.tp_new(cls, tuple, NULL); + Py_DECREF(tuple); + return self; +} + +static PyTypeObject SignatureType = { + PyObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type)) + 0, + "dbus.Signature", + 0, + 0, + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + str_subclass_tp_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + Signature_tp_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + Signature_tp_iter, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + DEFERRED_ADDRESS(&PyString_Type), /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + Signature_tp_new, /* tp_new */ + 0, /* tp_free */ +}; + +static inline int +init_signature(void) +{ + if (PyType_Ready(&SignatureIterType) < 0) return 0; + + SignatureType.tp_base = &PyString_Type; + if (PyType_Ready(&SignatureType) < 0) return 0; + /* disable the tp_print copied from PyString_Type, so tp_repr gets called as + desired */ + SignatureType.tp_print = NULL; + + return 1; +} + +static inline int +insert_signature (PyObject *this_module) +{ + Py_INCREF(&SignatureType); + if (PyModule_AddObject(this_module, "Signature", + (PyObject *)&SignatureType) < 0) return 0; + Py_INCREF(&SignatureIterType); + if (PyModule_AddObject(this_module, "_SignatureIter", + (PyObject *)&SignatureIterType) < 0) return 0; + + return 1; +} + +/* vim:set ft=c cino< sw=4 sts=4 et: */ diff --git a/_dbus_bindings/test.py b/_dbus_bindings/test.py new file mode 100644 index 0000000..08f0fd0 --- /dev/null +++ b/_dbus_bindings/test.py @@ -0,0 +1,30 @@ +import unittest + +from _dbus_bindings import Int32, Int64, ObjectPath +from _dbus_bindings import Message, SignalMessage + +class TestArgs(unittest.TestCase): + def setUp(self): + pass + + def testMisc(self): + self.assertEquals(Message.guess_signature('abc', 123), 'si') + + m = SignalMessage('/', 'com.example.Stuff', 'Done') + self.assertEquals(m.get_signature(), '') + m.extend('abc', 123, signature='si') + self.assertEquals(m.get_args(), ('abc', Int32(123))) + self.assertEquals(m.get_signature(), 'si') + self.assert_(m.has_signature('si')) + self.assert_(not m.has_signature('sx')) + + m = SignalMessage('/', 'com.example.Stuff', 'Done') + m.extend('abc', 123) + self.assertEquals(m.get_args(), ('abc', Int32(123))) + self.assertEquals(m.get_signature(), 'si') + m.extend(('/foo', 1), signature='(ox)') + self.assertEquals(m.get_args(), (u'abc', Int32(123), + (ObjectPath('/foo'), Int64(1L)))) + +if __name__ == '__main__': + unittest.main() diff --git a/_dbus_bindings/types-impl.h b/_dbus_bindings/types-impl.h new file mode 100644 index 0000000..e69d460 --- /dev/null +++ b/_dbus_bindings/types-impl.h @@ -0,0 +1,635 @@ +/* Simple D-Bus types: integers of various sizes, and ObjectPath. + * + * Copyright (C) 2006 Collabora Ltd. + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <stdint.h> + +/* IntNN, UIntNN ==================================================== */ + +static PyTypeObject Int16Type, Int32Type, Int64Type; +static PyTypeObject UInt16Type, UInt32Type, UInt64Type; +static PyTypeObject ObjectPathType; + +#define DEFINE_CHECK(type) \ +static inline int type##_Check (PyObject *o) \ +{ \ + return (o->ob_type == &type##Type) \ + || PyObject_IsInstance(o, (PyObject *)&type##Type); \ +} +DEFINE_CHECK(Int16) +DEFINE_CHECK(Int32) +DEFINE_CHECK(Int64) +DEFINE_CHECK(UInt16) +DEFINE_CHECK(UInt32) +DEFINE_CHECK(UInt64) +DEFINE_CHECK(ObjectPath) +#undef DEFINE_CHECK + +/* The Int and UInt types are nice and simple. +FIXME: use Python int as base for UInt32 and Int64 on 64-bit platforms? +*/ + +PyDoc_STRVAR(Int16_tp_doc, +"A signed 16-bit integer (a subtype of int)."); + +PyDoc_STRVAR(UInt16_tp_doc, +"An unsigned 16-bit integer (a subtype of int)."); + +PyDoc_STRVAR(Int32_tp_doc, +"A signed 32-bit integer (a subtype of int)."); + +PyDoc_STRVAR(UInt32_tp_doc, +"An unsigned 32-bit integer (a subtype of long)."); + +PyDoc_STRVAR(Int64_tp_doc, +"A signed 16-bit integer (a subtype of long)."); + +PyDoc_STRVAR(UInt64_tp_doc, +"An unsigned 64-bit integer (a subtype of long)."); + +static dbus_int16_t +int16_range_check(PyObject *obj) +{ + long i = PyInt_AsLong (obj); + if (i == -1 && PyErr_Occurred ()) return -1; + if (i < -0x8000 || i > 0x7fff) { + PyErr_Format(PyExc_OverflowError, "Value %d out of range for Int16", + (int)i); + return -1; + } + return i; +} + +static PyObject * +Int16_tp_new(PyTypeObject *cls, PyObject *args, PyObject *kwargs) +{ + PyObject *self = (PyInt_Type.tp_new)(cls, args, kwargs); + if (self && int16_range_check(self) == -1 && PyErr_Occurred()) { + Py_DECREF(self); + return NULL; + } + return self; +} + +static PyTypeObject Int16Type = { + PyObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type)) + 0, + "dbus.Int16", + 0, + 0, + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + int_subclass_tp_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + Int16_tp_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + DEFERRED_ADDRESS(&PyInt_Type), /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + Int16_tp_new, /* tp_new */ +}; + +static dbus_uint16_t +uint16_range_check(PyObject *obj) +{ + long i = PyInt_AsLong(obj); + if (i == -1 && PyErr_Occurred()) return (dbus_uint16_t)(-1); + if (i < 0 || i > 0xffff) { + PyErr_Format(PyExc_OverflowError, "Value %d out of range for UInt16", + (int)i); + return (dbus_uint16_t)(-1); + } + return i; +} + +static PyObject * +UInt16_tp_new(PyTypeObject *cls, PyObject *args, PyObject *kwargs) +{ + PyObject *self = (PyInt_Type.tp_new)(cls, args, kwargs); + if (self && uint16_range_check(self) == (dbus_uint16_t)(-1) + && PyErr_Occurred()) { + Py_DECREF (self); + return NULL; + } + return self; +} + +static PyTypeObject UInt16Type = { + PyObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type)) + 0, + "dbus.UInt16", + 0, + 0, + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + int_subclass_tp_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + UInt16_tp_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + DEFERRED_ADDRESS(&PyInt_Type), /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + UInt16_tp_new, /* tp_new */ +}; + +static dbus_int32_t +int32_range_check(PyObject *obj) +{ + long i = PyInt_AsLong(obj); + if (i == -1 && PyErr_Occurred()) return -1; + if (i < INT32_MIN || i > INT32_MAX) { + PyErr_Format(PyExc_OverflowError, "Value %d out of range for Int32", + (int)i); + return -1; + } + return i; +} + +static PyObject * +Int32_tp_new(PyTypeObject *cls, PyObject *args, PyObject *kwargs) +{ + PyObject *self = (PyInt_Type.tp_new)(cls, args, kwargs); + if (int32_range_check(self) == -1 && PyErr_Occurred()) return NULL; + return self; +} + +static PyTypeObject Int32Type = { + PyObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type)) + 0, + "dbus.Int32", + 0, + 0, + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + int_subclass_tp_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + Int32_tp_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + DEFERRED_ADDRESS(&PyInt_Type), /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + Int32_tp_new, /* tp_new */ +}; + +static dbus_uint32_t +uint32_range_check(PyObject *obj) +{ + unsigned long i; + PyObject *long_obj = PyNumber_Long(obj); + + if (!long_obj) return (dbus_uint32_t)(-1); + i = PyLong_AsUnsignedLong(long_obj); + if (i == (unsigned long)(-1) && PyErr_Occurred()) { + Py_DECREF(long_obj); + return (dbus_uint32_t)(-1); + } + if (i < 0 || i > UINT32_MAX) { + PyErr_Format(PyExc_OverflowError, "Value %d out of range for UInt32", + (int)i); + Py_DECREF(long_obj); + return (dbus_uint32_t)(-1); + } + Py_DECREF(long_obj); + return i; +} + +static PyObject * +UInt32_tp_new(PyTypeObject *cls, PyObject *args, PyObject *kwargs) +{ + PyObject *self = (PyLong_Type.tp_new)(cls, args, kwargs); + if (self && uint32_range_check(self) == (dbus_uint32_t)(-1) + && PyErr_Occurred()) { + Py_DECREF(self); + return NULL; + } + return self; +} + +static PyTypeObject UInt32Type = { + PyObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type)) + 0, + "dbus.UInt32", + 0, + 0, + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + long_subclass_tp_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + UInt32_tp_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + DEFERRED_ADDRESS(&PyLong_Type), /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + UInt32_tp_new, /* tp_new */ +}; + +#if defined(DBUS_HAVE_INT64) && defined(HAVE_LONG_LONG) + +static dbus_int64_t +int64_range_check(PyObject *obj) +{ + unsigned long i; + PyObject *long_obj = PyNumber_Long(obj); + + if (!long_obj) return -1; + i = PyLong_AsLongLong(long_obj); + if (i == -1 && PyErr_Occurred()) { + Py_DECREF(long_obj); + return -1; + } + if (i < INT64_MIN || i > INT64_MAX) { + PyErr_SetString(PyExc_OverflowError, "Value out of range for Int64"); + Py_DECREF(long_obj); + return -1; + } + Py_DECREF(long_obj); + return i; +} + +static PyObject * +Int64_tp_new(PyTypeObject *cls, PyObject *args, PyObject *kwargs) +{ + PyObject *self = (PyLong_Type.tp_new)(cls, args, kwargs); + if (self && int64_range_check(self) == -1 && PyErr_Occurred()) { + Py_DECREF(self); + return NULL; + } + return self; +} + +static PyTypeObject Int64Type = { + PyObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type)) + 0, + "dbus.Int64", + 0, + 0, + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + long_subclass_tp_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + Int64_tp_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + DEFERRED_ADDRESS(&PyLong_Type), /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + Int64_tp_new, /* tp_new */ +}; + +static dbus_uint64_t +uint64_range_check(PyObject *obj) +{ + unsigned long long i; + PyObject *long_obj = PyNumber_Long(obj); + + if (!long_obj) return (dbus_uint64_t)(-1); + i = PyLong_AsUnsignedLongLong(long_obj); + if (i == (unsigned long long)(-1) && PyErr_Occurred()) { + Py_DECREF(long_obj); + return (dbus_uint64_t)(-1); + } + if (i < 0 || i > UINT64_MAX) { + PyErr_SetString(PyExc_OverflowError, "Value out of range for UInt64"); + Py_DECREF(long_obj); + return (dbus_uint64_t)(-1); + } + Py_DECREF(long_obj); + return i; +} + +static PyObject * +UInt64_tp_new (PyTypeObject *cls, PyObject *args, PyObject *kwargs) +{ + PyObject *self = (PyLong_Type.tp_new)(cls, args, kwargs); + if (self && uint64_range_check(self) == (dbus_uint64_t)(-1) + && PyErr_Occurred()) { + Py_DECREF(self); + return NULL; + } + return self; +} + +static PyTypeObject UInt64Type = { + PyObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type)) + 0, + "dbus.UInt64", + 0, + 0, + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + long_subclass_tp_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + UInt64_tp_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + DEFERRED_ADDRESS(&PyLong_Type), /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + UInt64_tp_new, /* tp_new */ +}; + +#endif /* defined(DBUS_HAVE_INT64) && defined(HAVE_LONG_LONG) */ + +/* Object path ====================================================== */ + +PyDoc_STRVAR(ObjectPath_tp_doc, +"ObjectPath(path: str)\n\n" +"A D-Bus object path, such as '/com/example/MyApp/Documents/abc'.\n" +"\n" +"ObjectPath is a subtype of str, and object-paths behave like strings.\n"); + +static PyObject * +ObjectPath_tp_new(PyTypeObject *cls, PyObject *args, PyObject *kwargs) +{ + PyObject *tuple, *self; + const char *str = NULL; + static char *argnames[] = {"object_path", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s:__new__", argnames, + &str)) return NULL; + if (!_validate_object_path(str)) { + return NULL; + } + tuple = Py_BuildValue("(s)", str); + if (!tuple) return NULL; + self = PyString_Type.tp_new(cls, tuple, NULL); + Py_DECREF(tuple); + return self; +} + +/* Slight subtlety: In conn-methods-impl.h we assume that object paths + * are nicely simple: in particular, that they can never reference + * other objects, and that they share strings' concept of equality + * and hash value. If you break these assumptions, alter + * _register_object_path and _unregister_object_path to coerce + * ObjectPaths into ordinary Python strings. */ +static PyTypeObject ObjectPathType = { + PyObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type)) + 0, + "dbus.ObjectPath", + 0, + 0, + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + str_subclass_tp_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + ObjectPath_tp_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + DEFERRED_ADDRESS(&PyString_Type), /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + ObjectPath_tp_new, /* tp_new */ +}; + +static inline int +init_types(void) +{ + Int16Type.tp_base = &PyInt_Type; + if (PyType_Ready(&Int16Type) < 0) return 0; + /* disable the tp_print copied from PyInt_Type, so tp_repr gets called as + desired */ + Int16Type.tp_print = NULL; + + UInt16Type.tp_base = &PyInt_Type; + if (PyType_Ready(&UInt16Type) < 0) return 0; + UInt16Type.tp_print = NULL; + + Int32Type.tp_base = &PyInt_Type; + if (PyType_Ready(&Int32Type) < 0) return 0; + Int32Type.tp_print = NULL; + + UInt32Type.tp_base = &PyLong_Type; + if (PyType_Ready(&UInt32Type) < 0) return 0; + UInt32Type.tp_print = NULL; + + Int64Type.tp_base = &PyLong_Type; + if (PyType_Ready(&Int64Type) < 0) return 0; + Int64Type.tp_print = NULL; + + UInt64Type.tp_base = &PyLong_Type; + if (PyType_Ready(&UInt64Type) < 0) return 0; + UInt64Type.tp_print = NULL; + + ObjectPathType.tp_base = &PyString_Type; + if (PyType_Ready(&ObjectPathType) < 0) return 0; + ObjectPathType.tp_print = NULL; + + return 1; +} + +static inline int +insert_types(PyObject *this_module) +{ + Py_INCREF(&Int16Type); + Py_INCREF(&UInt16Type); + Py_INCREF(&Int32Type); + Py_INCREF(&UInt32Type); + Py_INCREF(&Int64Type); + Py_INCREF(&UInt64Type); + if (PyModule_AddObject(this_module, "Int16", + (PyObject *)&Int16Type) < 0) return 0; + if (PyModule_AddObject(this_module, "UInt16", + (PyObject *)&UInt16Type) < 0) return 0; + if (PyModule_AddObject(this_module, "Int32", + (PyObject *)&Int32Type) < 0) return 0; + if (PyModule_AddObject(this_module, "UInt32", + (PyObject *)&UInt32Type) < 0) return 0; + if (PyModule_AddObject(this_module, "Int64", + (PyObject *)&Int64Type) < 0) return 0; + if (PyModule_AddObject(this_module, "UInt64", + (PyObject *)&UInt64Type) < 0) return 0; + + Py_INCREF(&ObjectPathType); + if (PyModule_AddObject(this_module, "ObjectPath", + (PyObject *)&ObjectPathType) < 0) return 0; + + return 1; +} + +/* vim:set ft=c cino< sw=4 sts=4 et: */ diff --git a/_dbus_bindings/validation-impl.h b/_dbus_bindings/validation-impl.h new file mode 100644 index 0000000..9762fcb --- /dev/null +++ b/_dbus_bindings/validation-impl.h @@ -0,0 +1,241 @@ +/* Implementation of various validation functions for use in dbus-python. + * + * Copyright (C) 2006 Collabora Ltd. <http://www.collabora.co.uk/> + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +static dbus_bool_t _validate_bus_name(const char *name, + dbus_bool_t may_be_unique, + dbus_bool_t may_be_not_unique) +{ + dbus_bool_t dot = FALSE; + dbus_bool_t unique; + char last; + const char *ptr; + + if (name[0] == '\0') { + PyErr_SetString(PyExc_ValueError, "Invalid bus name: " + "may not be empty"); + return FALSE; + } + unique = (name[0] == ':'); + if (unique && !may_be_unique) { + PyErr_Format(PyExc_ValueError, "Invalid well-known bus name '%s':" + "only unique names may start with ':'", name); + return FALSE; + } + if (!unique && !may_be_not_unique) { + PyErr_Format(PyExc_ValueError, "Invalid unique bus name '%s': " + "unique names must start with ':'", name); + return FALSE; + } + if (strlen(name) > 255) { + PyErr_Format(PyExc_ValueError, "Invalid bus name '%s': " + "too long (> 255 characters)", name); + return FALSE; + } + last = '\0'; + for (ptr = name + (unique ? 1 : 0); *ptr; ptr++) { + if (*ptr == '.') { + dot = TRUE; + if (last == '.') { + PyErr_Format(PyExc_ValueError, "Invalid bus name '%s': " + "contains substring '..'", name); + return FALSE; + } + else if (last == '\0') { + PyErr_Format(PyExc_ValueError, "Invalid bus name '%s': " + "must not start with '.'", name); + return FALSE; + } + } + else if (*ptr >= '0' && *ptr <= '9') { + if (!unique) { + if (last == '.') { + PyErr_Format(PyExc_ValueError, "Invalid bus name '%s': " + "a digit may not follow '.' except in a " + "unique name starting with ':'", name); + return FALSE; + } + else if (last == '\0') { + PyErr_Format(PyExc_ValueError, "Invalid bus name '%s': " + "must not start with a digit", name); + return FALSE; + } + } + } + else if ((*ptr < 'a' || *ptr > 'z') && + (*ptr < 'A' || *ptr > 'Z') && *ptr != '_' && *ptr != '-') { + PyErr_Format(PyExc_ValueError, "Invalid bus name '%s': " + "contains invalid character '%c'", name, *ptr); + return FALSE; + } + last = *ptr; + } + if (last == '.') { + PyErr_Format(PyExc_ValueError, "Invalid bus name '%s': must " + "not end with '.'", name); + return FALSE; + } + if (!dot) { + PyErr_Format(PyExc_ValueError, "Invalid bus name '%s': must " + "contain '.'", name); + return FALSE; + } + return TRUE; +} + +static dbus_bool_t _validate_member_name(const char *name) +{ + const char *ptr; + + if (name[0] == '\0') { + PyErr_SetString(PyExc_ValueError, "Invalid member name: may not " + "be empty"); + return FALSE; + } + if (strlen(name) > 255) { + PyErr_Format(PyExc_ValueError, "Invalid member name '%s': " + "too long (> 255 characters)", name); + return FALSE; + } + for (ptr = name; *ptr; ptr++) { + if (*ptr >= '0' && *ptr <= '9') { + if (ptr == name) { + PyErr_Format(PyExc_ValueError, "Invalid member name '%s': " + "must not start with a digit", name); + return FALSE; + } + } + else if ((*ptr < 'a' || *ptr > 'z') && + (*ptr < 'A' || *ptr > 'Z') && *ptr != '_') { + PyErr_Format(PyExc_ValueError, "Invalid member name '%s': " + "contains invalid character '%c'", name, *ptr); + return FALSE; + } + } + return TRUE; +} + +static dbus_bool_t _validate_interface_name(const char *name) +{ + dbus_bool_t dot = FALSE; + char last; + const char *ptr; + + if (name[0] == '\0') { + PyErr_SetString(PyExc_ValueError, "Invalid interface or error name: " + "may not be empty"); + return FALSE; + } + if (strlen(name) > 255) { + PyErr_Format(PyExc_ValueError, "Invalid interface or error name '%s': " + "too long (> 255 characters)", name); + return FALSE; + } + last = '\0'; + for (ptr = name; *ptr; ptr++) { + if (*ptr == '.') { + dot = TRUE; + if (last == '.') { + PyErr_Format(PyExc_ValueError, "Invalid interface or error name '%s': " + "contains substring '..'", name); + return FALSE; + } + else if (last == '\0') { + PyErr_Format(PyExc_ValueError, "Invalid interface or error name '%s': " + "must not start with '.'", name); + return FALSE; + } + } + else if (*ptr >= '0' && *ptr <= '9') { + if (last == '.') { + PyErr_Format(PyExc_ValueError, "Invalid interface or error name '%s': " + "a digit may not follow '.'", name); + return FALSE; + } + else if (last == '\0') { + PyErr_Format(PyExc_ValueError, "Invalid interface or error name '%s': " + "must not start with a digit", name); + return FALSE; + } + } + else if ((*ptr < 'a' || *ptr > 'z') && + (*ptr < 'A' || *ptr > 'Z') && *ptr != '_') { + PyErr_Format(PyExc_ValueError, "Invalid interface or error name '%s': " + "contains invalid character '%c'", name, *ptr); + return FALSE; + } + last = *ptr; + } + if (last == '.') { + PyErr_Format(PyExc_ValueError, "Invalid interface or error name '%s': must " + "not end with '.'", name); + return FALSE; + } + if (!dot) { + PyErr_Format(PyExc_ValueError, "Invalid interface or error name '%s': must " + "contain '.'", name); + return FALSE; + } + return TRUE; +} + +static inline dbus_bool_t _validate_error_name(const char *name) +{ + return _validate_interface_name(name); +} + +static dbus_bool_t _validate_object_path(const char *path) +{ + const char *ptr; + + if (path[0] != '/') { + PyErr_Format(PyExc_ValueError, "Invalid object path '%s': does not " + "start with '/'", path); + return FALSE; + } + if (path[1] == '\0') return TRUE; + for (ptr = path + 1; *ptr; ptr++) { + if (*ptr == '/') { + if (ptr[-1] == '/') { + PyErr_Format(PyExc_ValueError, "Invalid object path '%s': " + "contains substring '//'", path); + return FALSE; + } + } + else if ((*ptr < 'a' || *ptr > 'z') && + (*ptr < 'A' || *ptr > 'Z') && + (*ptr < '0' || *ptr > '9') && *ptr != '_') { + PyErr_Format(PyExc_ValueError, "Invalid object path '%s': " + "contains invalid character '%c'", path, *ptr); + return FALSE; + } + } + if (ptr[-1] == '/') { + PyErr_Format(PyExc_ValueError, "Invalid object path '%s': ends " + "with '/' and is not just '/'", path); + return FALSE; + } + return TRUE; +} + +/* vim:set ft=c cino< sw=4 sts=4 et: */ diff --git a/_dbus_glib_bindings/module.c b/_dbus_glib_bindings/module.c new file mode 100644 index 0000000..4a225f2 --- /dev/null +++ b/_dbus_glib_bindings/module.c @@ -0,0 +1,74 @@ +/* Glue code to attach the GObject main loop to D-Bus from within Python. + * + * Copyright (C) 2006 Collabora Ltd. + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <Python.h> +#include "dbus_bindings.h" +#include <dbus/dbus-glib.h> +#include <dbus/dbus-glib-lowlevel.h> + +PyDoc_STRVAR(module_doc, ""); + +PyDoc_STRVAR(setup_with_g_main__doc__, +"setup_with_g_main(conn: dbus.Connection)"); +static PyObject * +setup_with_g_main (PyObject *unused, PyObject *args) +{ + DBusConnection *dbc; + PyObject *conn; + if (!PyArg_ParseTuple(args, "O:setup_with_g_main", &conn)) return NULL; + + dbc = Connection_BorrowDBusConnection (conn); + if (!dbc) return NULL; + dbus_connection_setup_with_g_main (dbc, NULL); + Py_RETURN_NONE; +} + +PyDoc_STRVAR(gthreads_init__doc__, +"gthreads_init()"); +static PyObject * +gthreads_init (PyObject *unused, PyObject *also_unused) +{ + dbus_g_thread_init(); + Py_RETURN_NONE; +} + +static PyMethodDef module_functions[] = { + {"setup_with_g_main", setup_with_g_main, METH_VARARGS, + setup_with_g_main__doc__}, + {"gthreads_init", gthreads_init, METH_NOARGS, gthreads_init__doc__}, + {NULL, NULL, 0, NULL} +}; + +PyMODINIT_FUNC +init_dbus_glib_bindings (void) +{ + PyObject *this_module; + + if (import_dbus_bindings () < 0) return; + this_module = Py_InitModule3 ("_dbus_glib_bindings", module_functions, + module_doc); + if (!this_module) return; +} + +/* vim:set ft=c cino< sw=4 sts=4 et: */ diff --git a/include/dbus_bindings.h b/include/dbus_bindings.h new file mode 100644 index 0000000..8b11585 --- /dev/null +++ b/include/dbus_bindings.h @@ -0,0 +1,64 @@ +/* C API for _dbus_bindings, used by _dbus_glib_bindings. + * + * Copyright (C) 2006 Collabora Ltd. + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef DBUS_BINDINGS_H +#define DBUS_BINDINGS_H + +#define DBUS_API_SUBJECT_TO_CHANGE 1 +#include <dbus/dbus.h> + +DBUS_BEGIN_DECLS + +#ifdef INSIDE_DBUS_BINDINGS + +static DBusConnection *Connection_BorrowDBusConnection (PyObject *); + +#else + +static void **dbus_bindings_API; +#define Connection_BorrowDBusConnection \ + (*(DBusConnection *(*)(PyObject *))dbus_bindings_API[0]) +static int +import_dbus_bindings(void) +{ + PyObject *module = PyImport_ImportModule ("_dbus_bindings"); + + if (module != NULL) + { + PyObject *c_api = PyObject_GetAttrString (module, "_C_API"); + if (c_api == NULL) return -1; + if (PyCObject_Check (c_api)) + { + dbus_bindings_API = (void **)PyCObject_AsVoidPtr (c_api); + } + Py_DECREF (c_api); + } + return 0; +} + +#endif + +DBUS_END_DECLS + +#endif |