Fixing a bug referencing a non-existent variable
[theonering] / src / util / go_utils.py
1 #!/usr/bin/env python
2
3 from __future__ import with_statement
4
5 import time
6 import functools
7 import logging
8
9 import gobject
10
11 import misc
12
13
14 _moduleLogger = logging.getLogger("go_utils")
15
16
17 def make_idler(func):
18         """
19         Decorator that makes a generator-function into a function that will continue execution on next call
20         """
21         a = []
22
23         @functools.wraps(func)
24         def decorated_func(*args, **kwds):
25                 if not a:
26                         a.append(func(*args, **kwds))
27                 try:
28                         a[0].next()
29                         return True
30                 except StopIteration:
31                         del a[:]
32                         return False
33
34         return decorated_func
35
36
37 def async(func):
38         """
39         Make a function mainloop friendly. the function will be called at the
40         next mainloop idle state.
41
42         >>> import misc
43         >>> misc.validate_decorator(async)
44         """
45
46         @functools.wraps(func)
47         def new_function(*args, **kwargs):
48
49                 def async_function():
50                         func(*args, **kwargs)
51                         return False
52
53                 gobject.idle_add(async_function)
54
55         return new_function
56
57
58 class Async(object):
59
60         def __init__(self, func, once = True):
61                 self.__func = func
62                 self.__idleId = None
63                 self.__once = once
64
65         def start(self):
66                 assert self.__idleId is None
67                 if self.__once:
68                         self.__idleId = gobject.idle_add(self._on_once)
69                 else:
70                         self.__idleId = gobject.idle_add(self.__func)
71
72         def is_running(self):
73                 return self.__idleId is not None
74
75         def cancel(self):
76                 if self.__idleId is not None:
77                         gobject.source_remove(self.__idleId)
78                         self.__idleId = None
79
80         def __call__(self):
81                 return self.start()
82
83         @misc.log_exception(_moduleLogger)
84         def _on_once(self):
85                 self.cancel()
86                 try:
87                         self.__func()
88                 finally:
89                         return False
90
91
92 class Timeout(object):
93
94         def __init__(self, func):
95                 self.__func = func
96                 self.__timeoutId = None
97
98         def start(self, **kwds):
99                 assert self.__timeoutId is None
100
101                 assert len(kwds) == 1
102                 timeoutInSeconds = kwds["seconds"]
103                 assert 0 <= timeoutInSeconds
104                 if timeoutInSeconds == 0:
105                         self.__timeoutId = gobject.idle_add(self._on_once)
106                 else:
107                         timeout_add_seconds(timeoutInSeconds, self._on_once)
108
109         def is_running(self):
110                 return self.__timeoutId is not None
111
112         def cancel(self):
113                 if self.__timeoutId is not None:
114                         gobject.source_remove(self.__timeoutId)
115                         self.__timeoutId = None
116
117         def __call__(self, **kwds):
118                 return self.start(**kwds)
119
120         @misc.log_exception(_moduleLogger)
121         def _on_once(self):
122                 self.cancel()
123                 try:
124                         self.__func()
125                 finally:
126                         return False
127
128
129 def throttled(minDelay, queue):
130         """
131         Throttle the calls to a function by queueing all the calls that happen
132         before the minimum delay
133
134         >>> import misc
135         >>> import Queue
136         >>> misc.validate_decorator(throttled(0, Queue.Queue()))
137         """
138
139         def actual_decorator(func):
140
141                 lastCallTime = [None]
142
143                 def process_queue():
144                         if 0 < len(queue):
145                                 func, args, kwargs = queue.pop(0)
146                                 lastCallTime[0] = time.time() * 1000
147                                 func(*args, **kwargs)
148                         return False
149
150                 @functools.wraps(func)
151                 def new_function(*args, **kwargs):
152                         now = time.time() * 1000
153                         if (
154                                 lastCallTime[0] is None or
155                                 (now - lastCallTime >= minDelay)
156                         ):
157                                 lastCallTime[0] = now
158                                 func(*args, **kwargs)
159                         else:
160                                 queue.append((func, args, kwargs))
161                                 lastCallDelta = now - lastCallTime[0]
162                                 processQueueTimeout = int(minDelay * len(queue) - lastCallDelta)
163                                 gobject.timeout_add(processQueueTimeout, process_queue)
164
165                 return new_function
166
167         return actual_decorator
168
169
170 def _old_timeout_add_seconds(timeout, callback):
171         return gobject.timeout_add(timeout * 1000, callback)
172
173
174 def _timeout_add_seconds(timeout, callback):
175         return gobject.timeout_add_seconds(timeout, callback)
176
177
178 try:
179         gobject.timeout_add_seconds
180         timeout_add_seconds = _timeout_add_seconds
181 except AttributeError:
182         timeout_add_seconds = _old_timeout_add_seconds