Rename dbus_bindings (sometimes a.k.a. dbus.dbus_bindings) to _dbus_bindings.
[dbus-python-phuang.git] / dbus / decorators.py
blob6dc21768ee7dbb86d6d620a57caf76faeda4e145
1 """Service-side D-Bus decorators."""
3 __all__ = ('explicitly_pass_message', 'method', 'signal')
4 __docformat__ = 'restructuredtext'
6 import _util
7 import inspect
8 import _dbus_bindings
10 def method(dbus_interface, in_signature=None, out_signature=None, async_callbacks=None, sender_keyword=None):
11 """Factory for decorators used to mark methods of a `dbus.service.Object`
12 to be exported on the D-Bus.
14 The decorated method will be exported over D-Bus as the method of the
15 same name on the given D-Bus interface.
17 :Parameters:
18 `dbus_interface` : str
19 Name of a D-Bus interface
20 `in_signature` : str or None
21 If not None, the signature of the method parameters in the usual
22 D-Bus notation
23 `out_signature` : str or None
24 If not None, the signature of the return value in the usual
25 D-Bus notation
26 `async_callbacks` : tuple containing (str,str), or None
27 If None (default) the decorated method is expected to return
28 values matching the `out_signature` as usual, or raise
29 an exception on error. If not None, the following applies:
31 `async_callbacks` contains the names of two keyword arguments to
32 the decorated function, which will be used to provide a success
33 callback and an error callback (in that order).
35 When the decorated method is called via the D-Bus, its normal
36 return value will be ignored; instead, a pair of callbacks are
37 passed as keyword arguments, and the decorated method is
38 expected to arrange for one of them to be called.
40 On success the success callback must be called, passing the
41 results of this method as positional parameters in the format
42 given by the `out_signature`.
44 On error the decorated method may either raise an exception
45 before it returns, or arrange for the error callback to be
46 called with an Exception instance as parameter.
48 `sender_keyword` : str or None
49 If not None, contains the name of a keyword argument to the
50 decorated function. When the method is called, the sender's
51 unique name will be passed as this keyword argument.
52 """
53 _util._validate_interface_or_name(dbus_interface)
55 def decorator(func):
56 args = inspect.getargspec(func)[0]
57 args.pop(0)
59 if async_callbacks:
60 if type(async_callbacks) != tuple:
61 raise TypeError('async_callbacks must be a tuple of (keyword for return callback, keyword for error callback)')
62 if len(async_callbacks) != 2:
63 raise ValueError('async_callbacks must be a tuple of (keyword for return callback, keyword for error callback)')
64 args.remove(async_callbacks[0])
65 args.remove(async_callbacks[1])
67 if sender_keyword:
68 args.remove(sender_keyword)
70 if in_signature:
71 in_sig = tuple(_dbus_bindings.Signature(in_signature))
73 if len(in_sig) > len(args):
74 raise ValueError, 'input signature is longer than the number of arguments taken'
75 elif len(in_sig) < len(args):
76 raise ValueError, 'input signature is shorter than the number of arguments taken'
78 func._dbus_is_method = True
79 func._dbus_async_callbacks = async_callbacks
80 func._dbus_interface = dbus_interface
81 func._dbus_in_signature = in_signature
82 func._dbus_out_signature = out_signature
83 func._dbus_sender_keyword = sender_keyword
84 func._dbus_args = args
85 return func
87 return decorator
89 def signal(dbus_interface, signature=None):
90 """Factory for decorators used to mark methods of a `dbus.service.Object`
91 to emit signals on the D-Bus.
93 Whenever the decorated method is called in Python, after the method
94 body is executed, a signal with the same name as the decorated method,
95 from the given D-Bus interface, will be emitted.
97 :Parameters:
98 `dbus_interface` : str
99 The D-Bus interface whose signal is emitted
100 `signature` : str
101 The signature of the signal in the usual D-Bus notation
103 _util._validate_interface_or_name(dbus_interface)
104 def decorator(func):
105 def emit_signal(self, *args, **keywords):
106 func(self, *args, **keywords)
107 message = _dbus_bindings.Signal(self._object_path, dbus_interface, func.__name__)
108 iter = message.get_iter(True)
110 if emit_signal._dbus_signature:
111 signature = tuple(_dbus_bindings.Signature(emit_signal._dbus_signature))
112 for (arg, sig) in zip(args, signature):
113 iter.append_strict(arg, sig)
114 else:
115 for arg in args:
116 iter.append(arg)
118 self._connection.send(message)
120 args = inspect.getargspec(func)[0]
121 args.pop(0)
123 if signature:
124 sig = tuple(_dbus_bindings.Signature(signature))
126 if len(sig) > len(args):
127 raise ValueError, 'signal signature is longer than the number of arguments provided'
128 elif len(sig) < len(args):
129 raise ValueError, 'signal signature is shorter than the number of arguments provided'
131 emit_signal.__name__ = func.__name__
132 emit_signal.__doc__ = func.__doc__
133 emit_signal._dbus_is_signal = True
134 emit_signal._dbus_interface = dbus_interface
135 emit_signal._dbus_signature = signature
136 emit_signal._dbus_args = args
137 return emit_signal
139 return decorator
141 def explicitly_pass_message(func):
142 """Decorator which marks the given function such that, if it is called
143 as a D-Bus signal recipient, then the Signal message will be passed
144 to it as a keyword parameter named ``dbus_message``.
146 Deprecated? Should Messages really be exposed to client code?
148 FIXME: this alters the namespace of the decorated function without
149 using the ``__magic__`` naming convention.
151 func._dbus_pass_message = True
152 return func