dbus.service: change unexport() to remove_from_connection() at J5's request
[dbus-python-phuang.git] / dbus / decorators.py
blob9cc0dbe414a18a903b309afb5255b181f78ad901
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
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,
38 utf8_strings=False, byte_arrays=False):
39 """Factory for decorators used to mark methods of a `dbus.service.Object`
40 to be exported on the D-Bus.
42 The decorated method will be exported over D-Bus as the method of the
43 same name on the given D-Bus interface.
45 :Parameters:
46 `dbus_interface` : str
47 Name of a D-Bus interface
48 `in_signature` : str or None
49 If not None, the signature of the method parameters in the usual
50 D-Bus notation
51 `out_signature` : str or None
52 If not None, the signature of the return value in the usual
53 D-Bus notation
54 `async_callbacks` : tuple containing (str,str), or None
55 If None (default) the decorated method is expected to return
56 values matching the `out_signature` as usual, or raise
57 an exception on error. If not None, the following applies:
59 `async_callbacks` contains the names of two keyword arguments to
60 the decorated function, which will be used to provide a success
61 callback and an error callback (in that order).
63 When the decorated method is called via the D-Bus, its normal
64 return value will be ignored; instead, a pair of callbacks are
65 passed as keyword arguments, and the decorated method is
66 expected to arrange for one of them to be called.
68 On success the success callback must be called, passing the
69 results of this method as positional parameters in the format
70 given by the `out_signature`.
72 On error the decorated method may either raise an exception
73 before it returns, or arrange for the error callback to be
74 called with an Exception instance as parameter.
76 `sender_keyword` : str or None
77 If not None, contains the name of a keyword argument to the
78 decorated function, conventionally ``'sender'``. When the
79 method is called, the sender's unique name will be passed as
80 this keyword argument.
82 `path_keyword` : str or None
83 If not None (the default), the decorated method will receive
84 the destination object path as a keyword argument with this
85 name. Normally you already know the object path, but in the
86 case of "fallback paths" you'll usually want to use the object
87 path in the method's implementation.
89 `destination_keyword` : str or None
90 If not None (the default), the decorated method will receive
91 the destination bus name as a keyword argument with this name.
92 Included for completeness - you shouldn't need this.
94 `message_keyword` : str or None
95 If not None (the default), the decorated method will receive
96 the `dbus.lowlevel.MethodCallMessage` as a keyword argument
97 with this name.
99 `utf8_strings` : bool
100 If False (default), D-Bus strings are passed to the decorated
101 method as objects of class dbus.String, a unicode subclass.
103 If True, D-Bus strings are passed to the decorated method
104 as objects of class dbus.UTF8String, a str subclass guaranteed
105 to be encoded in UTF-8.
107 This option does not affect object-paths and signatures, which
108 are always 8-bit strings (str subclass) encoded in ASCII.
110 `byte_arrays` : bool
111 If False (default), a byte array will be passed to the decorated
112 method as an `Array` (a list subclass) of `Byte` objects.
114 If True, a byte array will be passed to the decorated method as
115 a `ByteArray`, a str subclass. This is usually what you want,
116 but is switched off by default to keep dbus-python's API
117 consistent.
119 _dbus_bindings.validate_interface_name(dbus_interface)
121 def decorator(func):
122 args = inspect.getargspec(func)[0]
123 args.pop(0)
125 if async_callbacks:
126 if type(async_callbacks) != tuple:
127 raise TypeError('async_callbacks must be a tuple of (keyword for return callback, keyword for error callback)')
128 if len(async_callbacks) != 2:
129 raise ValueError('async_callbacks must be a tuple of (keyword for return callback, keyword for error callback)')
130 args.remove(async_callbacks[0])
131 args.remove(async_callbacks[1])
133 if sender_keyword:
134 args.remove(sender_keyword)
135 if path_keyword:
136 args.remove(path_keyword)
137 if destination_keyword:
138 args.remove(destination_keyword)
139 if message_keyword:
140 args.remove(message_keyword)
142 if in_signature:
143 in_sig = tuple(_dbus_bindings.Signature(in_signature))
145 if len(in_sig) > len(args):
146 raise ValueError, 'input signature is longer than the number of arguments taken'
147 elif len(in_sig) < len(args):
148 raise ValueError, 'input signature is shorter than the number of arguments taken'
150 func._dbus_is_method = True
151 func._dbus_async_callbacks = async_callbacks
152 func._dbus_interface = dbus_interface
153 func._dbus_in_signature = in_signature
154 func._dbus_out_signature = out_signature
155 func._dbus_sender_keyword = sender_keyword
156 func._dbus_path_keyword = path_keyword
157 func._dbus_destination_keyword = destination_keyword
158 func._dbus_message_keyword = message_keyword
159 func._dbus_args = args
160 func._dbus_get_args_options = {'byte_arrays': byte_arrays,
161 'utf8_strings': utf8_strings}
162 return func
164 return decorator
167 def signal(dbus_interface, signature=None, path_keyword=None):
168 """Factory for decorators used to mark methods of a `dbus.service.Object`
169 to emit signals on the D-Bus.
171 Whenever the decorated method is called in Python, after the method
172 body is executed, a signal with the same name as the decorated method,
173 with the given D-Bus interface, will be emitted from this object.
175 :Parameters:
176 `dbus_interface` : str
177 The D-Bus interface whose signal is emitted
178 `signature` : str
179 The signature of the signal in the usual D-Bus notation
181 `path_keyword` : str or None
182 A keyword argument to the decorated method. If not None,
183 that argument will not be emitted as an argument of
184 the signal, and when the signal is emitted, it will appear
185 to come from the object path given by the keyword argument.
187 Note that when calling the decorated method, you must always
188 pass in the object path as a keyword argument, not as a
189 positional argument.
191 _dbus_bindings.validate_interface_name(dbus_interface)
192 def decorator(func):
193 member_name = func.__name__
194 _dbus_bindings.validate_member_name(member_name)
196 def emit_signal(self, *args, **keywords):
197 func(self, *args, **keywords)
198 object_path = self.__dbus_object_path__
199 if path_keyword:
200 kw = keywords.pop(path_keyword, None)
201 if kw is not None:
202 if not (kw == object_path
203 or object_path == '/'
204 or kw.startswith(object_path + '/')):
205 raise DBusException('Object path %s is not in the '
206 'subtree starting at %s'
207 % (kw, object_path))
208 object_path = kw
210 message = _dbus_bindings.SignalMessage(object_path,
211 dbus_interface,
212 member_name)
214 if signature is not None:
215 message.append(signature=signature, *args)
216 else:
217 message.append(*args)
219 self._connection.send_message(message)
221 args = inspect.getargspec(func)[0]
222 args.pop(0)
224 if signature:
225 sig = tuple(_dbus_bindings.Signature(signature))
227 if len(sig) > len(args):
228 raise ValueError, 'signal signature is longer than the number of arguments provided'
229 elif len(sig) < len(args):
230 raise ValueError, 'signal signature is shorter than the number of arguments provided'
232 emit_signal.__name__ = func.__name__
233 emit_signal.__doc__ = func.__doc__
234 emit_signal._dbus_is_signal = True
235 emit_signal._dbus_interface = dbus_interface
236 emit_signal._dbus_signature = signature
237 emit_signal._dbus_args = args
238 return emit_signal
240 return decorator