root/gaphor/tags/gaphor-0.12.0/gaphor/misc/gidlethread.py

Revision 1899, 5.9 kB (checked in by wrobe..@pld-linux.org, 1 year ago)

- typo fixed

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1 # vim:sw=4:et:
2 """This module contains some helpers that can be used to execute generator
3 functions in the GObject main loop.
4
5 This module provided the following classes:
6 GIdleThread - Thread like behavior for generators in a main loop
7 Queue - A simple queue implementation suitable for use with GIdleThread
8
9 Exceptions:
10 QueueEmpty - raised when one tried to get a value of an empty queue
11 QueueFull - raised when the queue reaches it's max size and the oldest item
12             may not be disposed.
13 """
14
15 import sys
16 import gobject
17 import time
18 import traceback
19
20 class GIdleThread(object):
21     """This is a pseudo-"thread" for use with the GTK+ main loop.
22
23     This class does act a bit like a thread, all code is executed in
24     the callers thread though. The provided function should be a generator
25     (or iterator).
26
27     It can be started with start(). While the "thread" is running is_alive()
28     can be called to see if it's alive. wait([timeout]) will wait till the
29     generator is finished, or timeout seconds.
30     
31     If an exception is raised from within the generator, it is stored in
32     the exc_info property. Execution of the generator is finished. The
33     exc_info property contains a tuple (exc_type, exc_value, exc_traceback),
34     see sys.exc_info() for details.
35
36     Note that this routine runs in the current thread, so there is no need
37     for nasty locking schemes.
38
39     Example (runs a counter through the GLib main loop routine):
40     >>> def counter(max):
41     ...     for x in xrange(max):
42     ...         yield x
43     >>> t = GIdleThread(counter(123))
44     >>> t.start()
45     >>> while t.is_alive():
46     ...     main.iteration(False)
47     """
48
49     def __init__(self, generator, queue=None):
50         assert hasattr(generator, 'next'), 'The generator should be an iterator'
51         self._generator = generator
52         self._queue = queue
53         self._idle_id = 0
54         self._error = None
55         self._exc_info = (None, None, None)
56
57     def start(self, priority=gobject.PRIORITY_LOW):
58         """Start the generator. Default priority is low, so screen updates
59         will be allowed to happen.
60         """
61         idle_id = gobject.idle_add(self.__generator_executer,
62                                    priority=priority)
63         self._idle_id = idle_id
64         return idle_id
65
66     def wait(self, timeout=0):
67         """Wait until the corouine is finished or return after timeout seconds.
68         This is achieved by running the GTK+ main loop.
69         """
70         clock = time.clock
71         start_time = clock()
72         main = gobject.main_context_default()
73         while self.is_alive():
74             main.iteration(False)
75             if timeout and (clock() - start_time >= timeout):
76                 return
77
78     def interrupt(self):
79         """Force the generator to stop running.
80         """
81         if self.is_alive():
82             gobject.source_remove(self._idle_id)
83             self._idle_id = 0
84
85     def is_alive(self):
86         """Returns True if the generator is still running.
87         """
88         return self._idle_id != 0
89
90     error = property(lambda self: self._exc_info[0],
91                      doc="Return a possible exception that had occured "\
92                          "during execution of the generator")
93
94     exc_info = property(lambda self: self._exc_info,
95                      doc="Return a exception information as provided by "\
96                          "sys.exc_info()")
97
98     def __generator_executer(self):
99         try:
100             result = self._generator.next()
101             if self._queue:
102                 try:
103                     self._queue.put(result)
104                 except QueueFull:
105                     self.wait(0.5)
106                     # If this doesn't work...
107                     self._queue.put(result)
108             return True
109         except StopIteration:
110             self._idle_id = 0
111             return False
112         except:
113             self._exc_info = sys.exc_info()
114             #traceback.print_exc()
115             self._idle_id = 0
116             return False
117
118
119 class QueueEmpty(Exception):
120     """Exception raised whenever the queue is empty and someone tries to fetch
121     a value.
122     """
123     pass
124
125
126 class QueueFull(Exception):
127     """Exception raised when the queue is full and the oldest item may not be
128     disposed.
129     """
130     pass
131
132
133 class Queue(object):
134     """A FIFO queue. If the queue has a max size, the oldest item on the
135     queue is dropped if that size id exceeded.
136     """
137
138     def __init__(self, size=0, dispose_oldest=True):
139         self._queue = []
140         self._size = size
141         self._dispose_oldest = dispose_oldest
142
143     def put(self, item):
144         """Put item on the queue. If the queue size is limited ...
145         """
146         if self._size > 0 and len(self._queue) >= self._size:
147             if self._dispose_oldest:
148                 self.get()
149             else:
150                 raise QueueFull
151
152         self._queue.insert(0, item)
153
154     def get(self):
155         """Get the oldest item off the queue.
156         QueueEmpty is raised if no items are left on the queue.
157         """
158         try:
159             return self._queue.pop()
160         except IndexError:
161             raise QueueEmpty
162
163
164 if __name__ == '__main__':
165     def counter(max):
166         for i in range(max):
167             yield i
168
169     def shower(queue):
170         # Never stop reading the queue:
171         while True:
172             try:
173                 cnt = queue.get()
174                 print 'cnt =', cnt
175             except QueueEmpty:
176                 pass
177             yield None
178
179     print 'Test 1: (should print range 0..22)'
180     queue = Queue()
181     c = GIdleThread(counter(23), queue)
182     s = GIdleThread(shower(queue))
183    
184     main = gobject.main_context_default()
185     c.start()
186     s.start()
187     s.wait(2)
188
189     print 'Test 2: (should only print 22)'
190     queue = Queue(size=1)
191     c = GIdleThread(counter(23), queue)
192     s = GIdleThread(shower(queue))
193    
194     main = gobject.main_context_default()
195     c.start(priority=gobject.PRIORITY_DEFAULT)
196     s.start()
197     s.wait(3)
Note: See TracBrowser for help on using the browser.