Package gui :: Module interpreter
[hide private]
[frames] | no frames]

Source Code for Module gui.interpreter

  1  ############################################################################### 
  2  #                                                                             # 
  3  # Copyright (C) 2011-2014 Edward d'Auvergne                                   # 
  4  #                                                                             # 
  5  # This file is part of the program relax (http://www.nmr-relax.com).          # 
  6  #                                                                             # 
  7  # This program is free software: you can redistribute it and/or modify        # 
  8  # it under the terms of the GNU General Public License as published by        # 
  9  # the Free Software Foundation, either version 3 of the License, or           # 
 10  # (at your option) any later version.                                         # 
 11  #                                                                             # 
 12  # This program is distributed in the hope that it will be useful,             # 
 13  # but WITHOUT ANY WARRANTY; without even the implied warranty of              # 
 14  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               # 
 15  # GNU General Public License for more details.                                # 
 16  #                                                                             # 
 17  # You should have received a copy of the GNU General Public License           # 
 18  # along with this program.  If not, see <http://www.gnu.org/licenses/>.       # 
 19  #                                                                             # 
 20  ############################################################################### 
 21   
 22  # Module docstring. 
 23  """A threaded version of the relax interpreter for use by the GUI.""" 
 24   
 25  # Python module imports. 
 26  import sys 
 27  from threading import Thread 
 28  from time import sleep 
 29  from traceback import print_exc 
 30  import wx 
 31   
 32  # relax module imports. 
 33  from gui.errors import gui_raise 
 34  from lib.compat import Queue 
 35  from lib.errors import AllRelaxErrors 
 36  from prompt import interpreter 
 37  from status import Status; status = Status() 
 38  from user_functions.data import Uf_info; uf_info = Uf_info() 
 39   
 40   
