From f4cffc0e49d48a8dbf158230d7e816d8713566da Mon Sep 17 00:00:00 2001 From: Seth Nickell Date: Mon, 22 Sep 2003 05:45:59 +0000 Subject: 2003-09-21 Seth Nickell First checkin of the Python bindings. * python/.cvsignore: * python/Makefile.am: * python/dbus_bindings.pyx.in: * python/dbus_h_wrapper.h: Pieces for Pyrex to operate on, building a dbus_bindings.so python module for low-level access to the DBus APIs. * python/dbus.py: High-level Python module for accessing DBus objects. * configure.in: * Makefile.am: Build stuff for the python bindings. * acinclude.m4: Extra macro needed for finding the Python C header files. --- ChangeLog | 25 ++ Makefile.am | 5 +- acinclude.m4 | 26 ++ configure.in | 40 ++ python/.cvsignore | 8 + python/Makefile.am | 28 ++ python/dbus.py | 196 ++++++++++ python/dbus_bindings.pyx.in | 900 ++++++++++++++++++++++++++++++++++++++++++++ python/dbus_h_wrapper.h | 3 + python/extract.py | 237 ++++++++++++ test/test-service.c | 5 +- 11 files changed, 1471 insertions(+), 2 deletions(-) create mode 100644 python/.cvsignore create mode 100644 python/Makefile.am create mode 100644 python/dbus.py create mode 100644 python/dbus_bindings.pyx.in create mode 100644 python/dbus_h_wrapper.h create mode 100644 python/extract.py diff --git a/ChangeLog b/ChangeLog index d0c88df3..530c47c8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,28 @@ +2003-09-21 Seth Nickell + + First checkin of the Python bindings. + + * python/.cvsignore: + * python/Makefile.am: + * python/dbus_bindings.pyx.in: + * python/dbus_h_wrapper.h: + + Pieces for Pyrex to operate on, building a dbus_bindings.so + python module for low-level access to the DBus APIs. + + * python/dbus.py: + + High-level Python module for accessing DBus objects. + + * configure.in: + * Makefile.am: + + Build stuff for the python bindings. + + * acinclude.m4: + + Extra macro needed for finding the Python C header files. + 2003-09-21 Havoc Pennington * glib/dbus-gproxy.c (dbus_gproxy_manager_new): start diff --git a/Makefile.am b/Makefile.am index 5bb5ae6a..47329972 100644 --- a/Makefile.am +++ b/Makefile.am @@ -16,8 +16,11 @@ if DBUS_USE_MCS MONO_SUBDIR=mono endif +if HAVE_PYTHON + PYTHON_SUBDIR=python +endif -SUBDIRS=dbus bus doc $(GLIB_SUBDIR) $(GCJ_SUBDIR) $(MONO_SUBDIR) $(QT_SUBDIR) test tools +SUBDIRS=dbus bus doc $(GLIB_SUBDIR) $(GCJ_SUBDIR) $(MONO_SUBDIR) $(QT_SUBDIR) $(PYTHON_SUBDIR) test tools pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = dbus-1.pc $(GLIB_PC) diff --git a/acinclude.m4 b/acinclude.m4 index c80e0acf..27da0a4c 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -55,3 +55,29 @@ AC_DEFUN(PKG_CHECK_MODULES, [ ]) + +dnl a macro to check for ability to create python extensions +dnl AM_CHECK_PYTHON_HEADERS([ACTION-IF-POSSIBLE], [ACTION-IF-NOT-POSSIBLE]) +dnl function also defines PYTHON_INCLUDES +AC_DEFUN([AM_CHECK_PYTHON_HEADERS], +[AC_REQUIRE([AM_PATH_PYTHON]) +AC_MSG_CHECKING(for headers required to compile python extensions) +dnl deduce PYTHON_INCLUDES +py_prefix=`$PYTHON -c "import sys; print sys.prefix"` +py_exec_prefix=`$PYTHON -c "import sys; print sys.exec_prefix"` +PYTHON_INCLUDES="-I${py_prefix}/include/python${PYTHON_VERSION}" +if test "$py_prefix" != "$py_exec_prefix"; then + PYTHON_INCLUDES="$PYTHON_INCLUDES -I${py_exec_prefix}/include/python${PYTHON_VERSION}" +fi +AC_SUBST(PYTHON_INCLUDES) +dnl check if the headers exist: +save_CPPFLAGS="$CPPFLAGS" +CPPFLAGS="$CPPFLAGS $PYTHON_INCLUDES" +AC_TRY_CPP([#include ],dnl +[AC_MSG_RESULT(found) +$1],dnl +[AC_MSG_RESULT(not found) +$2]) +CPPFLAGS="$save_CPPFLAGS" +]) + diff --git a/configure.in b/configure.in index e81a8d6a..6cf4accb 100644 --- a/configure.in +++ b/configure.in @@ -35,6 +35,8 @@ AC_ARG_ENABLE(gcov, [ --enable-gcov compile with coverage p AC_ARG_ENABLE(abstract-sockets, [ --enable-abstract-sockets use abstract socket namespace (linux only)],enable_abstract_sockets=$enableval,enable_abstract_sockets=auto) AC_ARG_ENABLE(gcj, [ --enable-gcj build gcj bindings],enable_gcj=$enableval,enable_gcj=no) AC_ARG_ENABLE(mono, [ --enable-mono build mono bindings],enable_mono=$enableval,enable_mono=no) +AC_ARG_ENABLE(python, [ --enable-python build python bindings],enable_python=$enableval,enable_python=auto) + AC_ARG_WITH(xml, [ --with-xml=[libxml/expat] XML library to use]) AC_ARG_WITH(init-scripts, [ --with-init-scripts=[redhat] Style of init scripts to install]) @@ -834,6 +836,42 @@ fi AC_DEFINE_UNQUOTED(DBUS_SESSION_SOCKET_DIR, "$DBUS_SESSION_SOCKET_DIR", [Where per-session bus puts its sockets]) AC_SUBST(DBUS_SESSION_SOCKET_DIR) +# Detect if we can build Python bindings (need python, python headers, and pyrex) +if test x$enable_python = xno; then + have_python=no +else + AC_MSG_NOTICE([Checking to see if we can build Python bindings]) + have_python=no + AM_PATH_PYTHON(2.2) + + if test -z "$PYTHON" ; then + AC_MSG_WARN([Python not found]) + else + AC_CHECK_PROGS(PYREX, pyrexc) + + if test -z "$PYREX" ; then + have_pyrex=no + else + have_pyrex=yes + fi + + AM_CHECK_PYTHON_HEADERS(have_python_headers=yes,have_python_headers=no) + + if test x$have_pyrex = xyes -a x$have_python_headers = xyes ; then + have_python=yes + fi + fi + + if test x$have_python = xno ; then + if test x$enable_python = xyes ; then + AC_MSG_ERROR([Building python explicitly requested, but can't build python bindings]) + else + AC_MSG_WARN([Couldn't find either Pyrex or the Python headers, not building Python bindings]) + fi + fi +fi + +AM_CONDITIONAL(HAVE_PYTHON, test x$have_python = xyes) AC_OUTPUT([ @@ -846,6 +884,7 @@ bus/dbus-daemon-1.1 Makefile dbus/Makefile glib/Makefile +python/Makefile qt/Makefile gcj/Makefile gcj/org/Makefile @@ -914,6 +953,7 @@ echo " Building checks: ${enable_checks} Building Qt bindings: ${have_qt} Building GLib bindings: ${have_glib} + Building Python bindings: ${have_python} Building GTK+ tools: ${have_gtk} Building X11 code: ${enable_x11} Building documentation: ${enable_docs} diff --git a/python/.cvsignore b/python/.cvsignore new file mode 100644 index 00000000..8902bbe5 --- /dev/null +++ b/python/.cvsignore @@ -0,0 +1,8 @@ +*.pyc +*.pyo +Makefile +Makefile.in +build +*.la +*.lo +*.o \ No newline at end of file diff --git a/python/Makefile.am b/python/Makefile.am new file mode 100644 index 00000000..792dc477 --- /dev/null +++ b/python/Makefile.am @@ -0,0 +1,28 @@ +INCLUDES=-I$(top_builddir) -I$(top_builddir)/glib $(DBUS_CLIENT_CFLAGS) $(DBUS_GLIB_CFLAGS) $(DBUS_GLIB_TOOL_CFLAGS) $(PYTHON_INCLUDES) + +dbusdir = $(pythondir) +dbus_PYTHON = dbus.py + +dbusbindingsdir = $(pythondir) +dbusbindings_LTLIBRARIES = dbus_bindings.la + +dbus_bindings_la_LDFLAGS = -module -avoid-version -fPIC -export-symbols-regex initdbus_bindings +dbus_bindings_la_LIBADD = $(top_builddir)/dbus/libdbus-1.la $(top_builddir)/glib/libdbus-glib-1.la +dbus_bindings_la_SOURCES = dbus_bindings.c + +EXTRA_DIST = \ + dbus_bindings.pyx.in \ + extract.py \ + setup.py \ + test.py + +CLEANFILES = \ + dbus_bindings.pyx \ + dbus_bindings.c + + +dbus_bindings.pyx: dbus_bindings.pyx.in extract.py + -$(PYTHON) extract.py dbus_bindings.pyx.in -I$(top_builddir) > dbus_bindings.pyx + +dbus_bindings.c: dbus_bindings.pyx + -pyrexc dbus_bindings.pyx diff --git a/python/dbus.py b/python/dbus.py new file mode 100644 index 00000000..a7cca56a --- /dev/null +++ b/python/dbus.py @@ -0,0 +1,196 @@ + +"""Module for high-level communication over the FreeDesktop.org Bus (DBus) + +DBus allows you to share and access remote objects between processes +running on the desktop, and also to access system services (such as +the print spool). + +To use DBus, first get a Bus object, which provides a connection to one +of a few standard dbus-daemon instances that might be running. From the +Bus you can get a RemoteService. A service is provided by an application or +process connected to the Bus, and represents a set of objects. Once you +have a RemoteService you can get a RemoteObject that implements a specific interface +(an interface is just a standard group of member functions). Then you can call +those member functions directly. + +You can think of a complete method call as looking something like: + +Bus:SESSION -> Service:org.gnome.Evolution -> Object:/org/gnome/Evolution/Inbox -> Interface: org.gnome.Evolution.MailFolder -> Method: Forward('message1', 'seth@gnome.org') + +This communicates over the SESSION Bus to the org.gnome.Evolution process to call the +Forward method of the /org/gnome/Evolution/Inbox object (which provides the +org.gnome.Evolution.MailFolder interface) with two string arguments. + +For example, the dbus-daemon itself provides a service and some objects: + +# Get a connection to the desktop-wide SESSION bus +bus = dbus.Bus(dbus.Bus.TYPE_SESSION) + +# Get the service provided by the dbus-daemon named org.freedesktop.DBus +dbus_service = bus.get_service('org.freedesktop.DBus') + +# Get a reference to the desktop bus' standard object, denoted +# by the path /org/freedesktop/DBus. The object /org/freedesktop/DBus +# implements the 'org.freedesktop.DBus' interface +dbus_object = dbus_service.get_object('/org/freedesktop/DBus', + 'org.freedesktop.DBus') + +# One of the member functions in the org.freedesktop.DBus interface +# is ListServices(), which provides a list of all the other services +# registered on this bus. Call it, and print the list. +print(dbus_object.ListServices()) +""" + +import dbus_bindings + +class Bus: + """A connection to a DBus daemon. + + One of three possible standard buses, the SESSION, SYSTEM, + or ACTIVATION bus + """ + TYPE_SESSION = dbus_bindings.BUS_SESSION + TYPE_SYSTEM = dbus_bindings.BUS_SYSTEM + TYPE_ACTIVATION = dbus_bindings.BUS_ACTIVATION + + def __init__(self, bus_type=TYPE_SESSION, glib_mainloop=True): + self._connection = dbus_bindings.bus_get(bus_type) + + if (glib_mainloop): + self._connection.setup_with_g_main() + + def get_service(self, service_name="org.freedesktop.Broadcast"): + """Get one of the RemoteServices connected to this Bus. service_name + is just a string of the form 'com.widgetcorp.MyService' + """ + return RemoteService(self._connection, service_name) + + def get_connection(self): + """Get the dbus_bindings.Connection object associated with this Bus""" + return self._connection + + +class RemoteObject: + """A remote Object. + + A RemoteObject is provided by a RemoteService on a particular Bus. RemoteObjects + have member functions, and can be called like normal Python objects. + """ + def __init__(self, connection, service_name, object_path, interface): + self._connection = connection + self._service_name = service_name + self._object_path = object_path + self._interface = interface + + def __getattr__(self, member): + if member == '__call__': + return object.__call__ + else: + return RemoteMethod(self._connection, self._service_name, + self._object_path, self._interface, member) + + +class RemoteMethod: + """A remote Method. + + Typically a member of a RemoteObject. Calls to the + method produce messages that travel over the Bus and are routed + to a specific Service. + """ + def __init__(self, connection, service_name, object_path, interface, method_name): + self._connection = connection + self._service_name = service_name + self._object_path = object_path + self._interface = interface + self._method_name = method_name + + def __call__(self, *args): + print ("Going to call object(%s).interface(%s).method(%s)" + % (self._object_path, self._interface, self._method_name)) + + message = dbus_bindings.MethodCall(self._object_path, self._interface, self._method_name) + message.set_destination(self._service_name) + + # Add the arguments to the function + iter = message.get_iter() + for arg in args: + print ("Adding arg %s" % (arg)) + print ("Append success is %d" % (iter.append(arg))) + print ("Args now %s" % (message.get_args_list())) + + reply_message = self._connection.send_with_reply_and_block(message, 5000) + + args_tuple = reply_message.get_args_list() + if (len(args_tuple) == 0): + return + elif (len(args_tuple) == 1): + return args_tuple[0] + else: + return args_tuple + +class Service: + """A base class for exporting your own Services across the Bus + + Just inherit from Service, providing the name of your service + (e.g. org.designfu.SampleService). + """ + def __init__(self, service_name, bus=None): + self._service_name = service_name + + if bus == None: + # Get the default bus + self._bus = Bus() + else: + self._bus = bus + + dbus_bindings.bus_acquire_service(self._bus.get_connection(), service_name) + + def get_bus(self): + """Get the Bus this Service is on""" + return self._bus + +class Object: + """A base class for exporting your own Objects across the Bus. + + Just inherit from Object and provide a list of methods to share + across the Bus. These will appear as member functions of your + ServiceObject. + """ + def __init__(self, object_path, methods_to_share, service): + self._object_path = object_path + self._service = service + self._object_methods = methods_to_share + self._bus = service.get_bus() + self._connection = self._bus.get_connection() + + self._connection.register_object_path(object_path, self._unregister_cb, self._message_cb) + + def _unregister_cb(self, connection): + print ("Unregister") + + def message_cb(self, connection, message): + print ("Message %s received" % (message)) + print ("MethodCall %s" % (message.get_member())) + +class RemoteService: + """A remote service providing objects. + + A service is typically a process or application that provides + remote objects, but can also be the broadcast service that + receives signals from all applications on the Bus. + """ + + def __init__(self, connection, service_name): + self._connection = connection + self._service_name = service_name + + def get_object(self, object_path, interface): + """Get an object provided by this Service that implements a + particular interface. object_path is a string of the form + '/com/widgetcorp/MyService/MyObject1'. interface looks a lot + like a service_name (they're often the same) and is of the form, + 'com.widgetcorp.MyInterface', and mostly just defines the + set of member functions that will be present in the object. + """ + return RemoteObject(self._connection, self._service_name, object_path, interface) + diff --git a/python/dbus_bindings.pyx.in b/python/dbus_bindings.pyx.in new file mode 100644 index 00000000..2e575edc --- /dev/null +++ b/python/dbus_bindings.pyx.in @@ -0,0 +1,900 @@ +# -*- Mode: Python -*- + +#include "dbus_h_wrapper.h" + +cdef extern from "stdlib.h": + cdef void *malloc(size_t size) + cdef void free(void *ptr) + +cdef extern from "dbus-glib.h": + ctypedef struct GMainContext + cdef void dbus_connection_setup_with_g_main (DBusConnection *connection, + GMainContext *context) + cdef void dbus_server_setup_with_g_main (DBusServer *server, + GMainContext *context) + +cdef extern from "Python.h": + void Py_XINCREF (object) + void Py_XDECREF (object) + + +ctypedef struct DBusError: + char *name + char *message + unsigned int dummy1 + unsigned int dummy2 + unsigned int dummy3 + unsigned int dummy4 + unsigned int dummy5 + void *padding1 + +ctypedef struct DBusMessageIter: + void *dummy1 + void *dummy2 + dbus_uint32_t dummy3 + int dummy4 + int dummy5 + int dummy6 + int dummy7 + int dummy8 + int dummy9 + int dummy10 + int dummy11 + int pad1 + int pad2 + void *pad3 + +ctypedef struct DBusObjectPathVTable: + DBusObjectPathUnregisterFunction unregister_function + DBusObjectPathMessageFunction message_function + void (* dbus_internal_pad1) (void *) + void (* dbus_internal_pad2) (void *) + void (* dbus_internal_pad3) (void *) + void (* dbus_internal_pad4) (void *) + + +class DBusException(Exception): + pass + +class ConnectionError(Exception): + pass + +cdef void cunregister_function_handler (DBusConnection *connection, + void *user_data): + print ("cunregister_function_handler() called") + tup = user_data + function = tup[0] + args = [Connection(_conn=connection)] + function(*args) + +cdef DBusHandlerResult cmessage_function_handler (DBusConnection *connection, + DBusMessage *msg, + void *user_data): + print ("cmessage_function_handler() called") + tup = user_data + print (tup) + function = tup[1] + message = Message(_create=0) + message._set_msg(msg) + args = [Connection(_conn=connection), + message] + retval = function(*args) + if (retval == None): + retval = DBUS_HANDLER_RESULT_HANDLED + return retval + +cdef DBusHandlerResult chandle_message_function_handler (DBusConnection *connection, + DBusMessage *msg, + void *user_data): + function = user_data + messagein = Message(_create=0) + messagein._set_msg(msg) + args = [Connection(_conn=connection), + messagein] + retval = function(*args) + if (retval == None): + retval = DBUS_HANDLER_RESULT_HANDLED + return retval + +cdef class Connection: + cdef DBusConnection *conn + + # FIXME: this is a major major hack. We use this because casting values to + # python objects and returning seemed to be corrupting them. This is a "global variable" :-( + cdef char **_parsed_path + + def __init__(self, address=None, _conn=None): + cdef DBusError error + dbus_error_init(&error) + if _conn != NULL: + self.conn = _conn + dbus_connection_ref(self.conn) + else: + self.conn = dbus_connection_open(address, + &error) + if dbus_error_is_set(&error): + raise DBusException, error.message + + dbus_connection_ref(self.conn) + + def _set_conn(self, conn): + self.conn = conn + + def _get_conn(self): + return self.conn + + #FIXME: this is totally busted, don't use a class shared member like parsed_path + def _build_parsed_path(self, path_element_list): + cdef char **cpatharray + size = len(path_element_list) + cpatharray = malloc(sizeof(char*) * (size + 1)) + + for i in range(size): + path_element = path_element_list[i] + cpatharray[i] = path_element + + cpatharray[size] = NULL + + self._parsed_path = cpatharray + + def get_base_service(self): + return bus_get_base_service(self) + + def setup_with_g_main(self): + dbus_connection_setup_with_g_main(self.conn, NULL) + + def disconnect(self): + dbus_connection_disconnect(self.conn) + + def get_is_connected(self): + return dbus_connection_get_is_connected(self.conn) + + def get_is_authenticated(self): + return dbus_connection_get_is_authenticated(self.conn) + + def flush(self): + dbus_connection_flush(self.conn) + + def borrow_message(self): + m = Message(_create=0) + m._set_msg(dbus_connection_borrow_message(self.conn)) + return m + + def return_message(self, message): + msg = message._get_msg() + dbus_connection_return_message(self.conn, msg) + + def steal_borrowed_message(self, message): + msg = message._get_msg() + dbus_connection_steal_borrowed_message(self.conn, + msg) + + def pop_message(self): + cdef DBusMessage *msg + msg = dbus_connection_pop_message(self.conn) + if msg != NULL: + m = Message(_create=0) + m._set_msg(msg) + else: + m = None + return m + + def get_dispatch_status(self): + return dbus_connection_get_dispatch_status(self.conn) + + def dispatch(self): + return dbus_connection_dispatch(self.conn) + + def send(self, message): + #cdef dbus_uint32_t client_serial + #if type(message) != Message: + # raise TypeError + + msg = message._get_msg() + retval = dbus_connection_send(self.conn, + msg, + NULL) + return retval + + def send_with_reply(self, message, timeout_milliseconds): + cdef dbus_bool_t retval + cdef DBusPendingCall *cpending_call + cdef DBusError error + dbus_error_init(&error) + + cpending_call = NULL + + msg = message._get_msg() + + retval = dbus_connection_send_with_reply(self.conn, + msg, + &cpending_call, + timeout_milliseconds) + + if dbus_error_is_set(&error): + raise DBusException, error.message + + if (cpending_call != NULL): + pending_call = PendingCall(cpending_call) + else: + pending_call = None + + return (retval, pending_call) + + def send_with_reply_and_block(self, message, + timeout_milliseconds=0): + cdef DBusMessage * retval + cdef DBusError error + dbus_error_init(&error) + + msg = message._get_msg() + + print ("About to block") + + retval = dbus_connection_send_with_reply_and_block( + self.conn, + msg, + timeout_milliseconds, + &error) + + print ("done") + + if dbus_error_is_set(&error): + raise DBusException, error.message + + if retval == NULL: + raise AssertionError + + m = Message(_create=0) + m._set_msg(retval) + return m + + def set_watch_functions(self, add_function, remove_function, data): + pass + + def set_timeout_functions(self, add_function, remove_function, data): + pass + + def set_wakeup_main_function(self, wakeup_main_function, data): + pass + + # FIXME: set_dispatch_status_function, get_unix_user, set_unix_user_function + + def add_filter(self, function): + return dbus_connection_add_filter(self.conn, + chandle_message_function_handler, + function, + NULL) + + + #FIXME: remove_filter + # this is pretty tricky, we want to only remove the filter + # if we truly have no more calls to our message_function_handler...ugh + + def set_data(self, slot, data): + pass + + def get_data(self, slot): + pass + + def set_max_message_size(self, size): + dbus_connection_set_max_message_size(self.conn, size) + + def get_max_message_size(self): + return dbus_connection_get_max_message_size(self.conn) + + def set_max_received_size(self, size): + dbus_connection_set_max_received_size(self.conn, size) + + def get_max_received_size(self): + return dbus_connection_get_max_received_size(self.conn) + + def get_outgoing_size(self): + return dbus_connection_get_outgoing_size(self.conn) + + # preallocate_send, free_preallocated_send, send_preallocated + + def register_object_path(self, path, unregister_cb, message_cb): + cdef DBusObjectPathVTable cvtable + + cvtable.unregister_function = cunregister_function_handler + cvtable.message_function = cmessage_function_handler + + user_data = [unregister_cb, message_cb] + #Py_XINCREF(user_data) + + path_element_list = path[1:].split('/') + self._build_parsed_path(path_element_list) + + return dbus_connection_register_object_path(self.conn, self._parsed_path, &cvtable, + user_data) + + def register_fallback(self, path, unregister_cb, message_cb): + cdef DBusObjectPathVTable cvtable + + cvtable.unregister_function = cunregister_function_handler + cvtable.message_function = cmessage_function_handler + + user_data = [unregister_cb, message_cb] + Py_XINCREF(user_data) + + print ("Ref inced") + + path_element_list = path[1:].split('/') + self._build_parsed_path(path_element_list) + + return dbus_connection_register_fallback(self.conn, self._parsed_path, &cvtable, + user_data) + + #FIXME: unregister_object_path , see problems with remove_filter + + def list_registered (self, parent_path): + cdef char **cchild_entries + cdef dbus_bool_t retval + + path_element_list = parent_path[1:].split('/') + self._build_parsed_path(path_element_list) + + retval = dbus_connection_list_registered(self.conn, self._parsed_path, &cchild_entries) + + if (not retval): + #FIXME: raise out of memory exception? + return None + + i = 0 + child_entries = [] + print ("cchild_entries[0] is %d" % (cchild_entries[0])) + while (cchild_entries[i] != NULL): + child_entries.append(cchild_entries[i]) + i = i + 1 + + dbus_free_string_array(cchild_entries) + + return child_entries + + +cdef class PendingCall: + cdef DBusPendingCall *pending_call + + def __init__(self, _pending_call): + self.pending_call = _pending_call + dbus_pending_call_ref(self.pending_call) + + def _get_pending_call(self): + return self.pending_call + + def cancel(self): + dbus_pending_call_cancel(self.pending_call) + + def get_completed(self): + return dbus_pending_call_get_completed(self.pending_call) + + def get_reply(self): + message = Message(_create=0) + message._set_msg(dbus_pending_call_get_reply(self.pending_call)) + return message + + def block(self): + dbus_pending_call_block(self.pending_call) + +cdef class Watch: + cdef DBusWatch* watch + def __init__(self, cwatch): + self.watch = cwatch + + def get_fd(self): + return dbus_watch_get_fd(self.watch) + + # FIXME: not picked up correctly by extract.py + #def get_flags(self): + # return dbus_watch_get_flags(self.watch) + + def handle(self, flags): + return dbus_watch_handle(self.watch, flags) + + def get_enabled(self): + return dbus_watch_get_enabled(self.watch) + +cdef class MessageIter: + cdef DBusMessageIter *iter + cdef DBusMessageIter real_iter + + + def __init__(self, message): + self.iter = &self.real_iter + msg = message._get_msg() + dbus_message_iter_init(msg, self.iter) + + def get_iter(self): + return self.iter + + def has_next(self): + return dbus_message_iter_has_next(self.iter) + + def next(self): + return dbus_message_iter_next(self.iter) + + def get(self): + arg_type = self.get_arg_type() + + if arg_type == TYPE_INVALID: + raise TypeError, 'Invalid arg type in MessageIter' + elif arg_type == TYPE_STRING: + retval = self.get_string() + elif arg_type == TYPE_INT32: + retval = self.get_int32() + elif arg_type == TYPE_UINT32: + retval = self.get_uint32() + elif arg_type == TYPE_DOUBLE: + retval = self.get_double() + elif arg_type == TYPE_BYTE: + retval = self.get_byte() + elif arg_type == TYPE_BOOLEAN: + retval = self.get_boolean() + elif arg_type == TYPE_ARRAY: + array_type = self.get_array_type() + + if array_type == TYPE_STRING: + retval = self.get_string_array() + elif array_type == TYPE_BOOLEAN: + retval = self.get_boolean_array() + else: + raise TypeError, "Unknown array type %d in MessageIter" % (array_type) + else: + raise TypeError, 'Unknown arg type %d in MessageIter' % (argtype) + + return retval + + def get_arg_type(self): + return dbus_message_iter_get_arg_type(self.iter) + + def get_array_type(self): + return dbus_message_iter_get_array_type(self.iter) + + #FIXME: implement get_byte + #def get_byte(self): + # return dbus_message_iter_get_byte(self.iter) + + def get_boolean(self): + return dbus_message_iter_get_boolean(self.iter) + + def get_int32(self): + return dbus_message_iter_get_int32(self.iter) + + def get_uint32(self): + return dbus_message_iter_get_uint32(self.iter) + + def get_double(self): + return dbus_message_iter_get_double(self.iter) + + def get_string(self): + return dbus_message_iter_get_string(self.iter) + + def get_dict_key(self): + return dbus_message_iter_get_dict_key(self.iter) + + # FIXME: implement dbus_message_iter_get_named + # dbus_message_iter_init_array_iterator + + def get_byte_array(self): + cdef int len + cdef unsigned char *retval + dbus_message_iter_get_byte_array(self.iter, &retval, &len) + list = [] + for i from 0 <= i < len: + list.append(chr(retval[i])) + return list + + # FIXME: implement dbus_message_iter_get_boolean_array + # dbus_message_iter_get_int32_array + # dbus_message_iter_get_uint32_array + # dbus_message_iter_get_double_array + + def get_string_array(self): + cdef int len + cdef char **retval + + dbus_message_iter_get_string_array(self.iter, &retval, &len) + list = [] + for i from 0 <= i < len: + list.append(retval[i]) + return list + + # dbus_message_append_iter_init included in class Message + + #FIXME: handle all the different types? + def append(self, value): + value_type = type(value) + + if value_type == bool: + retval = self.append_boolean(value) + elif value_type == int: + retval = self.append_int32(value) + elif value_type == float: + retval = self.append_double(value) + elif value_type == str: + retval = self.append_string(value) + elif value_type == list: + if (len(list) == 1): + raise TypeError, "Empty list" + list_type = type(list[0]) + if list_type == str: + self.append_string_array(list) + else: + raise TypeError, "List of unknown type '%s'" % (list_type) + else: + raise TypeError, "Argument of unknown type '%s'" % (value_type) + + return retval + + def append_nil(self): + return dbus_message_iter_append_nil(self.iter) + + def append_boolean(self, value): + return dbus_message_iter_append_boolean(self.iter, value) + + def append_byte(self, value): + return dbus_message_iter_append_byte(self.iter, value) + + def append_int32(self, value): + return dbus_message_iter_append_int32(self.iter, value) + + def append_uint32(self, value): + return dbus_message_iter_append_uint32(self.iter, value) + + def append_double(self, value): + return dbus_message_iter_append_double(self.iter, value) + + def append_string(self, value): + return dbus_message_iter_append_string(self.iter, value) + + # FIXME: dbus_message_iter_append_named + + def append_dict_key(self, value): + return dbus_message_iter_append_dict_key(self.iter, value) + + # FIXME: append_array, append_dict_array, append_boolean_array, append_int32_array, append_uint32_array, append_double_array + + def append_byte_array(self, list): + cdef unsigned char * value + cdef int length + length = len(list) + value = malloc(length) + for i from 0 <= i < length: + item = list[i] + if type(item) != str or len(item) != 1: + raise TypeError + value[i] = ord(item) + return dbus_message_iter_append_byte_array(self.iter, value, length) + + def append_string_array(self, list): + cdef char **value + cdef int length + length = len(list) + value = malloc(length) + for i from 0 <= i < length: + item = list[i] + if type(item) != str: + raise TypeError + value[i] = item + return dbus_message_iter_append_string_array(self.iter, value, length) + + +(MESSAGE_TYPE_INVALID, MESSAGE_TYPE_METHOD_CALL, MESSAGE_TYPE_METHOD_RETURN, MESSAGE_TYPE_ERROR, MESSAGE_TYPE_SIGNAL) = range(5) +(TYPE_INVALID, TYPE_NIL, TYPE_BYTE, TYPE_BOOLEAN, TYPE_INT32, TYPE_UINT32, TYPE_INT64, TYPE_UINT64, TYPE_DOUBLE, TYPE_STRING, TYPE_NAMED, TYPE_ARRAY, TYPE_DICT, TYPE_OBJECT_PATH) = range(14) + +cdef class Message: + cdef DBusMessage *msg + + def __init__(self, message_type=MESSAGE_TYPE_INVALID, service=None, path=None, interface=None, + method=None, name=None, reply_to=None, error_name=None, error_message=None, + _create=1): + cdef char *cservice + if (service == None): + cservice = NULL + else: + cservice = service + + if not _create: + return 0 + + if message_type == MESSAGE_TYPE_METHOD_CALL: + self.msg = dbus_message_new_method_call(cservice, path, interface, method) + elif message_type == MESSAGE_TYPE_METHOD_RETURN: + msg = method_call._get_msg() + self.msg = dbus_message_new_method_return(msg) + elif message_type == MESSAGE_TYPE_SIGNAL: + self.msg = dbus_message_new_signal(path, interface, name) + elif message_type == MESSAGE_TYPE_ERROR: + msg = reply_to._get_msg() + self.msg = dbus_message_new_error(msg, error_name, error_message) + + def type_to_name(self, type): + if type == MESSAGE_TYPE_SIGNAL: + return "signal" + elif type == MESSAGE_TYPE_METHOD_CALL: + return "method call" + elif type == MESSAGE_TYPE_METHOD_RETURN: + return "method return" + elif type == MESSAGE_TYPE_ERROR: + return "error" + else: + return "(unknown message type)" + + def __str__(self): + message_type = self.get_type() + sender = self.get_sender() + + if sender == None: + sender = "(no sender)" + + if (message_type == MESSAGE_TYPE_METHOD_CALL) or (message_type == MESSAGE_TYPE_SIGNAL): + retval = '%s interface=%s; member=%s; sender=%s' % (self.type_to_name(message_type), + self.get_interface(), + self.get_member(), + sender) + elif message_type == MESSAGE_TYPE_METHOD_RETURN: + retval = '%s sender=%s' % (self.type_to_name(message_type), + sender) + elif message_type == MESSAGE_TYPE_ERROR: + retval = '%s name=%s; sender=%s' % (self.type_to_name(message_type), + self.get_error_name(), + sender) + else: + retval = "Message of unknown type %d" % (message_type) + + + # FIXME: should really use self.convert_to_tuple() here + + iter = self.get_iter() + value_at_iter = True + + while (value_at_iter): + type = iter.get_arg_type() + + if type == TYPE_INVALID: + break + elif type == TYPE_STRING: + str = iter.get_string() + arg = 'string:%s\n' % (str) + elif type == TYPE_INT32: + num = iter.get_int32() + arg = 'int32:%d\n' % (num) + elif type == TYPE_UINT32: + num = iter.get_uint32() + arg = 'uint32:%u\n' % (num) + elif type == TYPE_DOUBLE: + num = iter.get_double() + arg = 'double:%f\n' % (num) + elif type == TYPE_BYTE: + num = iter.get_byte() + arg = 'byte:%d\n' % (num) + elif type == TYPE_BOOLEAN: + bool = iter.get_boolean() + if (bool): + str = "true" + else: + str = "false" + arg = 'boolean:%s\n' % (str) + else: + arg = '(unknown arg type %d)\n' % type + + retval = retval + arg + value_at_iter = iter.next() + + return retval + + def _set_msg(self, msg): + self.msg = msg + + def _get_msg(self): + return self.msg + + def get_iter(self): + return MessageIter(self) + + def get_args_list(self): + retval = [ ] + + iter = self.get_iter() + try: + retval.append(iter.get()) + except TypeError, e: + return [ ] + + value_at_iter = iter.next() + while (value_at_iter): + retval.append(iter.get()) + value_at_iter = iter.next() + + return retval + + # FIXME: implement dbus_message_copy? + + def get_type(self): + return dbus_message_get_type(self.msg) + + def set_path(self, object_path): + return dbus_message_set_path(self.msg, object_path) + + def get_path(self): + return dbus_message_get_path(self.msg) + + def set_interface(self, interface): + return dbus_message_set_interface(self.msg, interface) + + def get_interface(self): + return dbus_message_get_interface(self.msg) + + def set_member(self, member): + return dbus_message_set_member(self.msg, member) + + def get_member(self): + return dbus_message_get_member(self.msg) + + def set_error_name(self, name): + return dbus_message_set_error_name(self.msg, name) + + def get_error_name(self): + return dbus_message_get_error_name(self.msg) + + def set_destination(self, destination): + return dbus_message_set_destination(self.msg, destination) + + def get_destination(self): + return dbus_message_get_destination(self.msg) + + def set_sender(self, sender): + return dbus_message_set_sender(self.msg, sender) + + def get_sender(self): + cdef char *sender + sender = dbus_message_get_sender(self.msg) + if (sender == NULL): + return None + else: + return sender + + def set_no_reply(self, no_reply): + dbus_message_set_no_reply(self.msg, no_reply) + + def get_no_reply(self): + return dbus_message_get_no_reply(self.msg) + + def is_method_call(self, interface, method): + return dbus_message_is_method_call(self.msg, interface, method) + + def is_signal(self, interface, signal_name): + return dbus_message_is_signal(self.msg, interface, signal_name) + + def is_error(self, error_name): + return dbus_message_is_error(self.msg, error_name) + + def has_destination(self, service): + return dbus_message_has_destination(self.msg, service) + + def has_sender(self, service): + return dbus_message_has_sender(self.msg, service) + + def get_serial(self): + return dbus_message_get_serial(self.msg) + + def set_reply_serial(self, reply_serial): + return dbus_message_set_reply_serial(self.msg, reply_serial) + + def get_reply_serial(self): + return dbus_message_get_reply_serial(self.msg) + + #FIXME: dbus_message_get_path_decomposed + + # FIXME: all the different dbus_message_*args* methods + +class Signal(Message): + def __init__(self, spath, sinterface, sname): + Message.__init__(self, MESSAGE_TYPE_SIGNAL, path=spath, interface=sinterface, name=sname) + +class MethodCall(Message): + def __init__(self, mpath, minterface, mmethod): + Message.__init__(self, MESSAGE_TYPE_METHOD_CALL, path=mpath, interface=minterface, method=mmethod) + +cdef class Server: + cdef DBusServer *server + def __init__(self, address): + cdef DBusError error + dbus_error_init(&error) + self.server = dbus_server_listen(address, + &error) + if dbus_error_is_set(&error): + raise DBusException, error.message + + def setup_with_g_main (self): + dbus_server_setup_with_g_main(self.server, NULL) + + def disconnect(self): + dbus_server_disconnect(self.server) + + def get_is_connected(self): + return dbus_server_get_is_connected(self.server) + +# def set_new_connection_function(self, function, data): +# dbus_server_set_new_connection_function(self.conn, function, +# data, NULL) + +# def set_watch_functions(self, add_function, remove_function, data): +# dbus_server_set_watch_functions(self.server, +# add_function, remove_function, +# data, NULL) + +# def set_timeout_functions(self, add_function, remove_function, data): +# dbus_server_set_timeout_functions(self.server, +# add_function, remove_function, +# data, NULL) + +# def handle_watch(self, watch, condition): +# dbus_server_handle_watch(self.conn, watch, condition) + +BUS_SESSION = DBUS_BUS_SESSION +BUS_SYSTEM = DBUS_BUS_SYSTEM +BUS_ACTIVATION = DBUS_BUS_ACTIVATION + +def bus_get (bus_type): + cdef DBusError error + dbus_error_init(&error) + cdef DBusConnection *connection + + connection = dbus_bus_get(bus_type, + &error) + + if dbus_error_is_set(&error): + raise DBusException, error.message + + return Connection(_conn=connection) + +def bus_get_base_service(connection): + conn = connection._get_conn() + return dbus_bus_get_base_service(conn) + +def bus_register(connection): + cdef DBusError error + dbus_error_init(&error) + cdef dbus_bool_t retval + + conn = connection._get_conn() + retval = dbus_bus_register(conn, + &error) + if dbus_error_is_set(&error): + raise DBusException, error.message + + return retval + +SERVICE_FLAG_PROHIBIT_REPLACEMENT = 0x1 +SERVICE_FLAG_REPLACE_EXISTING = 0x2 + +def bus_acquire_service(connection, service_name, flags=0): + cdef DBusError error + dbus_error_init(&error) + cdef int retval + + conn = connection._get_conn() + retval = dbus_bus_acquire_service(conn, + service_name, + flags, + &error) + if dbus_error_is_set(&error): + raise DBusException, error.message + return retval + +def bus_service_exists(connection, service_name): + cdef DBusError error + dbus_error_init(&error) + cdef dbus_bool_t retval + + conn = connection._get_conn() + retval = dbus_bus_service_exists(conn, + service_name, + &error) + if dbus_error_is_set(&error): + raise DBusException, error.message + return retval + + diff --git a/python/dbus_h_wrapper.h b/python/dbus_h_wrapper.h new file mode 100644 index 00000000..2e218c8c --- /dev/null +++ b/python/dbus_h_wrapper.h @@ -0,0 +1,3 @@ +#define DBUS_API_SUBJECT_TO_CHANGE 1 +#include + diff --git a/python/extract.py b/python/extract.py new file mode 100644 index 00000000..460af5ab --- /dev/null +++ b/python/extract.py @@ -0,0 +1,237 @@ +import commands +import glob +import re +import os +import string +import sys + +def clean_func(buf): + buf = strip_comments(buf) + pat = re.compile(r"""\\\n""", re.MULTILINE) + buf = pat.sub('',buf) + pat = re.compile(r"""^[#].*?$""", re.MULTILINE) + buf = pat.sub('',buf) + pat = re.compile(r"""^(typedef|struct|enum)(\s|.|\n)*?;\s*""", re.MULTILINE) + buf = pat.sub('',buf) + pat = re.compile(r"""\s+""", re.MULTILINE) + buf = pat.sub(' ',buf) + pat = re.compile(r""";\s*""", re.MULTILINE) + buf = pat.sub('\n',buf) + buf = buf.lstrip() + #pat=re.compile(r'\s+([*|&]+)\s*(\w+)') + pat = re.compile(r' \s+ ([*|&]+) \s* (\w+)',re.VERBOSE) + buf = pat.sub(r'\1 \2', buf) + pat = re.compile(r'\s+ (\w+) \[ \s* \]',re.VERBOSE) + buf = pat.sub(r'[] \1', buf) +# buf = string.replace(buf, 'G_CONST_RETURN ', 'const-') + buf = string.replace(buf, 'const ', '') + return buf + +def strip_comments(buf): + parts = [] + lastpos = 0 + while 1: + pos = string.find(buf, '/*', lastpos) + if pos >= 0: + parts.append(buf[lastpos:pos]) + pos = string.find(buf, '*/', pos) + if pos >= 0: + lastpos = pos + 2 + else: + break + else: + parts.append(buf[lastpos:]) + break + return string.join(parts, '') + +def find_enums(buf): + enums = [] + buf = strip_comments(buf) + buf = re.sub('\n', ' ', buf) + + enum_pat = re.compile(r'enum\s*{([^}]*)}\s*([A-Z][A-Za-z]*)(\s|;)') + splitter = re.compile(r'\s*,\s', re.MULTILINE) + pos = 0 + while pos < len(buf): + m = enum_pat.search(buf, pos) + if not m: break + + name = m.group(2) + vals = m.group(1) + isflags = string.find(vals, '<<') >= 0 + entries = [] + for val in splitter.split(vals): + if not string.strip(val): continue + entries.append(string.split(val)[0]) + enums.append((name, isflags, entries)) + + pos = m.end() + return enums + +#typedef unsigned int dbus_bool_t; +#typedef struct { +# +# } +#typedef struct FooStruct FooStruct; +# typedef void (* DBusAddWatchFunction) (DBusWatch *watch, +# void *data); + +def find_typedefs(buf): + typedefs = [] + buf = re.sub('\n', ' ', strip_comments(buf)) + typedef_pat = re.compile( + r"""typedef\s*(?P\w*) + \s* + ([(]\s*\*\s*(?P[\w* ]*)[)]|{([^}]*)}|) + \s* + (?P[(](?P[\s\w*,_]*)[)]|[\w ]*)""", + re.MULTILINE | re.VERBOSE) + pat = re.compile(r"""\s+""", re.MULTILINE) + pos = 0 + while pos < len(buf): + m = typedef_pat.search(buf, pos) + if not m: + break + if m.group('type') == 'enum': + pos = m.end() + continue + if m.group('args2') != None: + args = pat.sub(' ', m.group('args2')) + + current = '%s (* %s) (%s)' % (m.group('type'), + m.group('callback'), + args) + else: + current = '%s %s' % (m.group('type'), m.group('args1')) + typedefs.append(current) + pos = m.end() + return typedefs + +proto_pat = re.compile(r""" +(?P(-|\w|\&|\*)+\s*) # return type +\s+ # skip whitespace +(?P\w+)\s*[(] # match the function name until the opening ( +(?P.*?)[)] # group the function arguments +""", re.IGNORECASE|re.VERBOSE) +arg_split_pat = re.compile("\s*,\s*") + + +def find_functions(buf): + functions = [] + buf = clean_func(buf) + buf = string.split(buf,'\n') + for p in buf: + if len(p) == 0: + continue + + m = proto_pat.match(p) + if m == None: + continue + + func = m.group('func') + ret = m.group('ret') + args = m.group('args') + args = arg_split_pat.split(args) +# for i in range(len(args)): +# spaces = string.count(args[i], ' ') +# if spaces > 1: +# args[i] = string.replace(args[i], ' ', '-', spaces - 1) + + functions.append((func, ret, args)) + return functions + +class Writer: + def __init__(self, filename, enums, typedefs, functions): + if not (enums or typedefs or functions): + return + print 'cdef extern from "%s":' % filename + + self.output_enums(enums) + self.output_typedefs(typedefs) + self.output_functions(functions) + + print ' pass' + print + + def output_enums(self, enums): + for enum in enums: + print ' ctypedef enum %s:' % enum[0] + if enum[1] == 0: + for item in enum[2]: + print ' %s' % item + else: + i = 0 + for item in enum[2]: + print ' %s' % item +# print ' %s = 1 << %d' % (item, i) + i += 1 + print + def output_typedefs(self, typedefs): + for typedef in typedefs: + if typedef.find('va_list') != -1: + continue + + parts = typedef.split() + if parts[0] == 'struct': + if parts[-2] == parts[-1]: + parts = parts[:-1] + print ' ctypedef %s' % ' '.join(parts) + else: + print ' ctypedef %s' % typedef + + def output_functions(self, functions): + for func, ret, args in functions: + if func[0] == '_': + continue + + str = ', '.join(args) + if str.find('...') != -1: + continue + if str.find('va_list') != -1: + continue + if str.strip() == 'void': + continue + print ' %-20s %s (%s)' % (ret, func, str) + +def do_buffer(name, buffer): + functions = find_functions(buffer) + typedefs = find_typedefs(buffer) + enums = find_enums(buffer) + + Writer(name, enums, typedefs, functions) + +def do_header(filename, name=None): + if name == None: + name = filename + + buffer = "" + for line in open(filename).readlines(): + if line[0] == '#': + continue + buffer += line + + print '# -- %s -- ' % filename + do_buffer(name, buffer) + +filename = sys.argv[1] + +if filename.endswith('.h'): + do_header(filename) + raise SystemExit + +cppflags = "" + +for flag in sys.argv[2:]: + cppflags = cppflags + " " + flag + +fd = open(filename) + +for line in fd.readlines(): + if line.startswith('#include'): + filename = line.split(' ')[1][1:-2] + command = "echo '%s'|cpp %s" % (line, cppflags) + sys.stderr.write('running %s' % (command)) + output = commands.getoutput(command) + do_buffer(filename, output) + else: + print line[:-1] diff --git a/test/test-service.c b/test/test-service.c index f22b1753..d07575e2 100644 --- a/test/test-service.c +++ b/test/test-service.c @@ -106,7 +106,7 @@ main (int argc, int result; dbus_error_init (&error); - connection = dbus_bus_get (DBUS_BUS_ACTIVATION, &error); + connection = dbus_bus_get (DBUS_BUS_SESSION, &error); if (connection == NULL) { _dbus_verbose ("*** Failed to open connection to activating message bus: %s\n", @@ -126,10 +126,13 @@ main (int argc, filter_func, NULL, NULL)) die ("No memory"); + printf ("Acquiring service\n"); + result = dbus_bus_acquire_service (connection, "org.freedesktop.DBus.TestSuiteEchoService", 0, &error); if (dbus_error_is_set (&error)) { + printf ("Error %s", error.message); _dbus_verbose ("*** Failed to acquire service: %s\n", error.message); dbus_error_free (&error); -- cgit v1.2.1