mirror of
https://gitlab.freedesktop.org/dbus/dbus.git
synced 2025-12-26 05:10:08 +01:00
instead of marshal basic so we can handle recursive types in a variant * test/glib/test-dbus-glib.c: Add test for marshaling recurive types in variants * test/glib/test-service-glib.c, test-service-glib.xml (my_object_echo_variant [EchoVariant], my_object_process_variant_of_array_of_ints123 [ProcessVariantOfArrayOfInts123]): Add two test methods * python/introspect_parser.py: New module for parsing introspect data. * python/dbus_bindings.pyx: (various places): when throwing errors fix to use errormsg instead of message local variable because Pyrex can get confused with other message variables (initial patch by Robert McQueen <robert.mcqueen at collabora.co.uk>) (MessageIter::parse_signature_block): new method for getting the next block in a signiture. (MessageIter::append_strict): new method for appending values strictly using the passed in signature instead of guessing at the type (MessageItter:: append_dict, append_struct, append_array): use signatures to marshal children if the signature is available * python/exceptions.py (IntrospectionParserException): new exception * python/proxies.py (ProxyMethod::__call__): Marshal args with introspected signatures if available, else we fall back to the old way of doing things. (ProxyObject::_introspect_reply_handler ): parse introspection data * python/service.py (ObjectType::_reflect_on_method): Properly terminate <method> if there are no args in the reflection data * test/python/test-client.py: add tests for talking with the GLib test server. This gives us better coverage for introspection since python to python will always generate arguments as variants. It also allows us to test the robustness of the GLib bindings and interlanguage communications.
223 lines
8.3 KiB
Python
223 lines
8.3 KiB
Python
import dbus_bindings
|
|
import introspect_parser
|
|
import sys
|
|
from exceptions import MissingReplyHandlerException, MissingErrorHandlerException, IntrospectionParserException
|
|
|
|
class DeferedMethod:
|
|
"""A DeferedMethod
|
|
|
|
This is returned instead of ProxyMethod when we are defering DBus calls
|
|
while waiting for introspection data to be returned
|
|
|
|
This class can be used for debugging purposes
|
|
"""
|
|
def __call__(self, *args, **keywords):
|
|
return None
|
|
|
|
class ProxyMethod:
|
|
"""A proxy Method.
|
|
|
|
Typically a member of a ProxyObject. Calls to the
|
|
method produce messages that travel over the Bus and are routed
|
|
to a specific named Service.
|
|
"""
|
|
def __init__(self, connection, named_service, object_path, dbus_interface, method_name, introspect_sig):
|
|
self._connection = connection
|
|
self._named_service = named_service
|
|
self._object_path = object_path
|
|
self._method_name = method_name
|
|
self._dbus_interface = dbus_interface
|
|
self._introspect_sig = introspect_sig
|
|
|
|
def __call__(self, *args, **keywords):
|
|
dbus_interface = self._dbus_interface
|
|
if keywords.has_key('dbus_interface'):
|
|
dbus_interface = keywords['dbus_interface']
|
|
|
|
timeout = -1
|
|
if keywords.has_key('timeout'):
|
|
timeout = keywords['timeout']
|
|
|
|
reply_handler = None
|
|
if keywords.has_key('reply_handler'):
|
|
reply_handler = keywords['reply_handler']
|
|
|
|
error_handler = None
|
|
if keywords.has_key('error_handler'):
|
|
error_handler = keywords['error_handler']
|
|
|
|
if not(reply_handler and error_handler):
|
|
if reply_handler:
|
|
raise MissingErrorHandlerException()
|
|
elif error_handler:
|
|
raise MissingReplyHandlerException()
|
|
|
|
message = dbus_bindings.MethodCall(self._object_path, dbus_interface, self._method_name)
|
|
message.set_destination(self._named_service)
|
|
|
|
# Add the arguments to the function
|
|
iter = message.get_iter(True)
|
|
|
|
remainder = self._introspect_sig
|
|
for arg in args:
|
|
if self._introspect_sig:
|
|
(sig, remainder) = iter.parse_signature_block(remainder)
|
|
iter.append_strict(arg, sig)
|
|
else:
|
|
iter.append(arg)
|
|
|
|
if reply_handler:
|
|
result = self._connection.send_with_reply_handlers(message, timeout, reply_handler, error_handler)
|
|
args_tuple = result
|
|
else:
|
|
reply_message = self._connection.send_with_reply_and_block(message, timeout)
|
|
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 ProxyObject:
|
|
"""A proxy to the remote Object.
|
|
|
|
A ProxyObject is provided by the Bus. ProxyObjects
|
|
have member functions, and can be called like normal Python objects.
|
|
"""
|
|
ProxyMethodClass = ProxyMethod
|
|
DeferedMethodClass = DeferedMethod
|
|
|
|
INTROSPECT_STATE_DONT_INTROSPECT = 0
|
|
INTROSPECT_STATE_INTROSPECT_IN_PROGRESS = 1
|
|
INTROSPECT_STATE_INTROSPECT_DONE = 2
|
|
|
|
#TODO: default introspect to False right now because it is not done yet
|
|
# make sure to default to True later
|
|
def __init__(self, bus, named_service, object_path, introspect=True):
|
|
self._bus = bus
|
|
self._named_service = named_service
|
|
self._object_path = object_path
|
|
|
|
#PendingCall object for Introspect call
|
|
self._pending_introspect = None
|
|
#queue of async calls waiting on the Introspect to return
|
|
self._pending_introspect_queue = []
|
|
#dictionary mapping method names to their input signatures
|
|
self._introspect_method_map = {}
|
|
|
|
if not introspect:
|
|
self._introspect_state = self.INTROSPECT_STATE_DONT_INTROSPECT
|
|
else:
|
|
self._introspect_state = self.INTROSPECT_STATE_INTROSPECT_IN_PROGRESS
|
|
|
|
(result, self._pending_introspect) = self._Introspect()
|
|
|
|
|
|
def connect_to_signal(self, signal_name, handler_function, dbus_interface=None, **keywords):
|
|
self._bus.add_signal_receiver(handler_function,
|
|
signal_name=signal_name,
|
|
dbus_interface=dbus_interface,
|
|
named_service=self._named_service,
|
|
path=self._object_path,
|
|
**keywords)
|
|
|
|
def _Introspect(self):
|
|
message = dbus_bindings.MethodCall(self._object_path, 'org.freedesktop.DBus.Introspectable', 'Introspect')
|
|
message.set_destination(self._named_service)
|
|
|
|
result = self._bus.get_connection().send_with_reply_handlers(message, -1,
|
|
self._introspect_reply_handler,
|
|
self._introspect_error_handler)
|
|
return result
|
|
|
|
|
|
|
|
|
|
def _introspect_reply_handler(self, data):
|
|
try:
|
|
self._introspect_method_map = introspect_parser.process_introspection_data(data)
|
|
except IntrospectionParserException, e:
|
|
self._introspect_error_handler(e)
|
|
return
|
|
|
|
self._introspect_state = self.INTROSPECT_STATE_INTROSPECT_DONE
|
|
|
|
#TODO: we should actually call these even if introspection fails
|
|
for call in self._pending_introspect_queue:
|
|
(member, iface, args, keywords) = call
|
|
|
|
introspect_sig = None
|
|
|
|
tmp_iface = ''
|
|
if iface:
|
|
tmp_iface = iface + '.'
|
|
|
|
key = tmp_iface + '.' + member
|
|
if self._introspect_method_map.has_key (key):
|
|
introspect_sig = self._introspect_method_map[key]
|
|
|
|
|
|
call_object = self.ProxyMethodClass(self._bus.get_connection(),
|
|
self._named_service,
|
|
self._object_path, iface, member,
|
|
introspect_sig)
|
|
|
|
call_object(args, keywords)
|
|
|
|
def _introspect_error_handler(self, error):
|
|
self._introspect_state = self.INTROSPECT_STATE_DONT_INTROSPECT
|
|
sys.stderr.write(str(error))
|
|
|
|
def __getattr__(self, member, **keywords):
|
|
if member == '__call__':
|
|
return object.__call__
|
|
elif member.startswith('__') and member.endswith('__'):
|
|
raise AttributeError(member)
|
|
else:
|
|
introspect_sig = None
|
|
|
|
iface = None
|
|
if keywords.has_key('dbus_interface'):
|
|
iface = keywords['dbus_interface']
|
|
|
|
if self._introspect_state == self.INTROSPECT_STATE_INTROSPECT_IN_PROGRESS:
|
|
reply_handler = None
|
|
if keywords.has_key('reply_handler'):
|
|
reply_handler = keywords['reply_handler']
|
|
|
|
error_handler = None
|
|
if keywords.has_key('error_handler'):
|
|
error_handler = keywords['error_handler']
|
|
|
|
if not reply_handler:
|
|
self._pending_introspect.block()
|
|
else:
|
|
call = (memeber, iface, args, keywords)
|
|
self._pending_introspect_queue.append(call)
|
|
|
|
ret = self.DeferedMethodClass()
|
|
return ret
|
|
|
|
if self._introspect_state == self.INTROSPECT_STATE_INTROSPECT_DONE:
|
|
tmp_iface = ''
|
|
if iface:
|
|
tmp_iface = iface + '.'
|
|
|
|
key = tmp_iface + member
|
|
if self._introspect_method_map.has_key (key):
|
|
introspect_sig = self._introspect_method_map[key]
|
|
|
|
ret = self.ProxyMethodClass(self._bus.get_connection(),
|
|
self._named_service,
|
|
self._object_path, iface, member,
|
|
introspect_sig)
|
|
return ret
|
|
|
|
def __repr__(self):
|
|
return '<ProxyObject wrapping %s %s %s at %x>'%(
|
|
self._bus, self._named_service, self._object_path , id(self))
|
|
__str__ = __repr__
|
|
|