41 -class Interpreter(object):
42 """The threaded interpreter.""" 43 44 # Class variable for storing the class instance (for the singleton). 45 _instance = None 46
47 - def __new__(self, *args, **kargs):
48 """Replacement method for implementing the singleton design pattern.""" 49 50 # First instantiation. 51 if self._instance is None: 52 # Instantiate. 53 self._instance = object.__new__(self, *args, **kargs) 54 55 # Load a copy of the relax interpreter. 56 self._instance._interpreter = interpreter.Interpreter(show_script=False, raise_relax_error=True) 57 self._instance._interpreter.populate_self() 58 self._instance._interpreter.on(verbose=False) 59 60 # Start the interpreter thread for asynchronous operations. 61 self._instance._interpreter_thread = Interpreter_thread() 62 self._instance._interpreter_thread.start() 63 64 # Hack to turn off ANSI escape characters in GUI mode. 65 self._instance._interpreter.prompt_colour_off() 66 67 # Already instantiated, so return the instance. 68 return self._instance
69 70
71 - def _get_backend(self, uf):
72 """Return the user function object corresponding to the given string. 73 74 @param uf: The name of the user function. 75 @type uf: str 76 @return: The user function object. 77 @rtype: func 78 """ 79 80 # Get the user function info object. 81 info = uf_info.get_uf(uf) 82 83 # Return the backend. 84 return info.backend
85 86
87 - def apply(self, uf, *args, **kwds):
88 """Apply a user function for synchronous execution. 89 90 @param uf: The user function as a string. 91 @type uf: str 92 @param args: The user function arguments. 93 @type args: any arguments 94 @param kwds: The user function keyword arguments. 95 @type kwds: any keyword arguments 96 @return: Whether the user function was successfully applied or not. 97 @rtype: bool 98 """ 99 100 # Debugging. 101 if status.debug: 102 sys.stdout.write("debug> GUI interpreter: Applying the %s user function for synchronous execution.\n" % uf) 103 104 # Get the user function backend. 105 fn = self._get_backend(uf) 106 107 # Execute the user function. 108 try: 109 apply(fn, args, kwds) 110 111 # Catch all RelaxErrors. 112 except AllRelaxErrors: 113 instance = sys.exc_info()[1] 114 115 # Display a dialog with the error. 116 gui_raise(instance, raise_flag=False) 117 118 # Return as a failure. 119 return False 120 121 # Notify all observers that a user function has completed. 122 status.observers.gui_uf.notify() 123 124 # Return success. 125 return True
126 127
128 - def empty(self):
129 """Determine if the interpreter thread queue is empty. 130 131 This is a wrapper method for the thread method. 132 """ 133 134 # Return the queue empty state. 135 return self._interpreter_thread.empty()
136 137
138 - def exit(self):
139 """Cause the thread to exit once all currently queued user functions are processed. 140 141 This is a wrapper method for the thread method. 142 """ 143 144 # Call the thread's method. 145 return self._interpreter_thread.exit()
146 147
148 - def flush(self):
149 """Return only once the queue is empty. 150 151 This is a wrapper method for the interpreter thread. 152 """ 153 154 # Debugging. 155 if status.debug: 156 sys.stdout.write("debug> GUI interpreter: Flushing.\n") 157 158 # Wait a little while to prevent races with the reading of the queue. 159 sleep(0.05) 160 161 # Loop until empty. 162 while not self._interpreter_thread.empty(): 163 # Wait a bit for the queue to empty. 164 sleep(0.05) 165 166 # Wait until execution is complete. 167 while status.exec_lock.locked(): 168 sleep(0.1) 169 170 # Debugging. 171 if status.debug: 172 sys.stdout.write("debug> GUI interpreter: Flushed.\n")
173 174
175 - def join(self):
176 """Wrapper method for the Queue.join() method.""" 177 178 # Call the thread's method. 179 self._interpreter_thread.join()
180 181
182 - def queue(self, uf, *args, **kwds):
183 """Queue up a user function. 184 185 This is a wrapper method for the interpreter thread. 186 187 @param uf: The user function as a string. 188 @type uf: str 189 @param args: The user function arguments. 190 @type args: any arguments 191 @param kwds: The user function keyword arguments. 192 @type kwds: any keyword arguments 193 @return: Whether the user function was successfully applied or not (though as this is asynchronous, this cannot be checked so True will always be returned. 194 @rtype: bool 195 """ 196 197 # Debugging. 198 if status.debug: 199 sys.stdout.write("debug> GUI interpreter: Queuing the %s user function for asynchronous execution.\n" % uf) 200 201 # Get the user function. 202 fn = self._get_backend(uf) 203 204 # Call the thread's method. 205 self._interpreter_thread.queue(fn, *args, **kwds) 206 207 # Cannot judge if the user function was successful. 208 return True
209 210 211
212 -class Interpreter_thread(Thread):
213 """The threaded interpreter.""" 214
215 - def __init__(self):
216 """Initialise the object.""" 217 218 # Set up the thread object. 219 Thread.__init__(self) 220 221 # Set the thread to be daemonic so that relax can exit. 222 self.daemon = True 223 224 # Create a queue object for the user function calls. 225 self._queue = Queue() 226 227 # The list of user functions still in the queue. 228 self._uf_list = [] 229 230 # A flag for exiting the thread. 231 self._exit = False
232 233
234 - def empty(self):
235 """Is the queue empty?""" 236 237 # Execution is locked. 238 if status.exec_lock.locked(): 239 return False 240 241 # There are still queued calls. 242 elif len(self._uf_list): 243 return False 244 245 # The queue is empty. 246 else: 247 return True
248 249
250 - def exit(self):
251 """Cause the thread to exit once all currently queued user functions are processed.""" 252 253 # First set the flag. 254 self._exit = True 255 256 # Then queue a dummy user function. 257 self._queue.put([None, None, None])
258 259
260 - def join(self):
261 """Wrapper method for the Queue.join() method.""" 262 263 # Join the queue. 264 self._queue.join()
265 266
267 - def queue(self, fn, *args, **kwds):
268 """Queue up a user function for asynchronous execution. 269 270 @param fn: The user function as a string. 271 @type fn: str 272 @param args: The user function arguments. 273 @type args: any arguments 274 @param kwds: The user function keyword arguments. 275 @type kwds: any keyword arguments 276 """ 277 278 # Add the user function name to the list. 279 self._uf_list.append(repr(fn)) 280 281 # Place the user function and its args onto the queue. 282 self._queue.put([fn, args, kwds])
283 284
285 - def run(self):
286 """Execute the thread.""" 287 288 # Loop until told to exit. 289 while not self._exit: 290 # Get the user function from the queue. 291 fn, args, kwds = self._queue.get() 292 293 # No user function. 294 if fn == None: 295 continue 296 297 # Execution lock. 298 status.exec_lock.acquire('gui', mode='interpreter thread') 299 300 # Execute the user function, catching errors (the nested try-except statements within the try-finally statements are for Python 2.4 and earlier support). 301 try: 302 try: 303 apply(fn, args, kwds) 304 305 # Catch all RelaxErrors. 306 except AllRelaxErrors: 307 instance = sys.exc_info()[1] 308 309 # Display a dialog with the error. 310 wx.CallAfter(gui_raise, instance, raise_flag=False) 311 312 # Handle all other errors. 313 except: 314 # Print the exception. 315 print_exc() 316 317 # Release the lock. 318 finally: 319 # Signal that the queue item has been processed. 320 self._queue.task_done() 321 322 # Release the execution lock. 323 status.exec_lock.release() 324 325 # Remove the user function from the list. 326 self._uf_list.pop(self._uf_list.index(repr(fn))) 327 328 # Notify all observers that a user function has completed. 329 status.observers.gui_uf.notify()
330