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 # The module name. 332 module, ext = path.splitext(tail) 333 334 # Check if the script name is ok. 335 if search('\.', module): 336 raise RelaxError("The relax script must not contain the '.' character (except before the extension '*.py').") 337 if ext != '.py': 338 raise RelaxError("The script must have the extension *.py.") 339 340 # Execute the module. 341 try: 342 # Reverse the system path so that the script path is first. 343 sys.path.reverse() 344 345 # Execute the script as a module. 346 if dep_check.runpy_module: 347 runpy.run_module(module, globals) 348 349 # Allow scripts to run under Python <= 2.4. 350 else: 351 exec(compile(open(name).read(), name, 'exec'), globals) 352 353 finally: 354 # Remove the script path. 355 sys.path.reverse() 356 sys.path.pop(sys.path.index(script_path)) 357 358 # Unlock execution if needed. 359 status.exec_lock.release()
360 361
362 -def interact_prompt(self, intro=None, local={}):
363 """Replacement function for 'code.InteractiveConsole.interact'. 364 365 This will enter into the prompt. 366 367 @param intro: The string to print prior to jumping to the prompt mode. 368 @type intro: str 369 @param local: A namespace which will become that of the prompt (i.e. the namespace visible to 370 the user when in the prompt mode). This should be the output of a function such 371 as locals(). 372 @type local: dict 373 """ 374 375 # Print the program introduction. 376 if intro: 377 sys.stdout.write("%s\n" % intro) 378 379 # Ignore SIGINT. 380 #signal.signal(2, 1) 381 382 # Prompt. 383 more = False 384 while True: 385 try: 386 if more: 387 prompt = sys.ps2 388 else: 389 prompt = sys.ps1 390 try: 391 line = self.raw_input(prompt) 392 except EOFError: 393 self.write("\n") 394 break 395 else: 396 more = self.push(line) 397 except KeyboardInterrupt: 398 self.write("\nKeyboardInterrupt\n") 399 self.resetbuffer() 400 more = False
401 402
403 -def interact_script(self, intro=None, local={}, script_file=None, show_script=True, raise_relax_error=False):
404 """Replacement function for 'code.InteractiveConsole.interact'. 405 406 This will execute the script file. 407 408 409 @param intro: The string to print prior to jumping to the prompt mode. 410 @type intro: str 411 @param local: A namespace which will become that of the prompt (i.e. the namespace 412 visible to the user when in the prompt mode). This should be the 413 output of a function such as locals(). 414 @type local: dict 415 @param script_file: The script file to be executed. 416 @type script_file: None or str 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 # Throw the error. 473 if status.debug: 474 raise 475 476 # Be nicer to the user. 477 else: 478 sys.stderr.write("\nScript execution cancelled.\n") 479 480 # The script failed. 481 exec_pass = False 482 483 # Catch the RelaxErrors. 484 except AllRelaxErrors: 485 instance = sys.exc_info()[1] 486 487 # Throw the error. 488 if raise_relax_error: 489 raise 490 491 # Nice output for the user. 492 else: 493 # Print the scary traceback normally hidden from the user. 494 if status.debug or status.traceback: 495 self.showtraceback() 496 497 # Print the RelaxError message line. 498 else: 499 sys.stderr.write(instance.__str__()) 500 501 # The script failed. 502 exec_pass = False 503 504 # Throw all other errors. 505 except: 506 # Raise the error. 507 raise 508 509 # Add an empty line to make exiting relax look better. 510 if show_script: 511 sys.stdout.write("\n") 512 513 # Return the execution flag. 514 return exec_pass
515 516
517 -def prompt(intro=None, local=None):
518 """Python interpreter emulation. 519 520 This function replaces 'code.interact'. 521 522 523 @param intro: The string to print prior to jumping to the prompt mode. 524 @type intro: str 525 @param local: A namespace which will become that of the prompt (i.e. the namespace visible to 526 the user when in the prompt mode). This should be the output of a function such 527 as locals(). 528 @type local: dict 529 """ 530 531 # Replace the 'InteractiveConsole.interact' and 'InteractiveConsole.runcode' functions. 532 InteractiveConsole.interact = interact_prompt 533 InteractiveConsole.runcode = runcode 534 535 # The console. 536 console = InteractiveConsole(local) 537 console.interact(intro, local)
538 539
540 -def run_script(intro=None, local=None, script_file=None, show_script=True, raise_relax_error=False):
541 """Python interpreter emulation. 542 543 This function replaces 'code.interact'. 544 545 546 @param intro: The string to print prior to jumping to the prompt mode. 547 @type intro: str 548 @param local: A namespace which will become that of the prompt (i.e. the namespace 549 visible to the user when in the prompt mode). This should be the 550 output of a function such as locals(). 551 @type local: dict 552 @param script_file: The script file to be executed. 553 @type script_file: None or str 554 @param show_script: If true, the relax will print the script contents prior to executing 555 the script. 556 @type show_script: bool 557 @param raise_relax_error: If false, the default, then a nice error message will be sent to 558 STDERR, without a traceback, when a RelaxError occurs. This is to 559 make things nicer for the user. 560 @type raise_relax_error: bool 561 """ 562 563 # Replace the 'InteractiveConsole.interact' and 'InteractiveConsole.runcode' functions. 564 InteractiveConsole.interact = interact_script 565 InteractiveConsole.runcode = runcode 566 567 # The console. 568 console = InteractiveConsole(local) 569 return console.interact(intro, local, script_file, show_script=show_script, raise_relax_error=raise_relax_error)
570 571
572 -def runcode(self, code):
573 """Replacement code for code.InteractiveInterpreter.runcode. 574 575 @param code: The code to execute. 576 @type code: str 577 """ 578 579 try: 580 exec(code, self.locals) 581 except SystemExit: 582 raise 583 except AllRelaxErrors: 584 instance = sys.exc_info()[1] 585 self.write(instance.__str__()) 586 self.write("\n") 587 except: 588 self.showtraceback()
589