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