88from .signature import Variant
99from .proxy_object import BaseProxyObject
1010from . import introspection as intr
11- from contextlib import suppress
1211
1312import inspect
1413import socket
@@ -222,20 +221,27 @@ def _emit_interface_added(self, path, interface):
222221 if self ._disconnected :
223222 return
224223
225- body = {interface .name : {}}
226- properties = interface ._get_properties (interface )
224+ def get_properties_callback (interface , result , user_data , e ):
225+ if e is not None :
226+ try :
227+ raise e
228+ except Exception :
229+ logging .error (
230+ 'An exception ocurred when emitting ObjectManager.InterfacesAdded for %s. '
231+ 'Some properties will not be included in the signal.' ,
232+ interface .name ,
233+ exc_info = True )
227234
228- for prop in properties :
229- with suppress (Exception ):
230- body [interface .name ][prop .name ] = Variant (prop .signature ,
231- prop .prop_getter (interface ))
235+ body = {interface .name : result }
232236
233- self .send (
234- Message .new_signal (path = path ,
235- interface = 'org.freedesktop.DBus.ObjectManager' ,
236- member = 'InterfacesAdded' ,
237- signature = 'oa{sa{sv}}' ,
238- body = [path , body ]))
237+ self .send (
238+ Message .new_signal (path = path ,
239+ interface = 'org.freedesktop.DBus.ObjectManager' ,
240+ member = 'InterfacesAdded' ,
241+ signature = 'oa{sa{sv}}' ,
242+ body = [path , body ]))
243+
244+ ServiceInterface ._get_all_property_values (interface , get_properties_callback )
239245
240246 def _emit_interface_removed (self , path , removed_interfaces ):
241247 """Emit the ``org.freedesktop.DBus.ObjectManager.InterfacesRemoved` signal.
@@ -630,7 +636,7 @@ def __call__(self, reply):
630636
631637 bus .send (reply )
632638
633- def __exit__ (self , exc_type , exc_value , tb ):
639+ def _exit (self , exc_type , exc_value , tb ):
634640 if exc_type is None :
635641 return
636642
@@ -646,6 +652,12 @@ def __exit__(self, exc_type, exc_value, tb):
646652 ))
647653 return True
648654
655+ def __exit__ (self , exc_type , exc_value , tb ):
656+ self ._exit (exc_type , exc_value , tb )
657+
658+ def send_error (self , exc ):
659+ self ._exit (exc .__class__ , exc , exc .__traceback__ )
660+
649661 return SendReply ()
650662
651663 def _process_message (self , msg ):
@@ -713,8 +725,7 @@ def _process_message(self, msg):
713725 handler (msg , None )
714726 del self ._method_return_handlers [msg .reply_serial ]
715727
716- @classmethod
717- def _make_method_handler (cls , interface , method ):
728+ def _make_method_handler (self , interface , method ):
718729 def handler (msg , send_reply ):
719730 args = ServiceInterface ._msg_body_to_args (msg )
720731 result = method .fn (interface , * args )
@@ -792,16 +803,51 @@ def reply_handler(reply, err):
792803
793804 def _default_get_managed_objects_handler (self , msg , send_reply ):
794805 result = {}
795-
796- for node in self ._path_exports :
797- if not node .startswith (msg .path + '/' ) and msg .path != '/' :
798- continue
799-
806+ result_signature = 'a{oa{sa{sv}}}'
807+ error_handled = False
808+
809+ def is_result_complete ():
810+ if not result :
811+ return True
812+ for n , interfaces in result .items ():
813+ for value in interfaces .values ():
814+ if value is None :
815+ return False
816+
817+ return True
818+
819+ nodes = [
820+ node for node in self ._path_exports
821+ if msg .path == '/' or node .startswith (msg .path + '/' )
822+ ]
823+
824+ # first build up the result object to know when it's complete
825+ for node in nodes :
800826 result [node ] = {}
801827 for interface in self ._path_exports [node ]:
802- result [node ][interface .name ] = self ._get_all_properties (interface )
828+ result [node ][interface .name ] = None
829+
830+ if is_result_complete ():
831+ send_reply (Message .new_method_return (msg , result_signature , [result ]))
832+ return
833+
834+ def get_all_properties_callback (interface , values , node , err ):
835+ nonlocal error_handled
836+ if err is not None :
837+ if not error_handled :
838+ error_handled = True
839+ send_reply .send_error (err )
840+ return
803841
804- send_reply (Message .new_method_return (msg , 'a{oa{sa{sv}}}' , [result ]))
842+ result [node ][interface .name ] = values
843+
844+ if is_result_complete ():
845+ send_reply (Message .new_method_return (msg , result_signature , [result ]))
846+
847+ for node in nodes :
848+ for interface in self ._path_exports [node ]:
849+ ServiceInterface ._get_all_property_values (interface , get_all_properties_callback ,
850+ node )
805851
806852 def _default_properties_handler (self , msg , send_reply ):
807853 methods = {'Get' : 'ss' , 'Set' : 'ssv' , 'GetAll' : 's' }
@@ -858,14 +904,24 @@ def _default_properties_handler(self, msg, send_reply):
858904 if not prop .access .readable ():
859905 raise DBusError (ErrorType .UNKNOWN_PROPERTY ,
860906 'the property does not have read access' )
861- prop_value = getattr (interface , prop .prop_getter .__name__ )
862907
863- body , unix_fds = replace_fds_with_idx (prop .signature , [prop_value ])
908+ def get_property_callback (interface , prop , prop_value , err ):
909+ try :
910+ if err is not None :
911+ send_reply .send_error (err )
912+ return
913+
914+ body , unix_fds = replace_fds_with_idx (prop .signature , [prop_value ])
915+
916+ send_reply (
917+ Message .new_method_return (msg ,
918+ 'v' , [Variant (prop .signature , body [0 ])],
919+ unix_fds = unix_fds ))
920+ except Exception as e :
921+ send_reply .send_error (e )
922+
923+ ServiceInterface ._get_property_value (interface , prop , get_property_callback )
864924
865- send_reply (
866- Message .new_method_return (msg ,
867- 'v' , [Variant (prop .signature , body [0 ])],
868- unix_fds = unix_fds ))
869925 elif msg .member == 'Set' :
870926 if not prop .access .writable ():
871927 raise DBusError (ErrorType .PROPERTY_READ_ONLY , 'the property is readonly' )
@@ -874,27 +930,30 @@ def _default_properties_handler(self, msg, send_reply):
874930 raise DBusError (ErrorType .INVALID_SIGNATURE ,
875931 f'wrong signature for property. expected "{ prop .signature } "' )
876932 assert prop .prop_setter
933+
934+ def set_property_callback (interface , prop , err ):
935+ if err is not None :
936+ send_reply .send_error (err )
937+ return
938+ send_reply (Message .new_method_return (msg ))
939+
877940 body = replace_idx_with_fds (value .signature , [value .value ], msg .unix_fds )
878- setattr (interface , prop . prop_setter . __name__ , body [0 ])
879- send_reply ( Message . new_method_return ( msg ) )
941+ ServiceInterface . _set_property_value (interface , prop , body [0 ],
942+ set_property_callback )
880943
881944 elif msg .member == 'GetAll' :
882- body , unix_fds = replace_fds_with_idx ('a{sv}' , [self ._get_all_properties (interface )])
883- send_reply (Message .new_method_return (msg , 'a{sv}' , body , unix_fds = unix_fds ))
884945
885- else :
886- assert False
887-
888- def _get_all_properties (self , interface ):
889- result = {}
946+ def get_all_properties_callback (interface , values , user_data , err ):
947+ if err is not None :
948+ send_reply .send_error (err )
949+ return
950+ body , unix_fds = replace_fds_with_idx ('a{sv}' , [values ])
951+ send_reply (Message .new_method_return (msg , 'a{sv}' , body , unix_fds = unix_fds ))
890952
891- for prop in ServiceInterface ._get_properties (interface ):
892- if prop .disabled or not prop .access .readable ():
893- continue
894- result [prop .name ] = Variant (prop .signature , getattr (interface ,
895- prop .prop_getter .__name__ ))
953+ ServiceInterface ._get_all_property_values (interface , get_all_properties_callback )
896954
897- return result
955+ else :
956+ assert False
898957
899958 def _init_high_level_client (self ):
900959 '''The high level client is initialized when the first proxy object is
0 commit comments