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

Source Code for Module gui.interpreter

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