doc/tutorial.txt: Don't claim we have a tutorial for p2p connections yet
[dbus-python-phuang.git] / dbus / decorators.py
blobaa08940ebb697513c440977855003ebd104e9f05
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, 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 with the same name as the decorated method,
171 with the given D-Bus interface, will be emitted from this object.
173 :Parameters:
174 `dbus_interface` : str
175 The D-Bus interface whose signal is emitted
176 `signature` : str
177 The signature of the signal in the usual D-Bus notation
179 `path_keyword` : str or None
180 A keyword argument to the decorated method. If not None,
181 that argument will not be emitted as an argument of
182 the signal, and when the signal is emitted, it will appear
183 to come from the object path given by the keyword argument.
185 Note that when calling the decorated method, you must always
186 pass in the object path as a keyword argument, not as a
187 positional argument.
189 _dbus_bindings.validate_interface_name(dbus_interface)
190 def decorator(func):
191 member_name = func.__name__
192 _dbus_bindings.validate_member_name(member_name)
194 def emit_signal(self, *args, **keywords):
195 func(self, *args, **keywords)
196 object_path = self.__dbus_object_path__
197 if path_keyword:
198 kw = keywords.pop(path_keyword, None)
199 if kw is not None:
200 if not (kw == object_path
201 or object_path == '/'
202 or kw.startswith(object_path + '/')):
203 raise DBusException('Object path %s is not in the '
204 'subtree starting at %s'
205 % (kw, object_path))
206 object_path = kw
208 message = _dbus_bindings.SignalMessage(object_path,
209 dbus_interface,
210 member_name)
212 if signature is not None:
213 message.append(signature=signature, *args)
214 else:
215 message.append(*args)
217 self._connection.send_message(message)
219 args = inspect.getargspec(func)[0]
220 args.pop(0)
222 if signature:
223 sig = tuple(_dbus_bindings.Signature(signature))
225 if len(sig) > len(args):
226 raise ValueError, 'signal signature is longer than the number of arguments provided'
227 elif len(sig) < len(args):
228 raise ValueError, 'signal signature is shorter than the number of arguments provided'
230 emit_signal.__name__ = func.__name__
231 emit_signal.__doc__ = func.__doc__
232 emit_signal._dbus_is_signal = True
233 emit_signal._dbus_interface = dbus_interface
234 emit_signal._dbus_signature = signature
235 emit_signal._dbus_args = args
236 return emit_signal
238 return decorator