Package Skype4Py :: Module utils
[frames] | no frames]

Source Code for Module Skype4Py.utils

  1  '''Utility functions and classes used internally by Skype4Py. 
  2  ''' 
  3   
  4  import sys 
  5  import weakref 
  6  import threading 
  7  from new import instancemethod 
8 9 10 -def chop(s, n=1, d=None):
11 '''Chops initial words from a string and returns a list of them and the rest of the string. 12 13 @param s: String to chop from. 14 @type s: str or unicode 15 @param n: Number of words to chop. 16 @type n: int 17 @param d: Optional delimeter. Any white-char by default. 18 @type d: str or unicode 19 @return: A list of n first words from the string followed by the rest of the string 20 (C{[w1, w2, ..., wn, rest_of_string]}). 21 @rtype: list of str or unicode 22 ''' 23 24 spl = s.split(d, n) 25 if len(spl) == n: 26 spl.append(s[:0]) 27 if len(spl) != n + 1: 28 raise ValueError('chop: Could not chop %d words from \'%s\'' % (n, s)) 29 return spl
30
31 32 -def args2dict(s):
33 '''Converts a string in 'ARG="value", ARG2="value2"' format into a dictionary. 34 35 @param s: Input string with comma-separated 'ARG="value"' strings. 36 @type s: str or unicode 37 @return: C{{'ARG': 'value'}} dictionary. 38 @rtype: dict 39 ''' 40 41 d = {} 42 while s: 43 t, s = chop(s, 1, '=') 44 if s.startswith('"'): 45 i = 0 46 while True: 47 i = s.find('"', i+1) 48 # XXX How are the double-quotes escaped? The code below implements VisualBasic technique. 49 try: 50 if s[i+1] != '"': 51 break 52 else: 53 i += 1 54 except IndexError: 55 break 56 if i > 0: 57 d[t] = s[1:i] 58 s = s[i+1:] 59 else: 60 d[t] = s 61 break 62 else: 63 i = s.find(', ') 64 if i >= 0: 65 d[t] = s[:i] 66 s = s[i+2:] 67 else: 68 d[t] = s 69 break 70 return d
71
72 73 -def quote(s, always=False):
74 '''Adds double-quotes to string if needed. 75 76 @param s: String to add double-quotes to. 77 @type s: str or unicode 78 @param always: If True, adds quotes even if the input string contains no spaces. 79 @type always: bool 80 @return: If the given string contains spaces or always=True, returns the string enclosed 81 in double-quotes (if it contained quotes too, they are preceded with a backslash). 82 Otherwise returns the string unchnaged. 83 @rtype: str or unicode 84 ''' 85 86 if always or ' ' in s: 87 return '"%s"' % s.replace('"', '\\"') 88 return s
89
90 91 -def esplit(s, d=None):
92 '''Splits a string into words. 93 94 @param s: String to split. 95 @type s: str or unicode 96 @param d: Optional delimeter. Any white-char by default. 97 @type d: str or unicode 98 @return: A list of words or C{[]} if the string was empty. 99 @rtype: list of str or unicode 100 @note: This function works like C{s.split(d)} except that it always returns an 101 empty list instead of C{['']} for empty strings. 102 ''' 103 104 if s: 105 return s.split(d) 106 return []
107
108 109 -def cndexp(condition, truevalue, falsevalue):
110 '''Simulates a conditional expression known from C or Python 2.5+. 111 112 @param condition: Boolean value telling what should be returned. 113 @type condition: bool, see note 114 @param truevalue: Value returned if condition was True. 115 @type truevalue: any 116 @param falsevalue: Value returned if condition was False. 117 @type falsevalue: any 118 @return: Either truevalue or falsevalue depending on condition. 119 @rtype: same as type of truevalue or falsevalue 120 @note: The type of condition parameter can be anything as long as 121 C{bool(condition)} returns a bool value. 122 ''' 123 124 if condition: 125 return truevalue 126 return falsevalue
127
128 129 -class _WeakMethod(object):
130 '''Helper class for WeakCallableRef function (see below). 131 Don't use directly. 132 ''' 133
134 - def __init__(self, method, callback=None):
135 '''__init__. 136 137 @param method: Method to be referenced. 138 @type method: method 139 @param callback: Callback to be called when the method is collected. 140 @type callback: callable 141 ''' 142 self.im_func = method.im_func 143 try: 144 self.weak_im_self = weakref.ref(method.im_self, self._dies) 145 except TypeError: 146 self.weak_im_self = None 147 self.im_class = method.im_class 148 self.callback = callback
149
150 - def __call__(self):
151 if self.weak_im_self: 152 im_self = self.weak_im_self() 153 if im_self == None: 154 return None 155 else: 156 im_self = None 157 return instancemethod(self.im_func, im_self, self.im_class)
158
159 - def __repr__(self):
160 obj = self() 161 objrepr = repr(obj) 162 if obj == None: 163 objrepr = 'dead' 164 return '<weakref at 0x%x; %s>' % (id(self), objrepr)
165 - def _dies(self, ref):
166 # weakref to im_self died 167 self.im_func = self.im_class = None 168 if self.callback != None: 169 self.callback(self)
170
171 -def WeakCallableRef(c, callback=None):
172 '''Creates and returns a new weak reference to a callable object. 173 174 In contrast to weakref.ref() works on all kinds of callables. 175 Usage is same as weakref.ref(). 176 177 @param c: A callable that the weak reference should point at. 178 @type c: callable 179 @param callback: Callback called when the callable is collected (freed). 180 @type callback: callable 181 @return: A weak callable reference. 182 @rtype: weakref 183 ''' 184 185 try: 186 return _WeakMethod(c, callback) 187 except AttributeError: 188 return weakref.ref(c, callback)
189
190 191 -class _EventHandlingThread(threading.Thread):
192 - def __init__(self, name=None):
193 '''__init__. 194 195 @param name: name 196 @type name: unicode 197 ''' 198 threading.Thread.__init__(self, name='%s event handler' % name) 199 self.setDaemon(False) 200 self.lock = threading.Lock() 201 self.queue = []
202
203 - def enqueue(self, target, args, kwargs):
204 '''enqueue. 205 206 @param target: Callable to be called. 207 @type target: callable 208 @param args: Positional arguments for the callable. 209 @type args: tuple 210 @param kwargs: Keyword arguments for the callable. 211 @type kwargs: dict 212 ''' 213 self.queue.append((target, args, kwargs))
214
215 - def run(self):
216 '''Executes all enqueued targets. 217 ''' 218 while True: 219 try: 220 try: 221 self.lock.acquire() 222 h = self.queue[0] 223 del self.queue[0] 224 except IndexError: 225 break 226 finally: 227 self.lock.release() 228 h[0](*h[1], **h[2])
229
230 -class EventHandlingBase(object):
231 '''This class is used as a base by all classes implementing event handlers. 232 233 Look at known subclasses (above in epydoc) to see which classes will allow you to 234 attach your own callables (event handlers) to certain events occuring in them. 235 236 Read the respective classes documentations to learn what events are provided by them. The 237 events are always defined in a class whose name consist of the name of the class it provides 238 events for followed by C{Events}). For example class L{ISkype} provides events defined in 239 L{ISkypeEvents}. The events class is always defined in the same submodule as the main class. 240 241 The events class is just informative. It tells you what events you can assign your event 242 handlers to, when do they occur and what arguments lists should your event handlers 243 accept. 244 245 There are three ways of attaching an event handler to an event. 246 247 1. C{Events} object. 248 249 Use this method if you need to attach many event handlers to many events. 250 251 Write your event handlers as methods of a class. The superclass of your class 252 doesn't matter, Skype4Py will just look for methods with apropriate names. 253 The names of the methods and their arguments lists can be found in respective 254 events classes (see above). 255 256 Pass an instance of this class as the C{Events} argument to the constructor of 257 a class whose events you are interested in. For example:: 258 259 import Skype4Py 260 261 class MySkypeEvents: 262 def UserStatus(self, Status): 263 print 'The status of the user changed' 264 265 skype = Skype4Py.Skype(Events=MySkypeEvents()) 266 267 The C{UserStatus} method will be called when the status of the user currently logged 268 into skype is changed. 269 270 2. C{On...} properties. 271 272 This method lets you use any callables as event handlers. Simply assign them to C{On...} 273 properties (where "C{...}" is the name of the event) of the object whose events you are 274 interested in. For example:: 275 276 import Skype4Py 277 278 def user_status(Status): 279 print 'The status of the user changed' 280 281 skype = Skype4Py.Skype() 282 skype.OnUserStatus = user_status 283 284 The C{user_status} function will be called when the status of the user currently logged 285 into skype is changed. 286 287 The names of the events and their arguments lists should be taken from respective events 288 classes (see above). Note that there is no C{self} argument (which can be seen in the events 289 classes) simply because our event handler is a function, not a method. 290 291 3. C{RegisterEventHandler} / C{UnregisterEventHandler} methods. 292 293 This method, like the second one, also let you use any callables as event handlers. However, 294 it additionally let you assign many event handlers to a single event. 295 296 In this case, you use L{RegisterEventHandler} and L{UnregisterEventHandler} methods 297 of the object whose events you are interested in. For example:: 298 299 import Skype4Py 300 301 def user_status(Status): 302 print 'The status of the user changed' 303 304 skype = Skype4Py.Skype() 305 skype.RegisterEventHandler('UserStatus', user_status) 306 307 The C{user_status} function will be called when the status of the user currently logged 308 into skype is changed. 309 310 The names of the events and their arguments lists should be taken from respective events 311 classes (see above). Note that there is no C{self} argument (which can be seen in the events 312 classes) simply because our event handler is a function, not a method. 313 314 B{Important notes!} 315 316 The event handlers are always called on a separate thread. At any given time, there is at most 317 one handling thread per event type. This means that when a lot of events of the same type are 318 generated at once, handling of an event will start only after the previous one is handled. 319 Handling of events of different types may happen simultaneously. 320 321 In case of second and third method, only weak references to the event handlers are stored. This 322 means that you must make sure that Skype4Py is not the only one having a reference to the callable 323 or else it will be garbage collected and silently removed from Skype4Py's handlers list. On the 324 other hand, it frees you from worrying about cyclic references. 325 ''' 326 327 _EventNames = [] 328
329 - def __init__(self):
330 '''Initializes the object. 331 ''' 332 self._EventHandlerObj = None 333 self._DefaultEventHandlers = {} 334 self._EventHandlers = {} 335 self._EventThreads = {} 336 337 for event in self._EventNames: 338 self._EventHandlers[event] = []
339
340 - def _CallEventHandler(self, Event, *args, **kwargs):
341 '''Calls all event handlers defined for given Event (str), additional parameters 342 will be passed unchanged to event handlers, all event handlers are fired on 343 separate threads. 344 ''' 345 # get list of relevant handlers 346 handlers = dict([(x, x()) for x in self._EventHandlers[Event]]) 347 if None in handlers.values(): 348 # cleanup 349 self._EventHandlers[Event] = list([x[0] for x in handlers.items() if x[1] != None]) 350 handlers = filter(None, handlers.values()) 351 # try the On... handlers 352 try: 353 h = self._DefaultEventHandlers[Event]() 354 if h: 355 handlers.append(h) 356 except KeyError: 357 pass 358 # try the object handlers 359 try: 360 handlers.append(getattr(self._EventHandlerObj, Event)) 361 except AttributeError: 362 pass 363 # if no handlers, leave 364 if not handlers: 365 return 366 # initialize event handling thread if needed 367 if Event in self._EventThreads: 368 t = self._EventThreads[Event] 369 t.lock.acquire() 370 if not self._EventThreads[Event].isAlive(): 371 t = self._EventThreads[Event] = _EventHandlingThread(Event) 372 else: 373 t = self._EventThreads[Event] = _EventHandlingThread(Event) 374 # enqueue handlers in thread 375 for h in handlers: 376 t.enqueue(h, args, kwargs) 377 # start serial event processing 378 try: 379 t.lock.release() 380 except: 381 t.start()
382
383 - def RegisterEventHandler(self, Event, Target):
384 '''Registers any callable as an event handler. 385 386 @param Event: Name of the event. For event names, see the respective C{...Events} class. 387 @type Event: str 388 @param Target: Callable to register as the event handler. 389 @type Target: callable 390 @return: True is callable was successfully registered, False if it was already registered. 391 @rtype: bool 392 @see: L{EventHandlingBase} 393 ''' 394 395 if not callable(Target): 396 raise TypeError('%s is not callable' % repr(Target)) 397 if Event not in self._EventHandlers: 398 raise ValueError('%s is not a valid %s event name' % (Event, self.__class__.__name__)) 399 # get list of relevant handlers 400 handlers = dict([(x, x()) for x in self._EventHandlers[Event]]) 401 if None in handlers.values(): 402 # cleanup 403 self._EventHandlers[Event] = list([x[0] for x in handlers.items() if x[1] != None]) 404 if Target in handlers.values(): 405 return False 406 self._EventHandlers[Event].append(WeakCallableRef(Target)) 407 return True
408
409 - def UnregisterEventHandler(self, Event, Target):
410 '''Unregisters a previously registered event handler (a callable). 411 412 @param Event: Name of the event. For event names, see the respective C{...Events} class. 413 @type Event: str 414 @param Target: Callable to unregister. 415 @type Target: callable 416 @return: True if callable was successfully unregistered, False if it wasn't registered first. 417 @rtype: bool 418 @see: L{EventHandlingBase} 419 ''' 420 421 if not callable(Target): 422 raise TypeError('%s is not callable' % repr(Target)) 423 if Event not in self._EventHandlers: 424 raise ValueError('%s is not a valid %s event name' % (Event, self.__class__.__name__)) 425 # get list of relevant handlers 426 handlers = dict([(x, x()) for x in self._EventHandlers[Event]]) 427 if None in handlers.values(): 428 # cleanup 429 self._EventHandlers[Event] = list([x[0] for x in handlers.items() if x[1] != None]) 430 for wref, trg in handlers.items(): 431 if trg == Target: 432 self._EventHandlers[Event].remove(wref) 433 return True 434 return False
435
436 - def _SetDefaultEventHandler(self, Event, Target):
437 if Target: 438 if not callable(Target): 439 raise TypeError('%s is not callable' % repr(Target)) 440 self._DefaultEventHandlers[Event] = WeakCallableRef(Target) 441 else: 442 try: 443 del self._DefaultEventHandlers[Event] 444 except KeyError: 445 pass
446
447 - def _GetDefaultEventHandler(self, Event):
448 try: 449 return self._DefaultEventHandlers[Event]() 450 except KeyError: 451 pass
452
453 - def _SetEventHandlerObj(self, Obj):
454 '''Registers an object (Obj) as event handler, object should contain methods with names 455 corresponding to event names, only one obj is allowed at a time. 456 ''' 457 self._EventHandlerObj = Obj
458 459 @staticmethod
460 - def __AddEvents_make_event(Event):
461 # TODO: rework to make compatible with cython 462 return property(lambda self: self._GetDefaultEventHandler(Event), 463 lambda self, value: self._SetDefaultEventHandler(Event, value))
464 465 @classmethod
466 - def _AddEvents(cls, klass):
467 '''Adds events to class based on 'klass' attributes.''' 468 for event in dir(klass): 469 if not event.startswith('_'): 470 setattr(cls, 'On%s' % event, cls.__AddEvents_make_event(event)) 471 cls._EventNames.append(event)
472
473 474 -class Cached(object):
475 '''Base class for all cached objects. 476 477 Every object is identified by an Id specified as first parameter of the constructor. 478 Trying to create two objects with same Id yields the same object. Uses weak references 479 to allow the objects to be deleted normally. 480 481 @warning: C{__init__()} is always called, don't use it to prevent initializing an already 482 initialized object. Use C{_Init()} instead, it is called only once. 483 ''' 484 _cache_ = weakref.WeakValueDictionary() 485
486 - def __new__(cls, Id, *args, **kwargs):
487 h = cls, Id 488 try: 489 return cls._cache_[h] 490 except KeyError: 491 o = object.__new__(cls) 492 cls._cache_[h] = o 493 if hasattr(o, '_Init'): 494 o._Init(Id, *args, **kwargs) 495 return o
496
497 - def __copy__(self):
498 return self
499