Package dbus :: Module decorators
[hide private]
[frames] | no frames]

Source Code for Module dbus.decorators

  1  """Service-side D-Bus decorators.""" 
  2   
  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/> 
  7  # 
  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: 
 15  # 
 16  # The above copyright notice and this permission notice shall be 
 17  # included in all copies or substantial portions of the Software. 
 18  # 
 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. 
 27   
 28  __all__ = ('method', 'signal') 
 29  __docformat__ = 'restructuredtext' 
 30   
 31  import inspect 
 32   
 33  from dbus import validate_interface_name, Signature, validate_member_name 
 34  from dbus.lowlevel import SignalMessage 
 35  from dbus.exceptions import DBusException 
 36   
 37   
38 -def method(dbus_interface, in_signature=None, out_signature=None, 39 async_callbacks=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. 46 47 The decorated method will be exported over D-Bus as the method of the 48 same name on the given D-Bus interface. 49 50 :Parameters: 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 55 D-Bus notation 56 `out_signature` : str or None 57 If not None, the signature of the return value in the usual 58 D-Bus notation 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: 63 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). 67 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. 72 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`. 76 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. 80 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. 86 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. 93 94 For fallback objects, `rel_path_keyword` (new in 0.82.2) is 95 likely to be more useful. 96 97 :Since: 0.80.0? 98 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 104 '/'. 105 106 :Since: 0.82.2 107 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. 112 113 :Since: 0.80.0? 114 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 118 with this name. 119 120 :Since: 0.80.0? 121 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. 127 128 :Since: 0.82.0 129 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. 133 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. 137 138 This option does not affect object-paths and signatures, which 139 are always 8-bit strings (str subclass) encoded in ASCII. 140 141 :Since: 0.80.0 142 143 `byte_arrays` : bool 144 If False (default), a byte array will be passed to the decorated 145 method as an `Array` (a list subclass) of `Byte` objects. 146 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 150 consistent. 151 152 :Since: 0.80.0 153 """ 154 validate_interface_name(dbus_interface) 155 156 def decorator(func): 157 args = inspect.getargspec(func)[0] 158 args.pop(0) 159 160 if async_callbacks: 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]) 167 168 if sender_keyword: 169 args.remove(sender_keyword) 170 if rel_path_keyword: 171 args.remove(rel_path_keyword) 172 if path_keyword: 173 args.remove(path_keyword) 174 if destination_keyword: 175 args.remove(destination_keyword) 176 if message_keyword: 177 args.remove(message_keyword) 178 if connection_keyword: 179 args.remove(connection_keyword) 180 181 if in_signature: 182 in_sig = tuple(Signature(in_signature)) 183 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' 188 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} 203 return func
204 205 return decorator 206 207
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. 212 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. 216 217 :Parameters: 218 `dbus_interface` : str 219 The D-Bus interface whose signal is emitted 220 `signature` : str 221 The signature of the signal in the usual D-Bus notation 222 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. 228 229 Note that when calling the decorated method, you must always 230 pass in the object path as a keyword argument, not as a 231 positional argument. 232 233 This keyword argument cannot be used on objects where 234 the class attribute ``SUPPORTS_MULTIPLE_OBJECT_PATHS`` is true. 235 236 :Deprecated: since 0.82.0. Use `rel_path_keyword` instead. 237 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 241 the signal. 242 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). 248 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. 256 257 :Since: 0.82.0 258 """ 259 validate_interface_name(dbus_interface) 260 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') 271 272 def decorator(func): 273 member_name = func.__name__ 274 validate_member_name(member_name) 275 276 def emit_signal(self, *args, **keywords): 277 abs_path = None 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__) 288 289 rel_path = None 290 if rel_path_keyword is not None: 291 rel_path = keywords.pop(rel_path_keyword, None) 292 293 func(self, *args, **keywords) 294 295 for location in self.locations: 296 if abs_path is None: 297 # non-deprecated case 298 if rel_path is None or rel_path in ('/', ''): 299 object_path = location[1] 300 else: 301 # will be validated by SignalMessage ctor in a moment 302 object_path = location[1] + rel_path 303 else: 304 object_path = abs_path 305 306 message = SignalMessage(object_path, 307 dbus_interface, 308 member_name) 309 message.append(signature=signature, *args) 310 311 location[0].send_message(message)
312 # end emit_signal 313 314 args = inspect.getargspec(func)[0] 315 args.pop(0) 316 317 for keyword in rel_path_keyword, path_keyword: 318 if keyword is not None: 319 try: 320 args.remove(keyword) 321 except ValueError: 322 raise ValueError('function has no argument "%s"' % keyword) 323 324 if signature: 325 sig = tuple(Signature(signature)) 326 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' 331 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 338 return emit_signal 339 340 return decorator 341