Preparation for fallback-object support:
[dbus-python-phuang.git] / dbus / decorators.py
blob2b0d5390aefbf6611e87918f1c8a5b429c59ebc4
1 """Service-side D-Bus decorators."""
3 # Copyright (C) 2003, 2004, 2005, 2006 Red Hat Inc. <http://www.redhat.com/>
4 # Copyright (C) 2003 David Zeuthen
5 # Copyright (C) 2004 Rob Taylor
6 # Copyright (C) 2005, 2006 Collabora Ltd. <http://www.collabora.co.uk/>
8 # Licensed under the Academic Free License version 2.1
10 # This program is free software; you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License as published by
12 # the Free Software Foundation; either version 2 of the License, or
13 # (at your option) any later version.
15 # This program is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU General Public License for more details.
20 # You should have received a copy of the GNU General Public License
21 # along with this program; if not, write to the Free Software
22 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 __all__ = ('method', 'signal')
25 __docformat__ = 'restructuredtext'
27 import inspect
29 import _dbus_bindings
32 def method(dbus_interface, in_signature=None, out_signature=None,
33 async_callbacks=None,
34 sender_keyword=None, path_keyword=None, destination_keyword=None,
35 message_keyword=None,
36 utf8_strings=False, byte_arrays=False):
37 """Factory for decorators used to mark methods of a `dbus.service.Object`
38 to be exported on the D-Bus.
40 The decorated method will be exported over D-Bus as the method of the
41 same name on the given D-Bus interface.
43 :Parameters:
44 `dbus_interface` : str
45 Name of a D-Bus interface
46 `in_signature` : str or None
47 If not None, the signature of the method parameters in the usual
48 D-Bus notation
49 `out_signature` : str or None
50 If not None, the signature of the return value in the usual
51 D-Bus notation
52 `async_callbacks` : tuple containing (str,str), or None
53 If None (default) the decorated method is expected to return
54 values matching the `out_signature` as usual, or raise
55 an exception on error. If not None, the following applies:
57 `async_callbacks` contains the names of two keyword arguments to
58 the decorated function, which will be used to provide a success
59 callback and an error callback (in that order).
61 When the decorated method is called via the D-Bus, its normal
62 return value will be ignored; instead, a pair of callbacks are
63 passed as keyword arguments, and the decorated method is
64 expected to arrange for one of them to be called.
66 On success the success callback must be called, passing the
67 results of this method as positional parameters in the format
68 given by the `out_signature`.
70 On error the decorated method may either raise an exception
71 before it returns, or arrange for the error callback to be
72 called with an Exception instance as parameter.
74 `sender_keyword` : str or None
75 If not None, contains the name of a keyword argument to the
76 decorated function, conventionally ``'sender'``. When the
77 method is called, the sender's unique name will be passed as
78 this keyword argument.
80 `path_keyword` : str or None
81 If not None (the default), the decorated method will receive
82 the destination object path as a keyword argument with this
83 name. Normally you already know the object path, but in the
84 case of "fallback paths" you'll usually want to use the object
85 path in the method's implementation.
87 `destination_keyword` : str or None
88 If not None (the default), the decorated method will receive
89 the destination bus name as a keyword argument with this name.
90 Included for completeness - you shouldn't need this.
92 `message_keyword` : str or None
93 If not None (the default), the decorated method will receive
94 the `dbus.lowlevel.MethodCallMessage` as a keyword argument
95 with this name.
97 `utf8_strings` : bool
98 If False (default), D-Bus strings are passed to the decorated
99 method as objects of class dbus.String, a unicode subclass.
101 If True, D-Bus strings are passed to the decorated method
102 as objects of class dbus.UTF8String, a str subclass guaranteed
103 to be encoded in UTF-8.
105 This option does not affect object-paths and signatures, which
106 are always 8-bit strings (str subclass) encoded in ASCII.
108 `byte_arrays` : bool
109 If False (default), a byte array will be passed to the decorated
110 method as an `Array` (a list subclass) of `Byte` objects.
112 If True, a byte array will be passed to the decorated method as
113 a `ByteArray`, a str subclass. This is usually what you want,
114 but is switched off by default to keep dbus-python's API
115 consistent.
117 _dbus_bindings.validate_interface_name(dbus_interface)
119 def decorator(func):
120 args = inspect.getargspec(func)[0]
121 args.pop(0)
123 if async_callbacks:
124 if type(async_callbacks) != tuple:
125 raise TypeError('async_callbacks must be a tuple of (keyword for return callback, keyword for error callback)')
126 if len(async_callbacks) != 2:
127 raise ValueError('async_callbacks must be a tuple of (keyword for return callback, keyword for error callback)')
128 args.remove(async_callbacks[0])
129 args.remove(async_callbacks[1])
131 if sender_keyword:
132 args.remove(sender_keyword)
133 if path_keyword:
134 args.remove(path_keyword)
135 if destination_keyword:
136 args.remove(destination_keyword)
137 if message_keyword:
138 args.remove(message_keyword)
140 if in_signature:
141 in_sig = tuple(_dbus_bindings.Signature(in_signature))
143 if len(in_sig) > len(args):
144 raise ValueError, 'input signature is longer than the number of arguments taken'
145 elif len(in_sig) < len(args):
146 raise ValueError, 'input signature is shorter than the number of arguments taken'
148 func._dbus_is_method = True
149 func._dbus_async_callbacks = async_callbacks
150 func._dbus_interface = dbus_interface
151 func._dbus_in_signature = in_signature
152 func._dbus_out_signature = out_signature
153 func._dbus_sender_keyword = sender_keyword
154 func._dbus_path_keyword = path_keyword
155 func._dbus_destination_keyword = destination_keyword
156 func._dbus_message_keyword = message_keyword
157 func._dbus_args = args
158 func._dbus_get_args_options = {'byte_arrays': byte_arrays,
159 'utf8_strings': utf8_strings}
160 return func
162 return decorator
165 def signal(dbus_interface, signature=None, dbus_name=None, path_keyword=None):
166 """Factory for decorators used to mark methods of a `dbus.service.Object`
167 to emit signals on the D-Bus.
169 Whenever the decorated method is called in Python, after the method
170 body is executed, a signal whose name is `dbus_name` (or if `dbus_name`
171 is None, a signal with the same name as the decorated method),
172 with the given D-Bus interface, will be emitted from this object.
174 :Parameters:
175 `dbus_interface` : str
176 The D-Bus interface whose signal is emitted
177 `signature` : str
178 The signature of the signal in the usual D-Bus notation
180 `dbus_name` : str or None
181 The signal to be emitted when the decorated method is called.
182 If None, use the name (``__name__``) of the decorated method.
184 `path_keyword` : str or None
185 A keyword argument to the decorated method. If not None,
186 that argument will not be emitted as an argument of
187 the signal, and when the signal is emitted, it will appear
188 to come from the object path given by the keyword argument.
190 Note that when calling the decorated method, you must always
191 pass in the object path as a keyword argument, not as a
192 positional argument.
194 _dbus_bindings.validate_interface_name(dbus_interface)
195 def decorator(func):
196 member_name = dbus_name
197 if member_name is None:
198 member_name = func.__name__
199 _dbus_bindings.validate_member_name(member_name)
201 def emit_signal(self, *args, **keywords):
202 func(self, *args, **keywords)
203 object_path = self.__dbus_object_path__
204 if path_keyword:
205 kw = keywords.pop(path_keyword, None)
206 if kw is not None:
207 if not (kw == object_path
208 or object_path == '/'
209 or kw.startswith(object_path + '/')):
210 raise DBusException('Object path %s is not in the '
211 'subtree starting at %s'
212 % (kw, object_path))
213 object_path = kw
215 message = _dbus_bindings.SignalMessage(object_path,
216 dbus_interface,
217 member_name)
219 if signature is not None:
220 message.append(signature=signature, *args)
221 else:
222 message.append(*args)
224 self._connection.send_message(message)
226 args = inspect.getargspec(func)[0]
227 args.pop(0)
229 if signature:
230 sig = tuple(_dbus_bindings.Signature(signature))
232 if len(sig) > len(args):
233 raise ValueError, 'signal signature is longer than the number of arguments provided'
234 elif len(sig) < len(args):
235 raise ValueError, 'signal signature is shorter than the number of arguments provided'
237 emit_signal.__name__ = func.__name__
238 emit_signal.__doc__ = func.__doc__
239 emit_signal._dbus_is_signal = True
240 emit_signal._dbus_interface = dbus_interface
241 emit_signal._dbus_signature = signature
242 emit_signal._dbus_args = args
243 return emit_signal
245 return decorator