Home | History | Annotate | Download | only in Lib
      1 """Utilities needed to emulate Python's interactive interpreter.
      2 
      3 """
      4 
      5 # Inspired by similar code by Jeff Epler and Fredrik Lundh.
      6 
      7 
      8 import sys
      9 import traceback
     10 from codeop import CommandCompiler, compile_command
     11 
     12 __all__ = ["InteractiveInterpreter", "InteractiveConsole", "interact",
     13            "compile_command"]
     14 
     15 class InteractiveInterpreter:
     16     """Base class for InteractiveConsole.
     17 
     18     This class deals with parsing and interpreter state (the user's
     19     namespace); it doesn't deal with input buffering or prompting or
     20     input file naming (the filename is always passed in explicitly).
     21 
     22     """
     23 
     24     def __init__(self, locals=None):
     25         """Constructor.
     26 
     27         The optional 'locals' argument specifies the dictionary in
     28         which code will be executed; it defaults to a newly created
     29         dictionary with key "__name__" set to "__console__" and key
     30         "__doc__" set to None.
     31 
     32         """
     33         if locals is None:
     34             locals = {"__name__": "__console__", "__doc__": None}
     35         self.locals = locals
     36         self.compile = CommandCompiler()
     37 
     38     def runsource(self, source, filename="<input>", symbol="single"):
     39         """Compile and run some source in the interpreter.
     40 
     41         Arguments are as for compile_command().
     42 
     43         One several things can happen:
     44 
     45         1) The input is incorrect; compile_command() raised an
     46         exception (SyntaxError or OverflowError).  A syntax traceback
     47         will be printed by calling the showsyntaxerror() method.
     48 
     49         2) The input is incomplete, and more input is required;
     50         compile_command() returned None.  Nothing happens.
     51 
     52         3) The input is complete; compile_command() returned a code
     53         object.  The code is executed by calling self.runcode() (which
     54         also handles run-time exceptions, except for SystemExit).
     55 
     56         The return value is True in case 2, False in the other cases (unless
     57         an exception is raised).  The return value can be used to
     58         decide whether to use sys.ps1 or sys.ps2 to prompt the next
     59         line.
     60 
     61         """
     62         try:
     63             code = self.compile(source, filename, symbol)
     64         except (OverflowError, SyntaxError, ValueError):
     65             # Case 1
     66             self.showsyntaxerror(filename)
     67             return False
     68 
     69         if code is None:
     70             # Case 2
     71             return True
     72 
     73         # Case 3
     74         self.runcode(code)
     75         return False
     76 
     77     def runcode(self, code):
     78         """Execute a code object.
     79 
     80         When an exception occurs, self.showtraceback() is called to
     81         display a traceback.  All exceptions are caught except
     82         SystemExit, which is reraised.
     83 
     84         A note about KeyboardInterrupt: this exception may occur
     85         elsewhere in this code, and may not always be caught.  The
     86         caller should be prepared to deal with it.
     87 
     88         """
     89         try:
     90             exec(code, self.locals)
     91         except SystemExit:
     92             raise
     93         except:
     94             self.showtraceback()
     95 
     96     def showsyntaxerror(self, filename=None):
     97         """Display the syntax error that just occurred.
     98 
     99         This doesn't display a stack trace because there isn't one.
    100 
    101         If a filename is given, it is stuffed in the exception instead
    102         of what was there before (because Python's parser always uses
    103         "<string>" when reading from a string).
    104 
    105         The output is written by self.write(), below.
    106 
    107         """
    108         type, value, tb = sys.exc_info()
    109         sys.last_type = type
    110         sys.last_value = value
    111         sys.last_traceback = tb
    112         if filename and type is SyntaxError:
    113             # Work hard to stuff the correct filename in the exception
    114             try:
    115                 msg, (dummy_filename, lineno, offset, line) = value.args
    116             except ValueError:
    117                 # Not the format we expect; leave it alone
    118                 pass
    119             else:
    120                 # Stuff in the right filename
    121                 value = SyntaxError(msg, (filename, lineno, offset, line))
    122                 sys.last_value = value
    123         if sys.excepthook is sys.__excepthook__:
    124             lines = traceback.format_exception_only(type, value)
    125             self.write(''.join(lines))
    126         else:
    127             # If someone has set sys.excepthook, we let that take precedence
    128             # over self.write
    129             sys.excepthook(type, value, tb)
    130 
    131     def showtraceback(self):
    132         """Display the exception that just occurred.
    133 
    134         We remove the first stack item because it is our own code.
    135 
    136         The output is written by self.write(), below.
    137 
    138         """
    139         sys.last_type, sys.last_value, last_tb = ei = sys.exc_info()
    140         sys.last_traceback = last_tb
    141         try:
    142             lines = traceback.format_exception(ei[0], ei[1], last_tb.tb_next)
    143             if sys.excepthook is sys.__excepthook__:
    144                 self.write(''.join(lines))
    145             else:
    146                 # If someone has set sys.excepthook, we let that take precedence
    147                 # over self.write
    148                 sys.excepthook(ei[0], ei[1], last_tb)
    149         finally:
    150             last_tb = ei = None
    151 
    152     def write(self, data):
    153         """Write a string.
    154 
    155         The base implementation writes to sys.stderr; a subclass may
    156         replace this with a different implementation.
    157 
    158         """
    159         sys.stderr.write(data)
    160 
    161 
    162 class InteractiveConsole(InteractiveInterpreter):
    163     """Closely emulate the behavior of the interactive Python interpreter.
    164 
    165     This class builds on InteractiveInterpreter and adds prompting
    166     using the familiar sys.ps1 and sys.ps2, and input buffering.
    167 
    168     """
    169 
    170     def __init__(self, locals=None, filename="<console>"):
    171         """Constructor.
    172 
    173         The optional locals argument will be passed to the
    174         InteractiveInterpreter base class.
    175 
    176         The optional filename argument should specify the (file)name
    177         of the input stream; it will show up in tracebacks.
    178 
    179         """
    180         InteractiveInterpreter.__init__(self, locals)
    181         self.filename = filename
    182         self.resetbuffer()
    183 
    184     def resetbuffer(self):
    185         """Reset the input buffer."""
    186         self.buffer = []
    187 
    188     def interact(self, banner=None, exitmsg=None):
    189         """Closely emulate the interactive Python console.
    190 
    191         The optional banner argument specifies the banner to print
    192         before the first interaction; by default it prints a banner
    193         similar to the one printed by the real Python interpreter,
    194         followed by the current class name in parentheses (so as not
    195         to confuse this with the real interpreter -- since it's so
    196         close!).
    197 
    198         The optional exitmsg argument specifies the exit message
    199         printed when exiting. Pass the empty string to suppress
    200         printing an exit message. If exitmsg is not given or None,
    201         a default message is printed.
    202 
    203         """
    204         try:
    205             sys.ps1
    206         except AttributeError:
    207             sys.ps1 = ">>> "
    208         try:
    209             sys.ps2
    210         except AttributeError:
    211             sys.ps2 = "... "
    212         cprt = 'Type "help", "copyright", "credits" or "license" for more information.'
    213         if banner is None:
    214             self.write("Python %s on %s\n%s\n(%s)\n" %
    215                        (sys.version, sys.platform, cprt,
    216                         self.__class__.__name__))
    217         elif banner:
    218             self.write("%s\n" % str(banner))
    219         more = 0
    220         while 1:
    221             try:
    222                 if more:
    223                     prompt = sys.ps2
    224                 else:
    225                     prompt = sys.ps1
    226                 try:
    227                     line = self.raw_input(prompt)
    228                 except EOFError:
    229                     self.write("\n")
    230                     break
    231                 else:
    232                     more = self.push(line)
    233             except KeyboardInterrupt:
    234                 self.write("\nKeyboardInterrupt\n")
    235                 self.resetbuffer()
    236                 more = 0
    237         if exitmsg is None:
    238             self.write('now exiting %s...\n' % self.__class__.__name__)
    239         elif exitmsg != '':
    240             self.write('%s\n' % exitmsg)
    241 
    242     def push(self, line):
    243         """Push a line to the interpreter.
    244 
    245         The line should not have a trailing newline; it may have
    246         internal newlines.  The line is appended to a buffer and the
    247         interpreter's runsource() method is called with the
    248         concatenated contents of the buffer as source.  If this
    249         indicates that the command was executed or invalid, the buffer
    250         is reset; otherwise, the command is incomplete, and the buffer
    251         is left as it was after the line was appended.  The return
    252         value is 1 if more input is required, 0 if the line was dealt
    253         with in some way (this is the same as runsource()).
    254 
    255         """
    256         self.buffer.append(line)
    257         source = "\n".join(self.buffer)
    258         more = self.runsource(source, self.filename)
    259         if not more:
    260             self.resetbuffer()
    261         return more
    262 
    263     def raw_input(self, prompt=""):
    264         """Write a prompt and read a line.
    265 
    266         The returned line does not include the trailing newline.
    267         When the user enters the EOF key sequence, EOFError is raised.
    268 
    269         The base implementation uses the built-in function
    270         input(); a subclass may replace this with a different
    271         implementation.
    272 
    273         """
    274         return input(prompt)
    275 
    276 
    277 
    278 def interact(banner=None, readfunc=None, local=None, exitmsg=None):
    279     """Closely emulate the interactive Python interpreter.
    280 
    281     This is a backwards compatible interface to the InteractiveConsole
    282     class.  When readfunc is not specified, it attempts to import the
    283     readline module to enable GNU readline if it is available.
    284 
    285     Arguments (all optional, all default to None):
    286 
    287     banner -- passed to InteractiveConsole.interact()
    288     readfunc -- if not None, replaces InteractiveConsole.raw_input()
    289     local -- passed to InteractiveInterpreter.__init__()
    290     exitmsg -- passed to InteractiveConsole.interact()
    291 
    292     """
    293     console = InteractiveConsole(local)
    294     if readfunc is not None:
    295         console.raw_input = readfunc
    296     else:
    297         try:
    298             import readline
    299         except ImportError:
    300             pass
    301     console.interact(banner, exitmsg)
    302 
    303 
    304 if __name__ == "__main__":
    305     import argparse
    306 
    307     parser = argparse.ArgumentParser()
    308     parser.add_argument('-q', action='store_true',
    309                        help="don't print version and copyright messages")
    310     args = parser.parse_args()
    311     if args.q or sys.flags.quiet:
    312         banner = ''
    313     else:
    314         banner = None
    315     interact(banner)
    316