|  | """Utilities dealing with code objects.""" | 
|  |  | 
|  | import sys | 
|  | import string | 
|  | import traceback | 
|  |  | 
|  | def compile_command(source, filename="<input>", symbol="single"): | 
|  | r"""Compile a command and determine whether it is incomplete. | 
|  |  | 
|  | Arguments: | 
|  |  | 
|  | source -- the source string; may contain \n characters | 
|  | filename -- optional filename from which source was read; default "<input>" | 
|  | symbol -- optional grammar start symbol; "single" (default) or "eval" | 
|  |  | 
|  | Return value / exception raised: | 
|  |  | 
|  | - Return a code object if the command is complete and valid | 
|  | - Return None if the command is incomplete | 
|  | - Raise SyntaxError if the command is a syntax error | 
|  |  | 
|  | Approach: | 
|  |  | 
|  | Compile three times: as is, with \n, and with \n\n appended.  If | 
|  | it compiles as is, it's complete.  If it compiles with one \n | 
|  | appended, we expect more.  If it doesn't compile either way, we | 
|  | compare the error we get when compiling with \n or \n\n appended. | 
|  | If the errors are the same, the code is broken.  But if the errors | 
|  | are different, we expect more.  Not intuitive; not even guaranteed | 
|  | to hold in future releases; but this matches the compiler's | 
|  | behavior in Python 1.4 and 1.5. | 
|  |  | 
|  | """ | 
|  |  | 
|  | err = err1 = err2 = None | 
|  | code = code1 = code2 = None | 
|  |  | 
|  | try: | 
|  | code = compile(source, filename, symbol) | 
|  | except SyntaxError, err: | 
|  | pass | 
|  |  | 
|  | try: | 
|  | code1 = compile(source + "\n", filename, symbol) | 
|  | except SyntaxError, err1: | 
|  | pass | 
|  |  | 
|  | try: | 
|  | code2 = compile(source + "\n\n", filename, symbol) | 
|  | except SyntaxError, err2: | 
|  | pass | 
|  |  | 
|  | if code: | 
|  | return code | 
|  | try: | 
|  | e1 = err1.__dict__ | 
|  | except AttributeError: | 
|  | e1 = err1 | 
|  | try: | 
|  | e2 = err2.__dict__ | 
|  | except AttributeError: | 
|  | e2 = err2 | 
|  | if not code1 and e1 == e2: | 
|  | raise SyntaxError, err1 | 
|  |  | 
|  |  | 
|  | class InteractiveConsole: | 
|  | """Closely emulate the behavior of the interactive Python interpreter. | 
|  |  | 
|  | After code by Jeff Epler and Fredrik Lundh. | 
|  | """ | 
|  |  | 
|  | def __init__(self, filename="<console>", locals=None): | 
|  | """Constructor. | 
|  |  | 
|  | The optional filename argument specifies the (file)name of the | 
|  | input stream; it will show up in tracebacks.  It defaults to | 
|  | '<console>'. | 
|  |  | 
|  | """ | 
|  | self.filename = filename | 
|  | if locals is None: | 
|  | locals = {} | 
|  | self.locals = locals | 
|  | self.resetbuffer() | 
|  |  | 
|  | def resetbuffer(self): | 
|  | """Reset the input buffer (but not the variables!).""" | 
|  | self.buffer = [] | 
|  |  | 
|  | def interact(self, banner=None): | 
|  | """Closely emulate the interactive Python console.""" | 
|  | try: | 
|  | sys.ps1 | 
|  | except AttributeError: | 
|  | sys.ps1 = ">>> " | 
|  | try: | 
|  | sys.ps2 | 
|  | except AttributeError: | 
|  | sys.ps2 = "... " | 
|  | if banner is None: | 
|  | self.write("Python %s on %s\n%s\n(%s)\n" % | 
|  | (sys.version, sys.platform, sys.copyright, | 
|  | self.__class__.__name__)) | 
|  | else: | 
|  | self.write("%s\n" % str(banner)) | 
|  | more = 0 | 
|  | while 1: | 
|  | try: | 
|  | if more: | 
|  | prompt = sys.ps2 | 
|  | else: | 
|  | prompt = sys.ps1 | 
|  | try: | 
|  | line = self.raw_input(prompt) | 
|  | except EOFError: | 
|  | self.write("\n") | 
|  | break | 
|  | else: | 
|  | more = self.push(line) | 
|  | except KeyboardInterrupt: | 
|  | self.write("\nKeyboardInterrupt\n") | 
|  | self.resetbuffer() | 
|  | more = 0 | 
|  |  | 
|  | def push(self, line): | 
|  | """Push a line to the interpreter. | 
|  |  | 
|  | The line should not have a trailing newline. | 
|  |  | 
|  | One of three things will happen: | 
|  |  | 
|  | 1) The input is incorrect; compile_command() raised | 
|  | SyntaxError.  A syntax traceback will be printed. | 
|  |  | 
|  | 2) The input is incomplete, and more input is required; | 
|  | compile_command() returned None. | 
|  |  | 
|  | 3) The input is complete; compile_command() returned a code | 
|  | object.  The code is executed.  When an exception occurs, a | 
|  | traceback is printed.  All exceptions are caught except | 
|  | SystemExit, which is reraised. | 
|  |  | 
|  | The return value is 1 in case 2, 0 in the other cases.  (The | 
|  | return value can be used to decide whether to use sys.ps1 or | 
|  | sys.ps2 to prompt the next line.) | 
|  |  | 
|  | A note about KeyboardInterrupt: this exception may occur | 
|  | elsewhere in this code, and will not always be caught.  The | 
|  | caller should be prepared to deal with it. | 
|  |  | 
|  | """ | 
|  | self.buffer.append(line) | 
|  |  | 
|  | try: | 
|  | x = compile_command(string.join(self.buffer, "\n"), | 
|  | filename=self.filename) | 
|  | except SyntaxError: | 
|  | # Case 1 | 
|  | self.showsyntaxerror() | 
|  | self.resetbuffer() | 
|  | return 0 | 
|  |  | 
|  | if x is None: | 
|  | # Case 2 | 
|  | return 1 | 
|  |  | 
|  | # Case 3 | 
|  | try: | 
|  | exec x in self.locals | 
|  | except SystemExit: | 
|  | raise | 
|  | except: | 
|  | self.showtraceback() | 
|  | self.resetbuffer() | 
|  | return 0 | 
|  |  | 
|  | def showsyntaxerror(self): | 
|  | """Display the syntax error that just occurred. | 
|  |  | 
|  | This doesn't display a stack trace because there isn't one. | 
|  |  | 
|  | The output is written by self.write(), below. | 
|  |  | 
|  | """ | 
|  | type, value = sys.exc_info()[:2] | 
|  | # Work hard to stuff the correct filename in the exception | 
|  | try: | 
|  | msg, (filename, lineno, offset, line) = value | 
|  | except: | 
|  | pass | 
|  | else: | 
|  | try: | 
|  | value = SyntaxError(msg, (self.filename, lineno, offset, line)) | 
|  | except: | 
|  | value = msg, (self.filename, lineno, offset, line) | 
|  | list = traceback.format_exception_only(type, value) | 
|  | map(self.write, list) | 
|  |  | 
|  | def showtraceback(self): | 
|  | """Display the exception that just occurred. | 
|  |  | 
|  | We remove the first stack item because it is our own code. | 
|  |  | 
|  | The output is written by self.write(), below. | 
|  |  | 
|  | """ | 
|  | try: | 
|  | type, value, tb = sys.exc_info() | 
|  | tblist = traceback.extract_tb(tb) | 
|  | del tblist[0] | 
|  | list = traceback.format_list(tblist) | 
|  | list[len(list):] = traceback.format_exception_only(type, value) | 
|  | finally: | 
|  | tblist = tb = None | 
|  | map(self.write, list) | 
|  |  | 
|  | def write(self, data): | 
|  | """Write a string. | 
|  |  | 
|  | The base implementation writes to sys.stderr; a subclass may | 
|  | replace this with a different implementation. | 
|  |  | 
|  | """ | 
|  | sys.stderr.write(data) | 
|  |  | 
|  | def raw_input(self, prompt=""): | 
|  | """Write a prompt and read a line. | 
|  |  | 
|  | The returned line does not include the trailing newline. | 
|  | When the user enters the EOF key sequence, EOFError is raised. | 
|  |  | 
|  | The base implementation uses the built-in function | 
|  | raw_input(); a subclass may replace this with a different | 
|  | implementation. | 
|  |  | 
|  | """ | 
|  | return raw_input(prompt) | 
|  |  | 
|  |  | 
|  | def interact(banner=None, readfunc=None, locals=None): | 
|  | """Closely emulate the interactive Python interpreter. | 
|  |  | 
|  | This is a backwards compatible interface to the InteractiveConsole | 
|  | class.  It attempts to import the readline module to enable GNU | 
|  | readline if it is available. | 
|  |  | 
|  | Arguments (all optional, all default to None): | 
|  |  | 
|  | banner -- passed to InteractiveConsole.interact() | 
|  | readfunc -- if not None, replaces InteractiveConsole.raw_input() | 
|  | locals -- passed to InteractiveConsole.__init__() | 
|  |  | 
|  | """ | 
|  | try: | 
|  | import readline | 
|  | except: | 
|  | pass | 
|  | console = InteractiveConsole(locals=locals) | 
|  | if readfunc is not None: | 
|  | console.raw_input = readfunc | 
|  | console.interact(banner) | 
|  |  | 
|  | if __name__ == '__main__': | 
|  | interact() |