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 # Permission is hereby granted, free of charge, to any person
9 # obtaining a copy of this software and associated documentation
10 # files (the "Software"), to deal in the Software without
11 # restriction, including without limitation the rights to use, copy,
12 # modify, merge, publish, distribute, sublicense, and/or sell copies
13 # of the Software, and to permit persons to whom the Software is
14 # furnished to do so, subject to the following conditions:
16 # The above copyright notice and this permission notice shall be
17 # included in all copies or substantial portions of the Software.
19 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
23 # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
24 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 # DEALINGS IN THE SOFTWARE.
28 __all__
= ('method', 'signal')
29 __docformat__
= 'restructuredtext'
33 from dbus
import validate_interface_name
, Signature
, validate_member_name
34 from dbus
.lowlevel
import SignalMessage
35 from dbus
.exceptions
import DBusException
38 def method(dbus_interface
, in_signature
=None, out_signature
=None,
40 sender_keyword
=None, path_keyword
=None, destination_keyword
=None,
41 message_keyword
=None, connection_keyword
=None,
42 utf8_strings
=False, byte_arrays
=False,
43 rel_path_keyword
=None):
44 """Factory for decorators used to mark methods of a `dbus.service.Object`
45 to be exported on the D-Bus.
47 The decorated method will be exported over D-Bus as the method of the
48 same name on the given D-Bus interface.
51 `dbus_interface` : str
52 Name of a D-Bus interface
53 `in_signature` : str or None
54 If not None, the signature of the method parameters in the usual
56 `out_signature` : str or None
57 If not None, the signature of the return value in the usual
59 `async_callbacks` : tuple containing (str,str), or None
60 If None (default) the decorated method is expected to return
61 values matching the `out_signature` as usual, or raise
62 an exception on error. If not None, the following applies:
64 `async_callbacks` contains the names of two keyword arguments to
65 the decorated function, which will be used to provide a success
66 callback and an error callback (in that order).
68 When the decorated method is called via the D-Bus, its normal
69 return value will be ignored; instead, a pair of callbacks are
70 passed as keyword arguments, and the decorated method is
71 expected to arrange for one of them to be called.
73 On success the success callback must be called, passing the
74 results of this method as positional parameters in the format
75 given by the `out_signature`.
77 On error the decorated method may either raise an exception
78 before it returns, or arrange for the error callback to be
79 called with an Exception instance as parameter.
81 `sender_keyword` : str or None
82 If not None, contains the name of a keyword argument to the
83 decorated function, conventionally ``'sender'``. When the
84 method is called, the sender's unique name will be passed as
85 this keyword argument.
87 `path_keyword` : str or None
88 If not None (the default), the decorated method will receive
89 the destination object path as a keyword argument with this
90 name. Normally you already know the object path, but in the
91 case of "fallback paths" you'll usually want to use the object
92 path in the method's implementation.
94 For fallback objects, `rel_path_keyword` (new in 0.82.2) is
95 likely to be more useful.
99 `rel_path_keyword` : str or None
100 If not None (the default), the decorated method will receive
101 the destination object path, relative to the path at which the
102 object was exported, as a keyword argument with this
103 name. For non-fallback objects the relative path will always be
108 `destination_keyword` : str or None
109 If not None (the default), the decorated method will receive
110 the destination bus name as a keyword argument with this name.
111 Included for completeness - you shouldn't need this.
115 `message_keyword` : str or None
116 If not None (the default), the decorated method will receive
117 the `dbus.lowlevel.MethodCallMessage` as a keyword argument
122 `connection_keyword` : str or None
123 If not None (the default), the decorated method will receive
124 the `dbus.connection.Connection` as a keyword argument
125 with this name. This is generally only useful for objects
126 that are available on more than one connection.
130 `utf8_strings` : bool
131 If False (default), D-Bus strings are passed to the decorated
132 method as objects of class dbus.String, a unicode subclass.
134 If True, D-Bus strings are passed to the decorated method
135 as objects of class dbus.UTF8String, a str subclass guaranteed
136 to be encoded in UTF-8.
138 This option does not affect object-paths and signatures, which
139 are always 8-bit strings (str subclass) encoded in ASCII.
144 If False (default), a byte array will be passed to the decorated
145 method as an `Array` (a list subclass) of `Byte` objects.
147 If True, a byte array will be passed to the decorated method as
148 a `ByteArray`, a str subclass. This is usually what you want,
149 but is switched off by default to keep dbus-python's API
154 validate_interface_name(dbus_interface
)
157 args
= inspect
.getargspec(func
)[0]
161 if type(async_callbacks
) != tuple:
162 raise TypeError('async_callbacks must be a tuple of (keyword for return callback, keyword for error callback)')
163 if len(async_callbacks
) != 2:
164 raise ValueError('async_callbacks must be a tuple of (keyword for return callback, keyword for error callback)')
165 args
.remove(async_callbacks
[0])
166 args
.remove(async_callbacks
[1])
169 args
.remove(sender_keyword
)
171 args
.remove(rel_path_keyword
)
173 args
.remove(path_keyword
)
174 if destination_keyword
:
175 args
.remove(destination_keyword
)
177 args
.remove(message_keyword
)
178 if connection_keyword
:
179 args
.remove(connection_keyword
)
182 in_sig
= tuple(Signature(in_signature
))
184 if len(in_sig
) > len(args
):
185 raise ValueError, 'input signature is longer than the number of arguments taken'
186 elif len(in_sig
) < len(args
):
187 raise ValueError, 'input signature is shorter than the number of arguments taken'
189 func
._dbus
_is
_method
= True
190 func
._dbus
_async
_callbacks
= async_callbacks
191 func
._dbus
_interface
= dbus_interface
192 func
._dbus
_in
_signature
= in_signature
193 func
._dbus
_out
_signature
= out_signature
194 func
._dbus
_sender
_keyword
= sender_keyword
195 func
._dbus
_path
_keyword
= path_keyword
196 func
._dbus
_rel
_path
_keyword
= rel_path_keyword
197 func
._dbus
_destination
_keyword
= destination_keyword
198 func
._dbus
_message
_keyword
= message_keyword
199 func
._dbus
_connection
_keyword
= connection_keyword
200 func
._dbus
_args
= args
201 func
._dbus
_get
_args
_options
= {'byte_arrays': byte_arrays
,
202 'utf8_strings': utf8_strings
}
208 def signal(dbus_interface
, signature
=None, path_keyword
=None,
209 rel_path_keyword
=None):
210 """Factory for decorators used to mark methods of a `dbus.service.Object`
211 to emit signals on the D-Bus.
213 Whenever the decorated method is called in Python, after the method
214 body is executed, a signal with the same name as the decorated method,
215 with the given D-Bus interface, will be emitted from this object.
218 `dbus_interface` : str
219 The D-Bus interface whose signal is emitted
221 The signature of the signal in the usual D-Bus notation
223 `path_keyword` : str or None
224 A keyword argument to the decorated method. If not None,
225 that argument will not be emitted as an argument of
226 the signal, and when the signal is emitted, it will appear
227 to come from the object path given by the keyword argument.
229 Note that when calling the decorated method, you must always
230 pass in the object path as a keyword argument, not as a
233 This keyword argument cannot be used on objects where
234 the class attribute ``SUPPORTS_MULTIPLE_OBJECT_PATHS`` is true.
236 :Deprecated: since 0.82.0. Use `rel_path_keyword` instead.
238 `rel_path_keyword` : str or None
239 A keyword argument to the decorated method. If not None,
240 that argument will not be emitted as an argument of
243 When the signal is emitted, if the named keyword argument is given,
244 the signal will appear to come from the object path obtained by
245 appending the keyword argument to the object's object path.
246 This is useful to implement "fallback objects" (objects which
247 own an entire subtree of the object-path tree).
249 If the object is available at more than one object-path on the
250 same or different connections, the signal will be emitted at
251 an appropriate object-path on each connection - for instance,
252 if the object is exported at /abc on connection 1 and at
253 /def and /x/y/z on connection 2, and the keyword argument is
254 /foo, then signals will be emitted from /abc/foo and /def/foo
255 on connection 1, and /x/y/z/foo on connection 2.
259 validate_interface_name(dbus_interface
)
261 if path_keyword
is not None:
262 from warnings
import warn
263 warn(DeprecationWarning('dbus.service.signal::path_keyword has been '
264 'deprecated since dbus-python 0.82.0, and '
265 'will not work on objects that support '
266 'multiple object paths'),
267 DeprecationWarning, stacklevel
=2)
268 if rel_path_keyword
is not None:
269 raise TypeError('dbus.service.signal::path_keyword and '
270 'rel_path_keyword cannot both be used')
273 member_name
= func
.__name
__
274 validate_member_name(member_name
)
276 def emit_signal(self
, *args
, **keywords
):
278 if path_keyword
is not None:
279 if self
.SUPPORTS_MULTIPLE_OBJECT_PATHS
:
280 raise TypeError('path_keyword cannot be used on the '
281 'signals of an object that supports '
282 'multiple object paths')
283 abs_path
= keywords
.pop(path_keyword
, None)
284 if (abs_path
!= self
.__dbus
_object
_path
__ and
285 not self
.__dbus
_object
_path
__.startswith(abs_path
+ '/')):
286 raise ValueError('Path %r is not below %r', abs_path
,
287 self
.__dbus
_object
_path
__)
290 if rel_path_keyword
is not None:
291 rel_path
= keywords
.pop(rel_path_keyword
, None)
293 func(self
, *args
, **keywords
)
295 for location
in self
.locations
:
297 # non-deprecated case
298 if rel_path
is None or rel_path
in ('/', ''):
299 object_path
= location
[1]
301 # will be validated by SignalMessage ctor in a moment
302 object_path
= location
[1] + rel_path
304 object_path
= abs_path
306 message
= SignalMessage(object_path
,
309 message
.append(signature
=signature
, *args
)
311 location
[0].send_message(message
)
314 args
= inspect
.getargspec(func
)[0]
317 for keyword
in rel_path_keyword
, path_keyword
:
318 if keyword
is not None:
322 raise ValueError('function has no argument "%s"' % keyword
)
325 sig
= tuple(Signature(signature
))
327 if len(sig
) > len(args
):
328 raise ValueError, 'signal signature is longer than the number of arguments provided'
329 elif len(sig
) < len(args
):
330 raise ValueError, 'signal signature is shorter than the number of arguments provided'
332 emit_signal
.__name
__ = func
.__name
__
333 emit_signal
.__doc
__ = func
.__doc
__
334 emit_signal
._dbus
_is
_signal
= True
335 emit_signal
._dbus
_interface
= dbus_interface
336 emit_signal
._dbus
_signature
= signature
337 emit_signal
._dbus
_args
= args