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

Source Code for Module prompt.interpreter

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