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 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 # Force the GUI to yield (to prevent racing, especially in the GUI tests). 171 wx.Yield() 172 173 # Debugging. 174 if status.debug: 175 sys.stdout.write("debug> GUI interpreter: Flushed.\n")
176 177
178 - def join(self):
179 """Wrapper method for the Queue.join() method.""" 180 181 # Call the thread's method. 182 self._interpreter_thread.join()
183 184
185 - def queue(self, uf, *args, **kwds):
186 """Queue up a user function. 187 188 This is a wrapper method for the interpreter thread. 189 190 @param uf: The user function as a string. 191 @type uf: str 192 @param args: The user function arguments. 193 @type args: any arguments 194 @param kwds: The user function keyword arguments. 195 @type kwds: any keyword arguments 196 @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. 197 @rtype: bool 198 """ 199 200 # Debugging. 201 if status.debug: 202 sys.stdout.write("debug> GUI interpreter: Queuing the %s user function for asynchronous execution.\n" % uf) 203 204 # Get the user function. 205 fn = self._get_backend(uf) 206 207 # Call the thread's method. 208 self._interpreter_thread.queue(fn, *args, **kwds) 209 210 # Cannot judge if the user function was successful. 211 return True
212 213 214
215 -class Interpreter_thread(Thread):
216 """The threaded interpreter.""" 217
218 - def __init__(self):
219 """Initialise the object.""" 220 221 # Set up the thread object. 222 Thread.__init__(self) 223 224 # Set the thread to be daemonic so that relax can exit. 225 self.daemon = True 226 227 # Create a queue object for the user function calls. 228 self._queue = Queue() 229 230 # The list of user functions still in the queue. 231 self._uf_list = [] 232 233 # A flag for exiting the thread. 234 self._exit = False
235 236
237 - def empty(self):
238 """Is the queue empty?""" 239 240 # Execution is locked. 241 if status.exec_lock.locked(): 242 return False 243 244 # There are still queued calls. 245 elif len(self._uf_list): 246 return False 247 248 # The queue is empty. 249 else: 250 return True
251 252
253 - def exit(self):
254 """Cause the thread to exit once all currently queued user functions are processed.""" 255 256 # First set the flag. 257 self._exit = True 258 259 # Then queue a dummy user function. 260 self._queue.put([None, None, None])
261 262
263 - def join(self):
264 """Wrapper method for the Queue.join() method.""" 265 266 # Join the queue. 267 self._queue.join()
268 269
270 - def queue(self, fn, *args, **kwds):
271 """Queue up a user function for asynchronous execution. 272 273 @param fn: The user function as a string. 274 @type fn: str 275 @param args: The user function arguments. 276 @type args: any arguments 277 @param kwds: The user function keyword arguments. 278 @type kwds: any keyword arguments 279 """ 280 281 # Add the user function name to the list. 282 self._uf_list.append(repr(fn)) 283 284 # Place the user function and its args onto the queue. 285 self._queue.put([fn, args, kwds])
286 287
288 - def run(self):
289 """Execute the thread.""" 290 291 # Loop until told to exit. 292 while not self._exit: 293 # Get the user function from the queue. 294 fn, args, kwds = self._queue.get() 295 296 # No user function. 297 if fn == None: 298 continue 299 300 # Execution lock. 301 status.exec_lock.acquire('gui', mode='interpreter thread') 302 303 # Execute the user function, catching errors (the nested try-except statements within the try-finally statements are for Python 2.4 and earlier support). 304 try: 305 try: 306 fn(*args, **kwds) 307 308 # Catch all RelaxErrors. 309 except AllRelaxErrors: 310 instance = sys.exc_info()[1] 311 312 # Display a dialog with the error. 313 wx.CallAfter(gui_raise, instance, raise_flag=False) 314 315 # Handle all other errors. 316 except: 317 # Print the exception. 318 print_exc() 319 320 # Release the lock. 321 finally: 322 # Signal that the queue item has been processed. 323 self._queue.task_done() 324 325 # Release the execution lock. 326 status.exec_lock.release() 327 328 # Remove the user function from the list. 329 self._uf_list.pop(self._uf_list.index(repr(fn))) 330 331 # Notify all observers that a user function has completed. 332 status.observers.gui_uf.notify()
333