* python/dbus.py (class Sender): added to support dbus signals better
authorJohn (J5) Palmieri <johnp@redhat.com>
Fri, 11 Feb 2005 19:51:18 +0000 (19:51 +0000)
committerJohn (J5) Palmieri <johnp@redhat.com>
Fri, 11 Feb 2005 19:51:18 +0000 (19:51 +0000)
(Bus::add_signal_receiver): added expand_args parameter which defaults
to True.  When expand args is True the signal handler will pass the
message arguments as parameters to the signal handler.  If False
revert to previous behavior where the signal handler must get the
argument list from the message.  This is to help port applications
like HAL that have a tendancy to send variable length argument lists.
self._match_rule_to_receivers is now a dict of dicts.
(Bus::remove_signal_receiver): pop handler off the dict intead of
removing it from a list
(Bus::_signal_func): change signal handlers so that interface,
signal_name, service, path and message are packed into a Sender
object and that is passed to the handler.  If expand_args is True
extract the args list from the message and append it to the parameter
list

* python/dbus_bindings.pyx.in (class Signature): added to support
signiature types
(MessageIter::__init__): changed iteration limit to match D-BUS
(MessageIter::get*): added INT16, UINT16, SIGNATURE, DICT_ENTRY,
STRUCT and VARIENT type support
(MessageIter::python_value_to_dbus_sig): made recursive to support
recursive types
(MessageIter::append*): added Signature, dict, tuple
support

* python/examples/example-client.py: added examples of getting tuples
and dicts

* python/examples/example-service.py: added examples of sending tuples
and dicts

* python/examples/example-signal-recipient.py: Fixed to handle new
signal callback format

ChangeLog
python/dbus.py
python/dbus_bindings.pyx.in
python/examples/example-client.py
python/examples/example-service.py
python/examples/example-signal-recipient.py

index 085e542..1789aac 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,40 @@
+2005-02-11  John (J5) Palmieri  <johnp@redhat.com>
+
+       * python/dbus.py (class Sender): added to support dbus signals better
+       (Bus::add_signal_receiver): added expand_args parameter which defaults
+       to True.  When expand args is True the signal handler will pass the 
+       message arguments as parameters to the signal handler.  If False
+       revert to previous behavior where the signal handler must get the
+       argument list from the message.  This is to help port applications
+       like HAL that have a tendancy to send variable length argument lists.
+       self._match_rule_to_receivers is now a dict of dicts.
+       (Bus::remove_signal_receiver): pop handler off the dict intead of 
+       removing it from a list
+       (Bus::_signal_func): change signal handlers so that interface,
+       signal_name, service, path and message are packed into a Sender
+       object and that is passed to the handler.  If expand_args is True
+       extract the args list from the message and append it to the parameter
+       list
+       
+       * python/dbus_bindings.pyx.in (class Signature): added to support 
+       signiature types
+       (MessageIter::__init__): changed iteration limit to match D-BUS
+       (MessageIter::get*): added INT16, UINT16, SIGNATURE, DICT_ENTRY, 
+       STRUCT and VARIENT type support
+       (MessageIter::python_value_to_dbus_sig): made recursive to support
+       recursive types
+       (MessageIter::append*): added Signature, dict, tuple 
+       support
+
+       * python/examples/example-client.py: added examples of getting tuples
+       and dicts
+
+       * python/examples/example-service.py: added examples of sending tuples
+       and dicts
+
+       * python/examples/example-signal-recipient.py: Fixed to handle new
+       signal callback format
+
 2005-02-10  Havoc Pennington  <hp@redhat.com>
 
        * test/glib/test-dbus-glib.c (main): fix so this test doesn't fail
index e80b0ce..72c0979 100644 (file)
@@ -49,7 +49,15 @@ def init_gthreads ():
     if not _threads_initialized:
         dbus_bindings.init_gthreads ()
         _threads_initialized = 1
-    
+
+class Sender:
+    def __init__(self, interface, signal_name, service, path, message):
+        self.interface = interface
+        self.signal_name = signal_name 
+        self.service = service
+        self.path = path
+        self.message = message
+
 class Bus:
     """A connection to a DBus daemon.
 
@@ -83,13 +91,14 @@ class Bus:
         """
         return RemoteService(self, service_name)
 
