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

Source Code for Module prompt.interpreter

  1  ############################################################################### 
  2  #                                                                             # 
  3  # Copyright (C) 2003-2012 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  """The prompt based relax user interface (UI).""" 
 24   
 25  # Dependency check module. 
 26  import dep_check 
 27   
 28  # Python module imports. 
 29  import ansi 
 30  from code import InteractiveConsole 
 31  from math import pi 
 32  from os import F_OK, access, chdir, getcwd, path 
 33  from pydoc import pager 
 34  from re import search 
 35  if dep_check.readline_module: 
 36      import readline 
 37  if dep_check.runpy_module: 
 38      import runpy 
 39  import sys 
 40   
 41  # relax module imports. 
 42  from info import Info_box 
 43  from prompt.command import Ls, Lh, Ll, system 
 44  from prompt.help import _Helper, _Helper_python 
 45  if dep_check.readline_module: 
 46      from prompt.tab_completion import Tab_completion 
 47  from prompt.uf_objects import Class_container, Uf_object 
 48  from relax_errors import AllRelaxErrors, RelaxBinError, RelaxError, RelaxNoneError, RelaxStrError 
 49  from status import Status; status = Status() 
 50  from user_functions.data import Uf_info; uf_info = Uf_info() 
 51   
 52   
 53  # Module variables. 
 54  ################### 
 55   
 56  # The prompts (to change the Python prompt, as well as the function printouts). 
 57  PS1_ORIG = 'relax> ' 
 58  PS2_ORIG = 'relax| ' 
 59  PS3_ORIG = '\n%s' % PS1_ORIG 
 60   
 61  # Coloured text. 
 62  PS1_COLOUR = "%s%s%s" % (ansi.relax_prompt, PS1_ORIG, ansi.end) 
 63  PS2_COLOUR = "%s%s%s" % (ansi.relax_prompt, PS2_ORIG, ansi.end) 
 64  PS3_COLOUR = "\n%s%s%s" % (ansi.relax_prompt, PS1_ORIG, ansi.end) 
 65   
 66   
