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
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
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
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
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
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
160 obj = self()
161 objrepr = repr(obj)
162 if obj == None:
163 objrepr = 'dead'
164 return '<weakref at 0x%x; %s>' % (id(self), objrepr)
166
167 self.im_func = self.im_class = None
168 if self.callback != None:
169 self.callback(self)
170
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
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
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
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
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
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
346 handlers = dict([(x, x()) for x in self._EventHandlers[Event]])
347 if None in handlers.values():
348
349 self._EventHandlers[Event] = list([x[0] for x in handlers.items() if x[1] != None])
350 handlers = filter(None, handlers.values())
351
352 try:
353 h = self._DefaultEventHandlers[Event]()
354 if h:
355 handlers.append(h)
356 except KeyError:
357 pass
358
359 try:
360 handlers.append(getattr(self._EventHandlerObj, Event))
361 except AttributeError:
362 pass
363
364 if not handlers:
365 return
366
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
375 for h in handlers:
376 t.enqueue(h, args, kwargs)
377
378 try:
379 t.lock.release()
380 except:
381 t.start()
382
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
400 handlers = dict([(x, x()) for x in self._EventHandlers[Event]])
401 if None in handlers.values():
402
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
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
426 handlers = dict([(x, x()) for x in self._EventHandlers[Event]])
427 if None in handlers.values():
428
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
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
448 try:
449 return self._DefaultEventHandlers[Event]()
450 except KeyError:
451 pass
452
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
461
462 return property(lambda self: self._GetDefaultEventHandler(Event),
463 lambda self, value: self._SetDefaultEventHandler(Event, value))
464
465 @classmethod
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
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
499