-    def add_signal_receiver(self, handler_function, signal_name=None, interface=None, service=None, path=None):
+    def add_signal_receiver(self, handler_function, signal_name=None, interface=None, service=None, path=None, expand_args=True):
         match_rule = self._get_match_rule(signal_name, 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(handler_function)
-        
+            self._match_rule_to_receivers[match_rule] = {handler_function: True}
+
+        self._match_rule_to_receivers[match_rule][handler_function] = expand_args
+
         dbus_bindings.bus_add_match(self._connection, match_rule)
 
     def remove_signal_receiver(self, handler_function, signal_name=None, interface=None, service=None, path=None):
@@ -97,7 +106,7 @@ class Bus:
 
         if self._match_rule_to_receivers.has_key(match_rule):
             if self._match_rule_to_receivers[match_rule].__contains__(handler_function):
-                self._match_rule_to_receivers[match_rule].remove(handler_function)
+                self._match_rule_to_receivers[match_rule].pop(handler_function)
                 dbus_bindings.bus_remove_match(self._connection, match_rule)
 
     def get_connection(self):
@@ -130,18 +139,25 @@ class Bus:
         if (message.get_type() != dbus_bindings.MESSAGE_TYPE_SIGNAL):
             return dbus_bindings.HANDLER_RESULT_NOT_YET_HANDLED
         
-        interface = message.get_interface()
-        service   = message.get_sender()
-        path      = message.get_path()
-        member    = message.get_member()
+        interface      = message.get_interface()
+        service        = message.get_sender()
+        path           = message.get_path()
+        signal_name    = message.get_member()
 
-        match_rule = self._get_match_rule(member, interface, service, path)
+        match_rule = self._get_match_rule(signal_name, 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, message]
-            for receiver in receivers:
-                receiver(*args)
+
+            sender = Sender(interface, signal_name, service, path, message)
+            arg = [sender]
+            for receiver in receivers.iterkeys():
+               if receivers[receiver]:
+                    args = [sender]
+                   args.extend(message.get_args_list())
+                   receiver(*args)
+               else:
+                    receiver(*arg)
 
     def start_service_by_name(self, service):
         return dbus_bindings.bus_start_service_by_name(self._connection, service)
index 2da70b3..a1c4e69 100644 (file)
@@ -76,6 +76,9 @@ class ByteArray(str):
     def __init__(self, value):
         str.__init__(value)
 
+class Signature(str):
+    def __init__(self, value):
+        str.__init__(value)
 
 #forward delcerations
 cdef class Connection
@@ -431,9 +434,7 @@ cdef class MessageIter:
     def __init__(self, level=0):
         self.iter = &self.real_iter
         self.level = level
-        #don't allow us to recurse forever
-        #FIXME: what is a sane limit?
-        if(self.level > 100):
+        if(self.level > 32):
             raise TypeError, 'Type recurion is too deep' 
 
     cdef __cinit__(self, DBusMessageIter *iter):
@@ -456,6 +457,10 @@ cdef class MessageIter:
             raise TypeError, 'Invalid arg type in MessageIter'
         elif arg_type == TYPE_STRING:
             retval = self.get_string()
+        elif arg_type == TYPE_INT16:
+            retval = self.get_int16()
+        elif arg_type == TYPE_UINT16:
+            retval = self.get_uint16()
         elif arg_type == TYPE_INT32:
             retval = self.get_int32()
         elif arg_type == TYPE_UINT32:
@@ -471,50 +476,26 @@ cdef class MessageIter:
         elif arg_type == TYPE_BOOLEAN:
             retval = self.get_boolean()
         elif arg_type == TYPE_SIGNATURE:
-            raise TypeError, 'Signitures not implemented yet!'
-
+            retval = self.get_signature()
         elif arg_type == TYPE_ARRAY:
             array_type = self.get_element_type()
-            retval = self.get_array(array_type)
-        #elif arg_type == TYPE_DICT:
-        #    retval = self.get_dict()
-        #    TODO: Implement DICT when new type system implements them
+            if array_type == TYPE_DICT_ENTRY:
+                retval = self.get_dict()
+            else:
+                retval = self.get_array(array_type)
         elif arg_type == TYPE_OBJECT_PATH:
             retval = self.get_object_path()
         elif arg_type == TYPE_STRUCT:
-            raise TypeError, 'Structs not implemented yet!'
-            #TODO: implement structs
+            retval = self.get_struct()
         elif arg_type == TYPE_VARIANT:
-            raise TypeError, 'Varients not implemented yet!'
-            #TODO: implement variants
+            retval = self.get_variant()
+        elif arg_type == TYPE_DICT_ENTRY:
+            raise TypeError, 'Dictionary Entries can only appear as part of an array container'
         else:
             raise TypeError, 'Unknown arg type %d in MessageIter' % (arg_type)
 
         return retval
 
-#    TODO: Implement get_dict when DBUS supports dicts again
-#    def get_dict(self):
-#        cdef DBusMessageIter c_dict_iter
-#        cdef MessageIter dict_iter
-#       
-#        dbus_message_iter_recurse(self.iter, &c_dict_iter)
-#
-#        dict_iter = MessageIter()
-#        dict_iter.__cinit__(&c_dict_iter)
-#
-#        dict = {}
-#
-#        end_of_dict = False
-#
-#        while True:
-#            key = dict_iter.get_dict_key()
-#            value = dict_iter.get()
-#            dict[key] = value
-#            if not dict_iter.has_next():
-#                break
-#            dict_iter.next()
-#
-#        return dict
     def get_arg_type(self):
         return dbus_message_iter_get_arg_type(self.iter)
 
@@ -530,7 +511,21 @@ cdef class MessageIter:
         cdef dbus_bool_t c_val
         dbus_message_iter_get_basic(self.iter, <dbus_bool_t *>&c_val)
         return c_val
-        
+
+    def get_signature(self):
+        signature_string = self.get_string()
+        return Signature(signature_string)
+
+    def get_int16(self):
+        cdef dbus_int16_t c_val
+        dbus_message_iter_get_basic(self.iter, <dbus_int16_t *>&c_val)
+        return c_val
+
+    def get_uint16(self):
+        cdef dbus_uint16_t c_val
+        dbus_message_iter_get_basic(self.iter, <dbus_uint16_t *>&c_val)
+        return c_val
+
     def get_int32(self):
         cdef dbus_int32_t c_val
         dbus_message_iter_get_basic(self.iter, <dbus_int32_t *>&c_val)
@@ -565,10 +560,32 @@ cdef class MessageIter:
         object_path_string = self.get_string()
         return ObjectPath(object_path_string)
 
-# TODO: Implement dict when DBUS supports it again
-#    def get_dict_key(self):
-#        return dbus_message_iter_get_dict_key(self.iter)
+    def get_dict(self):
+        cdef DBusMessageIter c_dict_iter
+        cdef MessageIter dict_iter
+        level = self.level + 1
+
+        dbus_message_iter_recurse(self.iter, <DBusMessageIter *>&c_dict_iter)
+        dict_iter = MessageIter(level)
+        dict_iter.__cinit__(&c_dict_iter)
+        
+        python_dict = {}
+        cur_arg_type = dict_iter.get_arg_type()
+        while cur_arg_type == TYPE_DICT_ENTRY:
+            if cur_arg_type != TYPE_DICT_ENTRY:
+                raise TypeError, "Dictionary elements must be of type TYPE_DICT_ENTRY '%s != %s'" % (TYPE_DICT_ENTRY, cur_arg_type)
+                
+            dict_entry = dict_iter.get_struct()
+            if len(dict_entry) != 2:
+                raise TypeError, "Dictionary entries must be structs of two elements.  This entry had %i elements.'" % (len(dict_entry))
+
+            python_dict[dict_entry[0]] = dict_entry[1]
+            
+            dict_iter.next()
+            cur_arg_type = dict_iter.get_arg_type()
 
+        return python_dict
+   
     def get_array(self, type):
         cdef DBusMessageIter c_array_iter
         cdef MessageIter array_iter
@@ -579,39 +596,101 @@ cdef class MessageIter:
         array_iter.__cinit__(&c_array_iter)
         
         python_list = []
-        while True:
+        cur_arg_type = array_iter.get_arg_type()
+        while cur_arg_type != TYPE_INVALID:
+            if cur_arg_type != type:
+                raise TypeError, "Array elements must be of the same type '%s != %s'" % (type, cur_arg_type)
+                
             value = array_iter.get(type)
             python_list.append(value)
-            if not array_iter.has_next():
-                break
+            
             array_iter.next()
+            cur_arg_type = array_iter.get_arg_type()
 
         return python_list
 
-    #FIXME: handle all the different types?
-    def python_value_to_dbus_sig(self, value):
+    def get_variant(self):
+        cdef DBusMessageIter c_var_iter
+        cdef MessageIter var_iter
+        level = self.level + 1
+
+        dbus_message_iter_recurse(self.iter, <DBusMessageIter *>&c_var_iter)
+        var_iter = MessageIter(level)
+        var_iter.__cinit__(&c_var_iter)
+        
+        return var_iter.get() 
+
+    def get_struct(self):
+        cdef DBusMessageIter c_struct_iter
+        cdef MessageIter struct_iter
+        level = self.level + 1
+
+        dbus_message_iter_recurse(self.iter, <DBusMessageIter *>&c_struct_iter)
+        struct_iter = MessageIter(level)
+        struct_iter.__cinit__(&c_struct_iter)
+
+        python_list = []
+        while struct_iter.get_arg_type() != TYPE_INVALID:
+            value = struct_iter.get()
+            python_list.append(value)
+            
+            struct_iter.next()
+
+        return tuple(python_list)
+
+    def python_value_to_dbus_sig(self, value, level = 0):
+
+        if(level > 32):
+            raise TypeError, 'Type recurion is too deep' 
+
+        level = level + 1
+
         ptype = type(value)
         ret = ""
         if ptype == bool:
-            ret =  TYPE_BOOL
+            ret = TYPE_BOOL
+            ret = str(chr(ret))
         elif ptype == int:
             ret = TYPE_INT32
+            ret = str(chr(ret))
         elif ptype == long:
             ret = TYPE_INT64
+            ret = str(chr(ret))
         elif ptype == str:
             ret = TYPE_STRING
+            ret = str(chr(ret))
         elif ptype == float:
             ret = TYPE_FLOAT
-#        elif ptype == dict:
-#            TODO: Implement dict when DBUS supports it again
+            ret = str(chr(ret))
+        elif ptype == dict:
+            dict_list = value.items()
+            key, value = dict_list[0]
+
+            ret = str(chr(TYPE_ARRAY)) + str(chr(DICT_ENTRY_BEGIN))
+            ret = ret + self.python_value_to_dbus_sig(key, level)
+            ret = ret + self.python_value_to_dbus_sig(value, level)
+            ret = ret + str(chr(DICT_ENTRY_END))
+
+        elif ptype == tuple:
+            ret = str(chr(STRUCT_BEGIN))
+            for item in value:
+                ret = ret + self.python_value_to_dbus_sig(item, level)
+            ret = ret + str(chr(STRUCT_END))
         elif ptype == list:
-            ret = TYPE_ARRAY
+            ret = str(chr(TYPE_ARRAY))
+            ret = ret + self.python_value_to_dbus_sig(value[0], level)
         elif isinstance(value, ObjectPath):
             ret = TYPE_PATH
+            ret = str(chr(ret))
+        elif isinstance(ByteArray):
+            ret = str(chr(TYPE_ARRAY)) + str(chr(TYPE_BYTE))
+        elif isinstance(Signature):
+            ret = TYPE_SIGNATURE
+            ret = str(chr(ret))
         else:
             raise TypeError, "Argument of unknown type '%s'" % (ptype)
 
-        return str(chr(ret))
+        return ret
 
     
     #FIXME: handle all the different types?
@@ -627,9 +706,10 @@ cdef class MessageIter:
             retval = self.append_string(value)
         elif value_type == float:
             retval = self.append_double(value)
-#        elif value_type == dict:
-#            retval = self.append_dict(value)
-#            TODO: Implement dict when DBUS supports it again
+        elif value_type == dict:
+            retval = self.append_dict(value)
+        elif value_type == tuple:
+            retval = self.append_struct(value)
         elif value_type == list:
              retval = self.append_array(value)
         #elif value_type == None.__class__:
@@ -638,6 +718,8 @@ cdef class MessageIter:
             retval = self.append_object_path(value)
         elif isinstance(value, ByteArray):
             retval = self.append_array(value)
+        elif isinstance(value, Signature):
+            retval = self.append_signature(value)
         else:
             raise TypeError, "Argument of unknown type '%s'" % (value_type)
 
@@ -685,38 +767,71 @@ cdef class MessageIter:
         cdef char *c_value
         c_value = value
         return dbus_message_iter_append_basic(self.iter, TYPE_STRING, <char **>&c_value)    
-#    TODO: Implement dict when DBUS supports it again
-#    def append_dict_key(self, value):
-#        return dbus_message_iter_append_dict_key(self.iter, value)
 
     def append_object_path(self, value):
         cdef char *c_value
         c_value = value
         return dbus_message_iter_append_basic(self.iter, TYPE_PATH, <char **>&c_value)
 
-    # FIXME: append_array, append_boolean_array, append_uint32_array, 
-    # append_uint64_array
-#TODO: Implement dict when DBUS supports it again
-#    def append_dict(self, python_dict):
-#        cdef DBusMessageIter c_dict_iter
-#        cdef MessageIter dict_iter
-#        
-#        dbus_message_iter_append_dict(self.iter, &c_dict_iter)
-#        
-#        dict_iter = MessageIter()
-#        dict_iter.__cinit__(&c_dict_iter)
-#
-#        for key, value in python_dict.iteritems():
-#            if type(key) != str:
-#                raise TypeError, "DBus dict keys must be strings"
-#            dict_iter.append_dict_key(key)
-#            dict_iter.append(value)
+    def append_signature(self, value):
+        cdef char *c_value
+        c_value = value
+        return dbus_message_iter_append_basic(self.iter, TYPE_SIGNATURE, <char **>&c_value)
+
+
+    def append_dict(self, python_dict):
+        cdef DBusMessageIter c_dict_iter, c_dict_entry_iter
+        cdef MessageIter dict_iter, dict_entry_iter
+        
+        level = self.level + 1
+
+        dict_list = python_dict.items()
+        key, value = dict_list[0]
+
+        sig = str(chr(DICT_ENTRY_BEGIN))
+        sig = sig + self.python_value_to_dbus_sig(key)
+        sig = sig + self.python_value_to_dbus_sig(value)
+        sig = sig + str(chr(DICT_ENTRY_END))
+
+        dbus_message_iter_open_container(self.iter, TYPE_ARRAY, sig, <DBusMessageIter *>&c_dict_iter)
+        dict_iter = MessageIter(level)
+        dict_iter.__cinit__(&c_dict_iter)
+
+        for key, value in dict_list:
+            dbus_message_iter_open_container(dict_iter.iter, TYPE_DICT_ENTRY, sig, <DBusMessageIter *>&c_dict_entry_iter)
+            dict_entry_iter = MessageIter(level)
+            dict_entry_iter.__cinit__(&c_dict_entry_iter)
+
+            dict_entry_iter.append(key)
+            dict_entry_iter.append(value)
+
+            dbus_message_iter_close_container(dict_iter.iter, dict_entry_iter.iter)
+
+        dbus_message_iter_close_container(self.iter, dict_iter.iter)
+
+    def append_struct(self, python_struct):
+        cdef DBusMessageIter c_struct_iter
+        cdef MessageIter struct_iter
+
+        level = self.level + 1
+        dbus_message_iter_open_container(self.iter, TYPE_STRUCT, NULL, <DBusMessageIter *>&c_struct_iter)
+        struct_iter = MessageIter(level)
+        struct_iter.__cinit__(&c_struct_iter)
+        
+        for item in python_struct:
+            if not struct_iter.append(item):
+                dbus_message_iter_close_container(self.iter, struct_iter.iter)
+                return False
+
+        dbus_message_iter_close_container(self.iter, struct_iter.iter)
+
     def append_array(self, python_list):
         cdef DBusMessageIter c_array_iter
         cdef MessageIter array_iter
 
         level = self.level + 1
         sig = self.python_value_to_dbus_sig(python_list[0])
+
         dbus_message_iter_open_container(self.iter, TYPE_ARRAY, sig, <DBusMessageIter *>&c_array_iter)
         array_iter = MessageIter(level)
         array_iter.__cinit__(&c_array_iter)
@@ -790,7 +905,7 @@ cdef class MessageIter:
 
 
 (MESSAGE_TYPE_INVALID, MESSAGE_TYPE_METHOD_CALL, MESSAGE_TYPE_METHOD_RETURN, MESSAGE_TYPE_ERROR, MESSAGE_TYPE_SIGNAL) = range(5)
-(TYPE_INVALID, TYPE_BYTE, TYPE_BOOLEAN, TYPE_INT32, TYPE_UINT32, TYPE_INT64, TYPE_UINT64, TYPE_DOUBLE, TYPE_STRING, TYPE_OBJECT_PATH, TYPE_SIGNATURE, TYPE_ARRAY, TYPE_STRUCT, TYPE_STRUCT_START, TYPE_STRUCT_END, TYPE_VARIENT) = (0, ord('y'), ord('b'), ord('i'), ord('u'), ord('x'), ord('t'), ord('d'), ord('s'), ord('o'), ord('g'), ord('a'), ord('r'), ord('('), ord(')'), ord('v'))
+(TYPE_INVALID, TYPE_BYTE, TYPE_BOOLEAN, TYPE_INT16, TYPE_UINT16, TYPE_INT32, TYPE_UINT32, TYPE_INT64, TYPE_UINT64, TYPE_DOUBLE, TYPE_STRING, TYPE_OBJECT_PATH, TYPE_SIGNATURE, TYPE_ARRAY, TYPE_STRUCT, STRUCT_BEGIN, STRUCT_END, TYPE_VARIANT, TYPE_DICT_ENTRY, DICT_ENTRY_BEGIN, DICT_ENTRY_END) = (0, ord('y'), ord('b'), ord('n'), ord('i'), ord('u'), ord('q'), ord('x'), ord('t'), ord('d'), ord('s'), ord('o'), ord('g'), ord('a'), ord('r'), ord('('), ord(')'), ord('v'), ord('e'), ord('{'), ord('}'))
 (HANDLER_RESULT_HANDLED, HANDLER_RESULT_NOT_YET_HANDLED, HANDLER_RESULT_NEED_MEMORY) = range(3)
     
 cdef class Message:
index b3b22f5..0270ed6 100644 (file)
@@ -9,4 +9,13 @@ remote_object = remote_service.get_object("/SomeObject",
 
 hello_reply_list = remote_object.HelloWorld("Hello from example-client.py!")
 
+hello_reply_tuple = remote_object.GetTuple()
+
+hello_reply_dict = remote_object.GetDict()
+
 print (hello_reply_list)
+
+print str(hello_reply_tuple)
+
+print str(hello_reply_dict)
+
index 1ea7fd8..e9f407f 100644 (file)
@@ -5,12 +5,19 @@ import gtk
 
 class SomeObject(dbus.Object):
     def __init__(self, service):
-        dbus.Object.__init__(self, "/SomeObject", service, [self.HelloWorld])
+        export = [self.HelloWorld, self.GetTuple, self.GetDict]
+        dbus.Object.__init__(self, "/SomeObject", service, export)
 
     def HelloWorld(self, message, hello_message):
         print (str(hello_message))
         return ["Hello", " from example-service.py"]
 
+    def GetTuple(self, message):
+        return ("Hello Tuple", " from example-service.py")
+
+    def GetDict(self, message):
+        return {"first": "Hello Dict", "second": " from example-service.py"}
+
 session_bus = dbus.SessionBus()
 service = dbus.Service("org.designfu.SampleService", bus=session_bus)
 object = SomeObject(service)
index 65e5933..b530607 100644 (file)
@@ -5,9 +5,9 @@ bus = dbus.SessionBus()
 service = bus.get_service("org.designfu.TestService")
 object  = service.get_object("/org/designfu/TestService/object", "org.designfu.TestService")
 
-def hello_signal_handler(interface, signal_name, service, path, message):
+def hello_signal_handler(sender):
         print ("Received signal '%s.%s' from object '%s%s'"
-               % (interface, signal_name, service, path))
+               % (sender.interface, sender.signal_name, sender.service, sender.path))
 
 
 object.connect_to_signal("hello", hello_signal_handler)