Package camelot :: Package camelot :: Package view :: Module model_thread
[hide private]
[frames] | no frames]

Source Code for Module camelot.camelot.view.model_thread

  1  #  ============================================================================ 
  2  # 
  3  #  Copyright (C) 2007-2008 Conceptive Engineering bvba. All rights reserved. 
  4  #  www.conceptive.be / project-camelot@conceptive.be 
  5  # 
  6  #  This file is part of the Camelot Library. 
  7  # 
  8  #  This file may be used under the terms of the GNU General Public 
  9  #  License version 2.0 as published by the Free Software Foundation 
 10  #  and appearing in the file LICENSE.GPL included in the packaging of 
 11  #  this file.  Please review the following information to ensure GNU 
 12  #  General Public Licensing requirements will be met: 
 13  #  http://www.trolltech.com/products/qt/opensource.html 
 14  # 
 15  #  If you are unsure which license is appropriate for your use, please 
 16  #  review the following information: 
 17  #  http://www.trolltech.com/products/qt/licensing.html or contact 
 18  #  project-camelot@conceptive.be. 
 19  # 
 20  #  This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE 
 21  #  WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 
 22  # 
 23  #  For use of this library in commercial applications, please contact 
 24  #  project-camelot@conceptive.be 
 25  # 
 26  #  ============================================================================ 
 27   
 28  import os 
 29  import sys 
 30  import logging 
 31  import threading 
 32  import Queue 
 33   
 34  import settings 
 35   
 36  logger = logging.getLogger('camelot.view.model_thread') 
 37   
 38  _model_thread_ = [] 
 39   
40 -class ModelThreadException(Exception):
41 pass
42
43 -def model_function(original_function):
44 """Decorator to ensure a function is only called from within the model 45 thread. If this function is called in another thread, an exception will be 46 thrown 47 """ 48 49 def new_function(*args, **kwargs): 50 if threading.currentThread() != get_model_thread(): 51 logger.error('%s was called outside the model thread' % 52 (original_function.__name__)) 53 raise ModelThreadException() 54 return original_function(*args, **kwargs)
55 56 new_function.__name__ = original_function.__name__ 57 58 return new_function 59
60 -def gui_function(original_function):
61 """Decorator to ensure a function is only called from within the gui thread. 62 If this function is called in another thread, an exception will be thrown 63 64 @todo: now it only checks if the function is not called within the model 65 thread, this is incomplete 66 """ 67 68 def new_function(*args, **kwargs): 69 if threading.currentThread() == get_model_thread(): 70 logger.error('%s was called outside the gui thread' % 71 (original_function.__name__)) 72 raise ModelThreadException() 73 return original_function(*args, **kwargs)
74 75 new_function.__name__ = original_function.__name__ 76 77 return new_function 78 79
80 -class ModelThread(threading.Thread):
81 """Thread in which the model runs, all requests to the model should be 82 posted to the the model thread. 83 84 This class ensures the gui thread doesn't block when the model needs 85 time to complete tasks by providing asynchronous communication between 86 the model thread and the gui thread 87 """ 88
89 - def __init__(self, response_signaler):
90 """@param response_signaler: an object with methods called : 91 92 responseAvailable() : this method will be called when a response is 93 available 94 95 startProcessingRequest(), 96 97 stopProcessingRequest(), 98 """ 99 threading.Thread.__init__(self) 100 101 def setup_thread(): 102 # from libraries.elixir import setup_all 103 # for model in settings.ELIXIR_MODELS: 104 # __import__(model, globals(), locals(), [], -1) 105 # setup_all(create_tables=True) 106 # from model.base import Project 107 108 logger.debug('thread setup finished')
109 110 self._request_queue = Queue.Queue() 111 self._response_queue = Queue.Queue() 112 self._response_signaler = response_signaler 113 self.post(setup_thread) 114 logger.debug('model thread constructed')
115
116 - def run(self):
117 logger.debug('model thread started') 118 try: 119 from settings import setup_model 120 setup_model() 121 logger.debug('start handling requests') 122 while True: 123 new_event = threading.Event() 124 try: 125 (event, request, response, exception) = self._request_queue.get() 126 #self._response_queue.join() 127 logger.debug('start handling request') 128 # import inspect 129 # print inspect.getsource(request) 130 self._response_signaler.startProcessingRequest() 131 result = request() 132 self._response_queue.put((new_event, result, response)) 133 self._request_queue.task_done() 134 self._response_signaler.responseAvailable() 135 self._response_signaler.stopProcessingRequest() 136 logger.debug('finished handling request') 137 event.set() 138 #self._response_queue.join() 139 except Exception, e: 140 logger.error('exception caught in model thread', exc_info=e) 141 self._response_queue.put((new_event, e, exception)) 142 self._request_queue.task_done() 143 self._response_signaler.responseAvailable() 144 self._response_signaler.stopProcessingRequest() 145 event.set() 146 except: 147 logger.error('unhandled exception in model thread') 148 149 except Exception, e: 150 logger.error('exception caught in model thread', exc_info=e) 151 except: 152 logger.error('unhandled exception')
153
154 - def process_responses(self):
155 """Process all responses that are generated by completed requests 156 from the ModelThread. This method should be called from time 157 to time from within the GUI thread. 158 """ 159 try: 160 while True: 161 (event, result, response) = self._response_queue.get_nowait() 162 try: 163 response(result) 164 except Exception, e: 165 logger.error('exception in response', exc_info=e) 166 self._response_queue.task_done() 167 event.set() 168 except Queue.Empty, e: 169 pass
170
171 - def post(self, request, response=lambda result:None, 172 exception=lambda exc:None):
173 """Post a request to the model thread, request should be 174 a function that takes no arguments. The request function 175 will be called within the model thread. When the request 176 is finished, on first occasion, the response function will be 177 called within the gui thread. The response function takes as 178 arguments, the results of the request function. 179 @param request: function to be called within the model thread 180 @param response: function to be called within the gui thread, when 181 the request function is finished, the response function takes 182 as its argument the result of the request function 183 @param exception: function to be called in case of an exception in the 184 request function 185 @return a threading Event object which will be set to True when the 186 request function is finished and the response has been put on the queue 187 """ 188 event = threading.Event() 189 self._request_queue.put_nowait((event, request, response, exception)) 190 return event
191
192 - def post_and_block(self, request):
193 """Post a request tot the model thread, block until it is finished, and 194 then return it results. This function only exists for testing purposes, 195 it should never be used from within the gui thread 196 """ 197 # make sure there are no responses in the queue 198 self.process_responses() 199 results = [] 200 201 def re_raise(exc): 202 raise exc
203 204 event = self.post(request, 205 lambda result:results.append(result), 206 exception=re_raise) 207 event.wait() 208 self.process_responses() 209 return results[-1] 210
211 -def construct_model_thread(*args, **kwargs):
212 _model_thread_.append(ModelThread(*args, **kwargs))
213
214 -def get_model_thread():
215 return _model_thread_[0]
216