src/extras/pyrepl/python_reader.py
changeset 0 ca70ae20a155
equal deleted inserted replaced
-1:000000000000 0:ca70ae20a155
       
     1 #   Copyright 2000-2004 Michael Hudson mwh@python.net
       
     2 #
       
     3 #                        All Rights Reserved
       
     4 #
       
     5 # Portions Copyright (c) 2005 Nokia Corporation 
       
     6 #
       
     7 # Permission to use, copy, modify, and distribute this software and
       
     8 # its documentation for any purpose is hereby granted without fee,
       
     9 # provided that the above copyright notice appear in all copies and
       
    10 # that both that copyright notice and this permission notice appear in
       
    11 # supporting documentation.
       
    12 #
       
    13 # THE AUTHOR MICHAEL HUDSON DISCLAIMS ALL WARRANTIES WITH REGARD TO
       
    14 # THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
       
    15 # AND FITNESS, IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL,
       
    16 # INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
       
    17 # RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
       
    18 # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
       
    19 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
       
    20 
       
    21 # one impressive collections of imports:
       
    22 from pyrepl.completing_reader import CompletingReader
       
    23 from pyrepl.historical_reader import HistoricalReader
       
    24 from pyrepl import completing_reader, reader
       
    25 from pyrepl import commands, completer
       
    26 from pyrepl import module_lister
       
    27 import sys, os, re, code, atexit
       
    28 import warnings
       
    29 
       
    30 HISTORYFILE=u"c:\\pythonhistory.txt"
       
    31 
       
    32 try:
       
    33     import cPickle as pickle
       
    34 except ImportError:
       
    35     try:
       
    36         import pickle
       
    37     except ImportError:
       
    38         try:
       
    39             import dumbpickle as pickle
       
    40         except ImportError:
       
    41             print "No pickle module -> command history will not be saved."
       
    42             pickle=None
       
    43 
       
    44 CommandCompiler = code.CommandCompiler
       
    45 
       
    46 def eat_it(*args):
       
    47     """this function eats warnings, if you were wondering"""
       
    48     pass
       
    49 
       
    50 class maybe_accept(commands.Command):
       
    51     def do(self):
       
    52         r = self.reader
       
    53         text = r.get_unicode()
       
    54         try:
       
    55             # ooh, look at the hack:
       
    56             code = r.compiler("#coding:utf-8\n"+text.encode('utf-8'))
       
    57         except (OverflowError, SyntaxError, ValueError):
       
    58             self.finish = 1
       
    59         else:
       
    60             if code is None:
       
    61                 r.insert("\n")
       
    62             else:
       
    63                 self.finish = 1
       
    64 
       
    65 from_line_prog = re.compile(
       
    66     "^from\s+(?P<mod>[A-Za-z_.0-9]*)\s+import\s+(?P<name>[A-Za-z_.0-9]*)")
       
    67 import_line_prog = re.compile(
       
    68     "^(?:import|from)\s+(?P<mod>[A-Za-z_.0-9]*)\s*$")
       
    69 
       
    70 def mk_saver(reader):
       
    71     def saver(reader=reader):
       
    72         try:
       
    73             file = open(HISTORYFILE, "w")
       
    74         except IOError:
       
    75             pass
       
    76         else:
       
    77             pickle.dump(reader.history, file)
       
    78             file.close()
       
    79     return saver
       
    80 
       
    81 def prhelp():
       
    82     print '''
       
    83 This is pyrepl, a simple multiline editor with command history, tab
       
    84 completion and emacs-like key bindings.
       
    85 
       
    86 Most useful commands:
       
    87 (C-x means Control-x, M-x means Alt-x or Esc x)
       
    88 
       
    89     C-p, C-n     previous/next history item
       
    90     C-o          accept line and take next line from history
       
    91                  (handy for repeating sequences of commands)
       
    92     M-r          restore line to what it was when retrieved
       
    93                  from history
       
    94     C-r, C-s     incremental search back/forward
       
    95     C-a, C-e     beginning/end of line
       
    96     M-f, M-b     next/previous word
       
    97     C-k          cut to end of line
       
    98     C-w, M-d     cut word to left/right of cursor
       
    99     C-y          paste
       
   100     M-y          paste older (use after C-y)
       
   101     M-return     insert literal Return
       
   102     C-d          delete character/quit interpreter
       
   103     C-xC-s       save history to file (also saved on graceful exit)
       
   104     tab          complete item (attribute, method, variable...)
       
   105 '''
       
   106 
       
   107 class PythonicReader(CompletingReader, HistoricalReader):
       
   108     def collect_keymap(self):
       
   109         return super(PythonicReader, self).collect_keymap() + (
       
   110             (r'\r', 'maybe-accept'),
       
   111             (r'\M-\r', 'insert-nl'),
       
   112             (r'\n', 'maybe-accept'),
       
   113             (r'\M-\n', 'insert-nl'))
       
   114     
       
   115     def __init__(self, console, locals,
       
   116                  compiler=None):
       
   117         super(PythonicReader, self).__init__(console)
       
   118         self.completer = completer.Completer(locals)
       
   119         st = self.syntax_table
       
   120         for c in "._0123456789":
       
   121             st[c] = reader.SYNTAX_WORD
       
   122         self.locals = locals
       
   123         self.locals['prhelp']=prhelp
       
   124         if compiler is None:
       
   125             self.compiler = CommandCompiler()
       
   126         else:
       
   127             self.compiler = compiler
       
   128         try:
       
   129             file = open(HISTORYFILE)
       
   130         except IOError:
       
   131             pass
       
   132         else:
       
   133             try:
       
   134                 self.history = pickle.load(file)
       
   135             except:
       
   136                 self.history = []
       
   137             self.historyi = len(self.history)
       
   138             file.close()
       
   139         self.save_history=mk_saver(self)
       
   140         atexit.register(self.save_history)
       
   141         for c in [maybe_accept]:
       
   142             self.commands[c.__name__] = c
       
   143             self.commands[c.__name__.replace('_', '-')] = c        
       
   144     
       
   145     def get_completions(self, stem):
       
   146         b = self.get_unicode()
       
   147         m = import_line_prog.match(b)
       
   148         if m:
       
   149             mod = m.group("mod")
       
   150             try:
       
   151                 return module_lister.find_modules(mod)
       
   152             except ImportError:
       
   153                 pass
       
   154         m = from_line_prog.match(b)
       
   155         if m:
       
   156             mod, name = m.group("mod", "name")
       
   157             try:
       
   158                 l = module_lister._packages[mod]
       
   159             except KeyError:
       
   160                 try:
       
   161                     mod = __import__(mod, self.locals, self.locals, [''])
       
   162                     return [x for x in dir(mod) if x.startswith(name)]
       
   163                 except ImportError:
       
   164                     pass
       
   165             else:
       
   166                 return [x[len(mod) + 1:]
       
   167                         for x in l if x.startswith(mod + '.' + name)]
       
   168         try:
       
   169             l = completing_reader.uniqify(self.completer.complete(stem))
       
   170             return l
       
   171         except (NameError, AttributeError):
       
   172             return []
       
   173 
       
   174 class ReaderConsole(code.InteractiveInterpreter):
       
   175     II_init = code.InteractiveInterpreter.__init__
       
   176     def __init__(self, console, locals=None):
       
   177         self.II_init(locals)
       
   178         self.compiler = CommandCompiler()
       
   179         self.compile = self.compiler.compiler
       
   180         self.reader = PythonicReader(console, locals, self.compiler)
       
   181         self.locals['Reader'] = self.reader
       
   182 
       
   183     def run_user_init_file(self):
       
   184         for key in "PYREPLSTARTUP", "PYTHONSTARTUP":
       
   185             initfile = os.environ.get(key)
       
   186             if initfile is not None and os.path.exists(initfile):
       
   187                 break
       
   188         else:
       
   189             return
       
   190         try:
       
   191             execfile(initfile, self.locals, self.locals)
       
   192         except:
       
   193             etype, value, tb = sys.exc_info()
       
   194             import traceback
       
   195             traceback.print_exception(etype, value, tb.tb_next)
       
   196 
       
   197     def execute(self, text):
       
   198         try:
       
   199             # ooh, look at the hack:            
       
   200             code = self.compile("# coding:utf8\n"+text.encode('utf-8'),
       
   201                                 '<input>', 'single')
       
   202         except (OverflowError, SyntaxError, ValueError):
       
   203             self.showsyntaxerror("<input>")
       
   204         else:
       
   205             self.runcode(code)
       
   206             sys.stdout.flush()
       
   207 
       
   208     def interact(self):
       
   209         while 1:
       
   210             try:
       
   211                 try: # catches EOFError's and KeyboardInterrupts during execution
       
   212                     try: # catches KeyboardInterrupts during editing
       
   213                         try: # warning saver
       
   214                             # can't have warnings spewed onto terminal
       
   215                             sv = warnings.showwarning
       
   216                             warnings.showwarning = eat_it
       
   217                             l = unicode(self.reader.readline(), 'utf-8')
       
   218                         finally:
       
   219                             warnings.showwarning = sv
       
   220                     except KeyboardInterrupt:
       
   221                         print "KeyboardInterrupt"
       
   222                     else:
       
   223                         if l:
       
   224                             self.execute(l)
       
   225                 except EOFError:
       
   226                     break
       
   227                 except KeyboardInterrupt:
       
   228                     continue
       
   229             finally:
       
   230                 self.reader.save_history()
       
   231 
       
   232     def prepare(self):
       
   233         self.sv_sw = warnings.showwarning
       
   234         warnings.showwarning = eat_it
       
   235         self.reader.prepare()
       
   236         self.reader.refresh() # we want :after methods...
       
   237 
       
   238     def restore(self):
       
   239         self.reader.restore()
       
   240         warnings.showwarning = self.sv_sw
       
   241 
       
   242     def handle1(self, block=1):
       
   243         try:
       
   244             r = 1
       
   245             r = self.reader.handle1(block)
       
   246         except KeyboardInterrupt:
       
   247             self.restore()
       
   248             print "KeyboardInterrupt"
       
   249             self.prepare()
       
   250         else:
       
   251             if self.reader.finished:
       
   252                 text = self.reader.get_unicode()
       
   253                 self.restore()
       
   254                 if text:
       
   255                     self.execute(text)
       
   256                 self.prepare()
       
   257         return r
       
   258 
       
   259