_dbus_bindings: Expose name-validation functions to Python code.
[dbus-python-phuang.git] / dbus / decorators.py
blob682607afdce1fe40747862e6df4124a62b76e409
1 """Service-side D-Bus decorators."""
3 __all__ = ('explicitly_pass_message', 'method', 'signal')
4 __docformat__ = 'restructuredtext'
6 import inspect
8 import _dbus_bindings
11 def method(dbus_interface, in_signature=None, out_signature=None, async_callbacks=None, sender_keyword=None):
12 """Factory for decorators used to mark methods of a `dbus.service.Object`
13 to be exported on the D-Bus.
15 The decorated method will be exported over D-Bus as the method of the
16 same name on the given D-Bus interface.
18 :Parameters:
19 `dbus_interface` : str
20 Name of a D-Bus interface
21 `in_signature` : str or None
22 If not None, the signature of the method parameters in the usual
23 D-Bus notation
24 `out_signature` : str or None
25 If not None, the signature of the return value in the usual
26 D-Bus notation
27 `async_callbacks` : tuple containing (str,str), or None
28 If None (default) the decorated method is expected to return
29 values matching the `out_signature` as usual, or raise
30 an exception on error. If not None, the following applies:
32 `async_callbacks` contains the names of two keyword arguments to
33 the decorated function, which will be used to provide a success
34 callback and an error callback (in that order).
36 When the decorated method is called via the D-Bus, its normal
37 return value will be ignored; instead, a pair of callbacks are
38 passed as keyword arguments, and the decorated method is
39 expected to arrange for one of them to be called.
41 On success the success callback must be called, passing the
42 results of this method as positional parameters in the format
43 given by the `out_signature`.
45 On error the decorated method may either raise an exception
46 before it returns, or arrange for the error callback to be
47 called with an Exception instance as parameter.
49 `sender_keyword` : str or None
50 If not None, contains the name of a keyword argument to the
51 decorated function. When the method is called, the sender's
52 unique name will be passed as this keyword argument.
53 """
54 _dbus_bindings.validate_interface_name(dbus_interface)
56 def decorator(func):
57 args = inspect.getargspec(func)[0]
58 args.pop(0)
60 if async_callbacks:
61 if type(async_callbacks) != tuple:
62 raise TypeError('async_callbacks must be a tuple of (keyword for return callback, keyword for error callback)')
63 if len(async_callbacks) != 2:
64 raise ValueError('async_callbacks must be a tuple of (keyword for return callback, keyword for error callback)')
65 args.remove(async_callbacks[0])
66 args.remove(async_callbacks[1])
68 if sender_keyword:
69 args.remove(sender_keyword)
71 if in_signature:
72 in_sig = tuple(_dbus_bindings.Signature(in_signature))
74 if len(in_sig) > len(args):
75 raise ValueError, 'input signature is longer than the number of arguments taken'
76 elif len(in_sig) < len(args):
77 raise ValueError, 'input signature is shorter than the number of arguments taken'
79 func._dbus_is_method = True
80 func._dbus_async_callbacks = async_callbacks
81 func._dbus_interface = dbus_interface
82 func._dbus_in_signature = in_signature
83 func._dbus_out_signature = out_signature
84 func._dbus_sender_keyword = sender_keyword
85 func._dbus_args = args
86 return func
88 return decorator
91 def signal(dbus_interface, signature=None):
92 """Factory for decorators used to mark methods of a `dbus.service.Object`
93 to emit signals on the D-Bus.
95 Whenever the decorated method is called in Python, after the method
96 body is executed, a signal with the same name as the decorated method,
97 from the given D-Bus interface, will be emitted.
99 :Parameters:
100 `dbus_interface` : str
101 The D-Bus interface whose signal is emitted
102 `signature` : str
103 The signature of the signal in the usual D-Bus notation
105 _dbus_bindings.validate_interface_name(dbus_interface)
106 def decorator(func):
107 def emit_signal(self, *args, **keywords):
108 func(self, *args, **keywords)
109 message = _dbus_bindings.SignalMessage(self._object_path, dbus_interface, func.__name__)
111 if emit_signal._dbus_signature:
112 message.append(signature=emit_signal._dbus_signature,
113 *args)
114 else:
115 message.append(*args)
117 self._connection._send(message)
119 args = inspect.getargspec(func)[0]
120 args.pop(0)
122 if signature:
123 sig = tuple(_dbus_bindings.Signature(signature))
125 if len(sig) > len(args):
126 raise ValueError, 'signal signature is longer than the number of arguments provided'
127 elif len(sig) < len(args):
128 raise ValueError, 'signal signature is shorter than the number of arguments provided'
130 emit_signal.__name__ = func.__name__
131 emit_signal.__doc__ = func.__doc__
132 emit_signal._dbus_is_signal = True
133 emit_signal._dbus_interface = dbus_interface
134 emit_signal._dbus_signature = signature
135 emit_signal._dbus_args = args
136 return emit_signal
138 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