mirror of
https://gitlab.freedesktop.org/dbus/dbus.git
synced 2025-12-28 01:30:09 +01:00
* python/decorators.py: Add optional arguments to the method and
signal decorators to allow you to specify the signature of arguments
and return values. Preserve the doc strings of signal functions in the
decorated version, for pydoc and friends.
* python/dbus_bindings.pyx, python/proxies.py: Replace the
parse_signature_block function with an iterable dbus.Signature()
type. Fix a bug in MessageIter.append_strict where you could append
anything by claiming it was a string.
* python/service.py: Use the out_signature decoration on methods to
marshal return values, meaning you no longer require dbus.Array()
or dbus.Dictionary() to indicate the type when returning empty
arrays or dictionaries. Fix a bug where exceptions which are defined
in __main__ are not turned into error replies.
* test/python/test-client.py, test/python/test-service.py: Add test
for correct marshalling of return values according to out_signature.
Fix a bug in the async call test where the error_handler is missing a
self argument.
228 lines
8.6 KiB
Python
228 lines
8.6 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']
|
|
|
|
ignore_reply = False
|
|
if keywords.has_key('ignore_reply'):
|
|
ignore_reply = keywords['ignore_reply']
|
|
|
|
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)
|
|
|
|
if self._introspect_sig:
|
|
for (arg, sig) in zip(args, dbus_bindings.Signature(self._introspect_sig)):
|
|
iter.append_strict(arg, sig)
|
|
else:
|
|
for arg in args:
|
|
iter.append(arg)
|
|
|
|
if ignore_reply:
|
|
result = self._connection.send(message)
|
|
args_tuple = (result,)
|
|
elif 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
|
|
|
|
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_execute_queue(self):
|
|
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_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
|
|
#self._introspect_execute_queue()
|
|
|
|
def _introspect_error_handler(self, error):
|
|
self._introspect_state = self.INTROSPECT_STATE_DONT_INTROSPECT
|
|
self._introspect_execute_queue()
|
|
sys.stderr.write("Introspect error: " + str(error) + "\n")
|
|
|
|
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__
|
|
|