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