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

Source Code for Module prompt.interpreter

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