diff options
| -rw-r--r-- | python/Makefile.am | 28 | ||||
| -rw-r--r-- | python/dbus.py | 267 | ||||
| -rw-r--r-- | python/dbus_bindings.pyx.in | 917 | ||||
| -rw-r--r-- | python/dbus_h_wrapper.h | 3 | ||||
| -rw-r--r-- | python/extract.py | 237 | 
5 files changed, 1452 insertions, 0 deletions
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..c7ab5dd3 --- /dev/null +++ b/python/dbus.py @@ -0,0 +1,267 @@ + +"""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) + +        self._connection.add_filter(self._signal_func) +        self._match_rule_to_receivers = { } +        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 add_signal_receiver(self, receiver, interface=None, service=None, path=None): +        match_rule = self._get_match_rule(interface, service, path) +         +        if (not self._match_rule_to_receivers.has_key(match_rule)): +            self._match_rule_to_receivers[match_rule] = [ ] +        self._match_rule_to_receivers[match_rule].append(receiver) +         +        dbus_bindings.bus_add_match(self._connection, match_rule) +         +    def get_connection(self): +        """Get the dbus_bindings.Connection object associated with this Bus""" +        return self._connection + +    def _get_match_rule(self, interface, service, path): +##        if (interface): +##            match_rule = match_rule + ",interface='%s'" % (interface) +##        if (service): +##            match_rule = match_rule + ",service='%s'" % (service) +##        if (path): +##            match_rule = match_rule + ",path='%s'" % (path) +        # FIXME: use the service here too!!! +        return "type='signal',interface='%s',path='%s'" % (interface, path) +     +    def _signal_func(self, connection, message): +        if (message.get_type() != dbus_bindings.MESSAGE_TYPE_SIGNAL): +            return +         +        interface = message.get_interface() +        service   = message.get_sender() +        path      = message.get_path() +        member    = message.get_member() + +        match_rule = self._get_match_rule(interface, service, path) + +        if (self._match_rule_to_receivers.has_key(match_rule)): +            receivers = self._match_rule_to_receivers[match_rule] +            args = [interface, member, service, path] +            for receiver in receivers: +                receiver(*args) +         + +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): +        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: +            iter.append(arg) + +        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 + +    def get_service_name(self): +        """Get the name of this service""" +        return self._service_name + +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._bus = service.get_bus() +        self._connection = self._bus.get_connection() + +        self._method_name_to_method = self._build_method_dictionary(methods_to_share) +         +        self._connection.register_object_path(object_path, self._unregister_cb, self._message_cb) + +    def broadcast_signal(self, interface, signal_name): +        message = dbus_bindings.Signal(self._object_path, interface, signal_name) +        #FIXME: need to set_sender, but it always disconnects when we do this +        #message.set_sender(self._service.get_service_name()) +        self._connection.send(message) + +    def _unregister_cb(self, connection): +        print ("Unregister") +         +    def _message_cb(self, connection, message): +        target_method_name = message.get_member() +        target_method = self._method_name_to_method[target_method_name] +        args = message.get_args_list() +         +        try: +            retval = target_method(*args) +        except Exception, e: +            if e.__module__ == '__main__': +                # FIXME: is it right to use .__name__ here? +                error_name = e.__class__.__name__ +            else: +                error_name = e.__module__ + '.' + str(e.__class__.__name__) +            error_contents = str(e) +            reply = dbus_bindings.Error(message, error_name, error_contents) +        else: +            reply = dbus_bindings.MethodReturn(message) +            if retval != None: +                iter = reply.get_iter() +                iter.append(retval) +                 +        self._connection.send(reply) + +    def _build_method_dictionary(self, methods): +        method_dict = {} +        for method in methods: +            if method_dict.has_key(method.__name__): +                print ('WARNING: registering DBus Object methods, already have a method named %s' % (method.__name__)) +            method_dict[method.__name__] = method +        return method_dict + +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..4ae056c5 --- /dev/null +++ b/python/dbus_bindings.pyx.in @@ -0,0 +1,917 @@ +# -*- 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 *) + + +_user_data_references = [ ] + +class DBusException(Exception): +    pass + +class ConnectionError(Exception): +    pass + +cdef void cunregister_function_handler (DBusConnection *connection, +                                        void *user_data): +    tup = <object>user_data +    assert (type(tup) == list)     +    function = tup[1] +    args = [Connection(_conn=<object>connection)] +    function(*args) + +cdef DBusHandlerResult cmessage_function_handler (DBusConnection *connection, +                                                  DBusMessage *msg, +                                                  void *user_data): +    tup = <object>user_data +    assert (type(tup) == list) +    function = tup[0] +    message = Message(_create=0) +    message._set_msg(<object>msg) +    args = [Connection(_conn=<object>connection), +            message] +    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 <DBusConnection*>_conn != NULL: +            self.conn = <DBusConnection*>_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 = <DBusConnection*>conn +         +    def _get_conn(self): +        return <object>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 = <char **>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(<object>dbus_connection_borrow_message(self.conn)) +        return m +     +    def return_message(self, message): +        msg = message._get_msg() +        dbus_connection_return_message(self.conn, <DBusMessage*>msg) + +    def steal_borrowed_message(self, message): +        msg = message._get_msg() +        dbus_connection_steal_borrowed_message(self.conn, +                                               <DBusMessage*>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(<object>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, +                                      <DBusMessage*>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, +                                                 <DBusMessage*>msg, +                                                 &cpending_call, +                                                 timeout_milliseconds) + +        if dbus_error_is_set(&error): +            raise DBusException, error.message + +        if (cpending_call != NULL): +            pending_call = PendingCall(<object>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() + +        retval = dbus_connection_send_with_reply_and_block( +            <DBusConnection*>self.conn, +            <DBusMessage*>msg, +            <int>timeout_milliseconds, +            &error) + +        if dbus_error_is_set(&error): +            raise DBusException, error.message + +        if retval == NULL: +            raise AssertionError +         +        m = Message(_create=0) +        m._set_msg(<object>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, filter_function): +        user_data = [ filter_function ] +        global _user_data_references +        _user_data_references.append(user_data) +         +        return dbus_connection_add_filter(self.conn, +                                          cmessage_function_handler, +                                          <void*>user_data, +                                          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 = [message_cb, unregister_cb] +        global _user_data_references +        _user_data_references.append(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, +                                                    <void*>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 = [message_cb, unregister_cb] +        global _user_data_references +        _user_data_references.append(user_data)         +         +        path_element_list = path[1:].split('/') +        self._build_parsed_path(path_element_list) +         +        return dbus_connection_register_fallback(self.conn, self._parsed_path, &cvtable, +                                                 <void*>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 = [] + +        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 = <DBusPendingCall*>_pending_call +        dbus_pending_call_ref(self.pending_call) +         +    def _get_pending_call(self): +        return <object>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(<object>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 = <DBusWatch*>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(<DBusMessage*>msg, self.iter) +     +    def get_iter(self): +        return <object>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, <int*>&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, <int*>&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 = <unsigned char*>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 = <char**>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, +                 method_call=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 + +        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: +            cmsg = method_call._get_msg() +            self.msg = dbus_message_new_method_return(<DBusMessage*>cmsg) +        elif message_type == MESSAGE_TYPE_SIGNAL: +            self.msg = dbus_message_new_signal(path, interface, name) +        elif message_type == MESSAGE_TYPE_ERROR: +            cmsg = reply_to._get_msg() +            self.msg = dbus_message_new_error(<DBusMessage*>cmsg, 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 = <DBusMessage*>msg + +    def _get_msg(self): +        return <object>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) + +class MethodReturn(Message): +    def __init__(self, method_call): +        Message.__init__(self, MESSAGE_TYPE_METHOD_RETURN, method_call=method_call) + +class Error(Message): +    def __init__(self, reply_to, error_name, error_message): +        Message.__init__(self, MESSAGE_TYPE_ERROR, reply_to=reply_to, error_name=error_name, error_message=error_message) +         +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=<object>connection) + +def bus_get_base_service(connection): +    conn = connection._get_conn() +    return dbus_bus_get_base_service(<DBusConnection*>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(<DBusConnection*>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(<DBusConnection*>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(<DBusConnection*>conn, +                                     service_name, +                                     &error) +    if dbus_error_is_set(&error): +        raise DBusException, error.message +    return retval + +def bus_add_match(connection, rule): +    cdef DBusError error +    dbus_error_init(&error) +     +    conn = connection._get_conn() +    dbus_bus_add_match (<DBusConnection*>conn, rule, &error) +     +    if dbus_error_is_set(&error): +        raise DBusException, error.message + +def bus_remove_match(connection, rule): +    cdef DBusError error +    dbus_error_init(&error) + +    conn = connection._get_conn() +    dbus_bus_remove_match (<DBusConnection*>conn, rule, &error) +     +    if dbus_error_is_set(&error): +        raise DBusException, error.message 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 <dbus/dbus.h> + 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<type>\w*) +            \s* +            ([(]\s*\*\s*(?P<callback>[\w* ]*)[)]|{([^}]*)}|) +            \s* +            (?P<args1>[(](?P<args2>[\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<ret>(-|\w|\&|\*)+\s*)      # return type +\s+                            # skip whitespace +(?P<func>\w+)\s*[(]  # match the function name until the opening ( +(?P<args>.*?)[)]               # 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]  | 
