Reorganise README to reflect that API docs are generated by default
[dbus-python-phuang.git] / dbus / decorators.py
blob00cf20d350f3ab6c1189ca3e42f7402d8ccb499b
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 from dbus import validate_interface_name, Signature, validate_member_name
30 from dbus.lowlevel import SignalMessage
31 from dbus.exceptions import DBusException
34 def method(dbus_interface, in_signature=None, out_signature=None,
35 async_callbacks=None,
36 sender_keyword=None, path_keyword=None, destination_keyword=None,
37 message_keyword=None, connection_keyword=None,
38 utf8_strings=False, byte_arrays=False,
39 rel_path_keyword=None):
40 """Factory for decorators used to mark methods of a `dbus.service.Object`
41 to be exported on the D-Bus.
43 The decorated method will be exported over D-Bus as the method of the
44 same name on the given D-Bus interface.
46 :Parameters:
47 `dbus_interface` : str
48 Name of a D-Bus interface
49 `in_signature` : str or None
50 If not None, the signature of the method parameters in the usual
51 D-Bus notation
52 `out_signature` : str or None
53 If not None, the signature of the return value in the usual
54 D-Bus notation
55 `async_callbacks` : tuple containing (str,str), or None
56 If None (default) the decorated method is expected to return
57 values matching the `out_signature` as usual, or raise
58 an exception on error. If not None, the following applies:
60 `async_callbacks` contains the names of two keyword arguments to
61 the decorated function, which will be used to provide a success
62 callback and an error callback (in that order).
64 When the decorated method is called via the D-Bus, its normal
65 return value will be ignored; instead, a pair of callbacks are
66 passed as keyword arguments, and the decorated method is
67 expected to arrange for one of them to be called.
69 On success the success callback must be called, passing the
70 results of this method as positional parameters in the format
71 given by the `out_signature`.
73 On error the decorated method may either raise an exception
74 before it returns, or arrange for the error callback to be
75 called with an Exception instance as parameter.
77 `sender_keyword` : str or None
78 If not None, contains the name of a keyword argument to the
79 decorated function, conventionally ``'sender'``. When the
80 method is called, the sender's unique name will be passed as
81 this keyword argument.
83 `path_keyword` : str or None
84 If not None (the default), the decorated method will receive
85 the destination object path as a keyword argument with this
86 name. Normally you already know the object path, but in the
87 case of "fallback paths" you'll usually want to use the object
88 path in the method's implementation.
90 For fallback objects, `rel_path_keyword` (new in 0.82.2) is
91 likely to be more useful.
93 :Since: 0.80.0?
95 `rel_path_keyword` : str or None
96 If not None (the default), the decorated method will receive
97 the destination object path, relative to the path at which the
98 object was exported, as a keyword argument with this
99 name. For non-fallback objects the relative path will always be
100 '/'.
102 :Since: 0.82.2
104 `destination_keyword` : str or None
105 If not None (the default), the decorated method will receive
106 the destination bus name as a keyword argument with this name.
107 Included for completeness - you shouldn't need this.
109 :Since: 0.80.0?
111 `message_keyword` : str or None
112 If not None (the default), the decorated method will receive
113 the `dbus.lowlevel.MethodCallMessage` as a keyword argument
114 with this name.
116 :Since: 0.80.0?
118 `connection_keyword` : str or None
119 If not None (the default), the decorated method will receive
120 the `dbus.connection.Connection` as a keyword argument
121 with this name. This is generally only useful for objects
122 that are available on more than one connection.
124 :Since: 0.82.0
126 `utf8_strings` : bool
127 If False (default), D-Bus strings are passed to the decorated
128 method as objects of class dbus.String, a unicode subclass.
130 If True, D-Bus strings are passed to the decorated method
131 as objects of class dbus.UTF8String, a str subclass guaranteed
132 to be encoded in UTF-8.
134 This option does not affect object-paths and signatures, which
135 are always 8-bit strings (str subclass) encoded in ASCII.
137 :Since: 0.80.0
139 `byte_arrays` : bool
140 If False (default), a byte array will be passed to the decorated
141 method as an `Array` (a list subclass) of `Byte` objects.
143 If True, a byte array will be passed to the decorated method as
144 a `ByteArray`, a str subclass. This is usually what you want,
145 but is switched off by default to keep dbus-python's API
146 consistent.
148 :Since: 0.80.0
150 validate_interface_name(dbus_interface)
152 def decorator(func):
153 args = inspect.getargspec(func)[0]
154 args.pop(0)
156 if async_callbacks:
157 if type(async_callbacks) != tuple:
158 raise TypeError('async_callbacks must be a tuple of (keyword for return callback, keyword for error callback)')
159 if len(async_callbacks) != 2:
160 raise ValueError('async_callbacks must be a tuple of (keyword for return callback, keyword for error callback)')
161 args.remove(async_callbacks[0])
162 args.remove(async_callbacks[1])
164 if sender_keyword:
165 args.remove(sender_keyword)
166 if rel_path_keyword:
167 args.remove(rel_path_keyword)
168 if path_keyword:
169 args.remove(path_keyword)
170 if destination_keyword:
171 args.remove(destination_keyword)
172 if message_keyword:
173 args.remove(message_keyword)
174 if connection_keyword:
175 args.remove(connection_keyword)
177 if in_signature:
178 in_sig = tuple(Signature(in_signature))
180 if len(in_sig) > len(args):
181 raise ValueError, 'input signature is longer than the number of arguments taken'
182 elif len(in_sig) < len(args):
183 raise ValueError, 'input signature is shorter than the number of arguments taken'
185 func._dbus_is_method = True
186 func._dbus_async_callbacks = async_callbacks
187 func._dbus_interface = dbus_interface
188 func._dbus_in_signature = in_signature
189 func._dbus_out_signature = out_signature
190 func._dbus_sender_keyword = sender_keyword
191 func._dbus_path_keyword = path_keyword
192 func._dbus_rel_path_keyword = rel_path_keyword
193 func._dbus_destination_keyword = destination_keyword
194 func._dbus_message_keyword = message_keyword
195 func._dbus_connection_keyword = connection_keyword
196 func._dbus_args = args
197 func._dbus_get_args_options = {'byte_arrays': byte_arrays,
198 'utf8_strings': utf8_strings}
199 return func
201 return decorator
204 def signal(dbus_interface, signature=None, path_keyword=None,
205 rel_path_keyword=None):
206 """Factory for decorators used to mark methods of a `dbus.service.Object`
207 to emit signals on the D-Bus.
209 Whenever the decorated method is called in Python, after the method
210 body is executed, a signal with the same name as the decorated method,
211 with the given D-Bus interface, will be emitted from this object.
213 :Parameters:
214 `dbus_interface` : str
215 The D-Bus interface whose signal is emitted
216 `signature` : str
217 The signature of the signal in the usual D-Bus notation
219 `path_keyword` : str or None
220 A keyword argument to the decorated method. If not None,
221 that argument will not be emitted as an argument of
222 the signal, and when the signal is emitted, it will appear
223 to come from the object path given by the keyword argument.
225 Note that when calling the decorated method, you must always
226 pass in the object path as a keyword argument, not as a
227 positional argument.
229 This keyword argument cannot be used on objects where
230 the class attribute ``SUPPORTS_MULTIPLE_OBJECT_PATHS`` is true.
232 :Deprecated: since 0.82.0. Use `rel_path_keyword` instead.
234 `rel_path_keyword` : str or None
235 A keyword argument to the decorated method. If not None,
236 that argument will not be emitted as an argument of
237 the signal.
239 When the signal is emitted, if the named keyword argument is given,
240 the signal will appear to come from the object path obtained by
241 appending the keyword argument to the object's object path.
242 This is useful to implement "fallback objects" (objects which
243 own an entire subtree of the object-path tree).
245 If the object is available at more than one object-path on the
246 same or different connections, the signal will be emitted at
247 an appropriate object-path on each connection - for instance,
248 if the object is exported at /abc on connection 1 and at
249 /def and /x/y/z on connection 2, and the keyword argument is
250 /foo, then signals will be emitted from /abc/foo and /def/foo
251 on connection 1, and /x/y/z/foo on connection 2.
253 :Since: 0.82.0
255 validate_interface_name(dbus_interface)
257 if path_keyword is not None:
258 from warnings import warn
259 warn(DeprecationWarning('dbus.service.signal::path_keyword has been '
260 'deprecated since dbus-python 0.82.0, and '
261 'will not work on objects that support '
262 'multiple object paths'),
263 DeprecationWarning, stacklevel=2)
264 if rel_path_keyword is not None:
265 raise TypeError('dbus.service.signal::path_keyword and '
266 'rel_path_keyword cannot both be used')
268 def decorator(func):
269 member_name = func.__name__
270 validate_member_name(member_name)
272 def emit_signal(self, *args, **keywords):
273 abs_path = None
274 if path_keyword is not None:
275 if self.SUPPORTS_MULTIPLE_OBJECT_PATHS:
276 raise TypeError('path_keyword cannot be used on the '
277 'signals of an object that supports '
278 'multiple object paths')
279 abs_path = keywords.pop(path_keyword, None)
280 if (abs_path != self.__dbus_object_path__ and
281 not self.__dbus_object_path__.startswith(abs_path + '/')):
282 raise ValueError('Path %r is not below %r', abs_path,
283 self.__dbus_object_path__)
285 rel_path = None
286 if rel_path_keyword is not None:
287 rel_path = keywords.pop(rel_path_keyword, None)
289 func(self, *args, **keywords)
291 for location in self.locations:
292 if abs_path is None:
293 # non-deprecated case
294 if rel_path is None or rel_path in ('/', ''):
295 object_path = location[1]
296 else:
297 # will be validated by SignalMessage ctor in a moment
298 object_path = location[1] + rel_path
299 else:
300 object_path = abs_path
302 message = SignalMessage(object_path,
303 dbus_interface,
304 member_name)
305 message.append(signature=signature, *args)
307 location[0].send_message(message)
308 # end emit_signal
310 args = inspect.getargspec(func)[0]
311 args.pop(0)
313 for keyword in rel_path_keyword, path_keyword:
314 if keyword is not None:
315 try:
316 args.remove(keyword)
317 except ValueError:
318 raise ValueError('function has no argument "%s"' % keyword)
320 if signature:
321 sig = tuple(Signature(signature))
323 if len(sig) > len(args):
324 raise ValueError, 'signal signature is longer than the number of arguments provided'
325 elif len(sig) < len(args):
326 raise ValueError, 'signal signature is shorter than the number of arguments provided'
328 emit_signal.__name__ = func.__name__
329 emit_signal.__doc__ = func.__doc__
330 emit_signal._dbus_is_signal = True
331 emit_signal._dbus_interface = dbus_interface
332 emit_signal._dbus_signature = signature
333 emit_signal._dbus_args = args
334 return emit_signal
336 return decorator