67 -class Interpreter:
68 - def __init__(self, show_script=True, quit=True, raise_relax_error=False):
69 """The interpreter class. 70 71 @param show_script: If true, the relax will print the script contents prior to 72 executing the script. 73 @type show_script: bool 74 @param quit: If true, the default, then relax will exit after running the 75 run() method. 76 @type quit: bool 77 @param raise_relax_error: If false, the default, then relax will print a nice error 78 message to STDERR, without a traceback, when a RelaxError 79 occurs. This is to make things nicer for the user. 80 @type raise_relax_error: bool 81 """ 82 83 # Place the arguments in the class namespace. 84 self.__show_script = show_script 85 self.__quit_flag = quit 86 self.__raise_relax_error = raise_relax_error 87 88 # Build the intro string. 89 info = Info_box() 90 self.__intro_string = info.intro_text() 91 92 # The prompts (change the Python prompt, as well as the function printouts). 93 if ansi.enable_control_chars(stream=1): 94 self.prompt_colour_on() 95 else: 96 self.prompt_colour_off() 97 98 # Set up the interpreter objects. 99 self._locals = self._setup()
100 101
102 - def _execute_uf(self, *args, **kargs):
103 """Private method for executing the given user function. 104 105 @keyword uf_name: The name of the user function. 106 @type uf_name: str 107 """ 108 109 # Checks. 110 if 'uf_name' not in kargs: 111 raise RelaxError("The user function name argument 'uf_name' has not been supplied.") 112 113 # Process the user function name. 114 uf_name = kargs.pop('uf_name') 115 116 # Split up the name. 117 if search('\.', uf_name): 118 class_name, uf_name = uf_name.split('.') 119 else: 120 class_name = None 121 122 # Get the class object. 123 if class_name: 124 class_obj = self._locals[class_name] 125 126 # Get the user function. 127 if class_name: 128 uf = getattr(class_obj, uf_name) 129 else: 130 uf = self._locals[uf_name] 131 132 # Call the user function. 133 uf(*args, **kargs)
134 135
136 - def _setup(self):
137 """Set up all the interpreter objects. 138 139 All objects are initialised and placed in a dictionary. These will be later placed in different namespaces such as the run() method local namespace. All the user functions and classes will be auto-generated. 140 141 @return: The dictionary of interpreter objects. 142 @rtype: dict 143 """ 144 145 # Initialise the dictionary. 146 objects = {} 147 148 # Python objects. 149 objects['pi'] = pi 150 151 # Import the functions emulating system commands. 152 objects['lh'] = Lh() 153 objects['ll'] = Ll() 154 objects['ls'] = Ls() 155 objects['system'] = system 156 157 # The GPL license. 158 objects['gpl'] = objects['GPL'] = GPL() 159 160 # Builtin interpreter functions. 161 objects['intro_off'] = self.off 162 objects['intro_on'] = self.on 163 objects['exit'] = objects['bye'] = objects['quit'] = objects['q'] = _Exit() 164 165 # Modify the help system. 166 objects['help_python'] = _Helper_python() 167 objects['help'] = _Helper() 168 169 # Add the user function classes. 170 for name, data in uf_info.class_loop(): 171 # Generate a new container. 172 obj = Class_container(name, data.title) 173 174 # Add the object to the local namespace. 175 objects[name] = obj 176 177 # Add the user functions. 178 self._uf_dict = {} 179 for name, data in uf_info.uf_loop(): 180 # Split up the name. 181 if search('\.', name): 182 class_name, uf_name = name.split('.') 183 else: 184 class_name = None 185 186 # Generate a new container. 187 obj = Uf_object(name, title=data.title, kargs=data.kargs, backend=data.backend, desc=data.desc) 188 189 # Get the class object. 190 if class_name: 191 class_obj = objects[class_name] 192 193 # Add the object to the local namespace or user function class. 194 if class_name: 195 setattr(class_obj, uf_name, obj) 196 else: 197 objects[name] = obj 198 199 # Store the user functions by full text name (for faster retrieval). 200 self._uf_dict[name] = obj 201 202 # Return the dictionary. 203 return objects
204 205
206 - def off(self, verbose=True):
207 """Turn the user function introductions off.""" 208 209 status.uf_intro = False 210 211 # Print out. 212 if verbose: 213 print("Echoing of user function calls has been disabled.")
214 215
216 - def on(self, verbose=True):
217 """Turn the user function introductions on.""" 218 219 status.uf_intro = True 220 221 # Print out. 222 if verbose: 223 print("Echoing of user function calls has been enabled.")
224 225
226 - def populate_self(self):
227 """Place all special objects into self.""" 228 229 # Add the interpreter objects to the class namespace. 230 for name in self._locals.keys(): 231 setattr(self, name, self._locals[name])
232 233
234 - def prompt_colour_off(self):
235 """Turn the prompt colouring ANSI escape sequences off.""" 236 237 sys.ps1 = status.ps1 = PS1_ORIG 238 sys.ps2 = status.ps2 = PS2_ORIG 239 sys.ps3 = status.ps3 = PS3_ORIG
240 241
242 - def prompt_colour_on(self):
243 """Turn the prompt colouring ANSI escape sequences off.""" 244 245 sys.ps1 = status.ps1 = PS1_COLOUR 246 sys.ps2 = status.ps2 = PS2_COLOUR 247 sys.ps3 = status.ps3 = PS3_COLOUR
248 249
250 - def run(self, script_file=None):
251 """Run the python interpreter. 252 253 The namespace of this function is the namespace seen inside the interpreter. All user 254 accessible functions, classes, etc, should be placed in this namespace. 255 256 257 @param script_file: The script file to be executed. For the interpreter mode, this 258 should be left as None. 259 @type script_file: None or str 260 """ 261 262 # Add the interpreter objects to the local run namespace. 263 for name in self._locals.keys(): 264 locals()[name] = self._locals[name] 265 266 # Setup tab completion. 267 if dep_check.readline_module: 268 readline.set_completer(Tab_completion(name_space=locals()).finish) 269 readline.set_completer_delims(' \t\n`~!@#$%^&*()=+{}\\|;:",<>/?') 270 readline.parse_and_bind("tab: complete") 271 272 # Execute the script file if given. 273 if script_file: 274 # Turn on the user function intro flag. 275 status.uf_intro = True 276 277 # Run the script. 278 return run_script(intro=self.__intro_string, local=locals(), script_file=script_file, quit=self.__quit_flag, show_script=self.__show_script, raise_relax_error=self.__raise_relax_error) 279 280 # Go to the prompt. 281 else: 282 prompt(intro=self.__intro_string, local=locals())
283 284 285
286 -class _Exit:
287 - def __repr__(self):
288 """Exit the program.""" 289 290 print("Exiting the program.") 291 sys.exit()
292 293
294 -class GPL:
295 """A special object for displaying the GPL license.""" 296
297 - def __repr__(self):
298 """Replacement representation.""" 299 300 # First display the GPL using paging. 301 file = open('docs/COPYING') 302 pager(file.read()) 303 304 # Then return some text to print out. 305 return "The GNU General Public License."
306 307 308
309 -def exec_script(name, globals):
310 """Execute the script.""" 311 312 # Execution lock. 313 status.exec_lock.acquire('script UI', mode='script') 314 315 # The module path. 316 head, tail = path.split(name) 317 script_path = path.join(getcwd(), head) 318 sys.path.append(script_path) 319 320 # Switch directories for nested scripting. 321 if head: 322 orig_dir = getcwd() 323 chdir(head) 324 325 # The module name. 326 module, ext = path.splitext(tail) 327 328 # Check if the script name is ok. 329 if search('\.', module): 330 raise RelaxError("The relax script must not contain the '.' character (except before the extension '*.py').") 331 if ext != '.py': 332 raise RelaxError("The script must have the extension *.py.") 333 334 # Execute the module. 335 try: 336 # Reverse the system path so that the script path is first. 337 sys.path.reverse() 338 339 # Execute the script as a module. 340 if dep_check.runpy_module: 341 runpy.run_module(module, globals) 342 343 # Allow scripts to run under Python <= 2.4. 344 else: 345 exec(compile(open(name).read(), name, 'exec'), globals) 346 347 finally: 348 # Switch back to the original working directory. 349 if head: 350 chdir(orig_dir) 351 352 # Remove the script path. 353 sys.path.reverse() 354 sys.path.pop(sys.path.index(script_path)) 355 356 # Unlock execution if needed. 357 status.exec_lock.release()
358 359
360 -def interact_prompt(self, intro=None, local={}):
361 """Replacement function for 'code.InteractiveConsole.interact'. 362 363 This will enter into the prompt. 364 365 @param intro: The string to print prior to jumping to the prompt mode. 366 @type intro: str 367 @param local: A namespace which will become that of the prompt (i.e. the namespace visible to 368 the user when in the prompt mode). This should be the output of a function such 369 as locals(). 370 @type local: dict 371 """ 372 373 # Print the program introduction. 374 if intro: 375 sys.stdout.write("%s\n" % intro) 376 377 # Ignore SIGINT. 378 #signal.signal(2, 1) 379 380 # Prompt. 381 more = False 382 while True: 383 try: 384 if more: 385 prompt = sys.ps2 386 else: 387 prompt = sys.ps1 388 try: 389 line = self.raw_input(prompt) 390 except EOFError: 391 self.write("\n") 392 break 393 else: 394 more = self.push(line) 395 except KeyboardInterrupt: 396 self.write("\nKeyboardInterrupt\n") 397 self.resetbuffer() 398 more = False
399 400
401 -def interact_script(self, intro=None, local={}, script_file=None, quit=True, show_script=True, raise_relax_error=False):
402 """Replacement function for 'code.InteractiveConsole.interact'. 403 404 This will execute the script file. 405 406 407 @param intro: The string to print prior to jumping to the prompt mode. 408 @type intro: str 409 @param local: A namespace which will become that of the prompt (i.e. the namespace 410 visible to the user when in the prompt mode). This should be the 411 output of a function such as locals(). 412 @type local: dict 413 @param script_file: The script file to be executed. 414 @type script_file: None or str 415 @param quit: If true, the default, then relax will exit after running the script. 416 @type quit: bool 417 @param show_script: If true, the relax will print the script contents prior to executing 418 the script. 419 @type show_script: bool 420 @param raise_relax_error: If false, the default, then a nice error message will be sent to 421 STDERR, without a traceback, when a RelaxError occurs. This is to 422 make things nicer for the user. 423 @type raise_relax_error: bool 424 """ 425 426 # Print the program introduction. 427 if intro: 428 sys.stdout.write("%s\n" % intro) 429 430 # Print the script. 431 if show_script: 432 try: 433 file = open(script_file, 'r') 434 except IOError: 435 try: 436 raise RelaxError("The script file '" + script_file + "' does not exist.") 437 except AllRelaxErrors: 438 instance = sys.exc_info()[1] 439 sys.stdout.write(instance.__str__()) 440 sys.stdout.write("\n") 441 return 442 443 # Coloured text. 444 if ansi.enable_control_chars(stream=1): 445 sys.stdout.write(ansi.script) 446 447 # Print the script. 448 sys.stdout.write("script = " + repr(script_file) + "\n") 449 sys.stdout.write("----------------------------------------------------------------------------------------------------\n") 450 sys.stdout.write(file.read()) 451 sys.stdout.write("----------------------------------------------------------------------------------------------------") 452 453 # End coloured text. 454 if ansi.enable_control_chars(stream=1): 455 sys.stdout.write(ansi.end) 456 457 # Terminating newline. 458 sys.stdout.write("\n") 459 460 # Close the script file handle. 461 file.close() 462 463 # The execution flag. 464 exec_pass = True 465 466 # Execute the script. 467 try: 468 exec_script(script_file, local) 469 470 # Catch ctrl-C. 471 except KeyboardInterrupt: 472 # Unlock execution. 473 status.exec_lock.release() 474 475 # Throw the error. 476 if status.debug: 477 raise 478 479 # Be nicer to the user. 480 else: 481 sys.stderr.write("\nScript execution cancelled.\n") 482 483 # The script failed. 484 exec_pass = False 485 486 # Catch the RelaxErrors. 487 except AllRelaxErrors: 488 instance = sys.exc_info()[1] 489 490 # Unlock execution. 491 status.exec_lock.release() 492 493 # Throw the error. 494 if raise_relax_error: 495 raise 496 497 # Nice output for the user. 498 else: 499 # Print the scary traceback normally hidden from the user. 500 if status.debug: 501 self.showtraceback() 502 503 # Print the RelaxError message line. 504 else: 505 sys.stderr.write(instance.__str__()) 506 507 # The script failed. 508 exec_pass = False 509 510 # Throw all other errors. 511 except: 512 # Unlock execution. 513 status.exec_lock.release() 514 515 # Raise the error. 516 raise 517 518 # Add an empty line to make exiting relax look better. 519 if show_script: 520 sys.stdout.write("\n") 521 522 # Quit relax. 523 # FIXME: need to drop off end of interpreter loop to exit cleanly 524 #if quit: 525 # sys.exit() 526 527 # Return the execution flag. 528 return exec_pass
529 530
531 -def prompt(intro=None, local=None):
532 """Python interpreter emulation. 533 534 This function replaces 'code.interact'. 535 536 537 @param intro: The string to print prior to jumping to the prompt mode. 538 @type intro: str 539 @param local: A namespace which will become that of the prompt (i.e. the namespace visible to 540 the user when in the prompt mode). This should be the output of a function such 541 as locals(). 542 @type local: dict 543 """ 544 545 # Replace the 'InteractiveConsole.interact' and 'InteractiveConsole.runcode' functions. 546 InteractiveConsole.interact = interact_prompt 547 InteractiveConsole.runcode = runcode 548 549 # The console. 550 console = InteractiveConsole(local) 551 console.interact(intro, local)
552 553
554 -def run_script(intro=None, local=None, script_file=None, quit=True, show_script=True, raise_relax_error=False):
555 """Python interpreter emulation. 556 557 This function replaces 'code.interact'. 558 559 560 @param intro: The string to print prior to jumping to the prompt mode. 561 @type intro: str 562 @param local: A namespace which will become that of the prompt (i.e. the namespace 563 visible to the user when in the prompt mode). This should be the 564 output of a function such as locals(). 565 @type local: dict 566 @param script_file: The script file to be executed. 567 @type script_file: None or str 568 @param quit: If true, the default, then relax will exit after running the script. 569 @type quit: bool 570 @param show_script: If true, the relax will print the script contents prior to executing 571 the script. 572 @type show_script: bool 573 @param raise_relax_error: If false, the default, then a nice error message will be sent to 574 STDERR, without a traceback, when a RelaxError occurs. This is to 575 make things nicer for the user. 576 @type raise_relax_error: bool 577 """ 578 579 # Replace the 'InteractiveConsole.interact' and 'InteractiveConsole.runcode' functions. 580 InteractiveConsole.interact = interact_script 581 InteractiveConsole.runcode = runcode 582 583 # The console. 584 console = InteractiveConsole(local) 585 return console.interact(intro, local, script_file, quit, show_script=show_script, raise_relax_error=raise_relax_error)
586 587
588 -def runcode(self, code):
589 """Replacement code for code.InteractiveInterpreter.runcode. 590 591 @param code: The code to execute. 592 @type code: str 593 """ 594 595 try: 596 exec(code, self.locals) 597 except SystemExit: 598 raise 599 except AllRelaxErrors: 600 instance = sys.exc_info()[1] 601 self.write(instance.__str__()) 602 self.write("\n") 603 except: 604 self.showtraceback()
605