src/extras/pyrepl/python_reader.py
changeset 0 ca70ae20a155
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/extras/pyrepl/python_reader.py	Tue Feb 16 10:07:05 2010 +0530
@@ -0,0 +1,259 @@
+#   Copyright 2000-2004 Michael Hudson mwh@python.net
+#
+#                        All Rights Reserved
+#
+# Portions Copyright (c) 2005 Nokia Corporation 
+#
+# Permission to use, copy, modify, and distribute this software and
+# its documentation for any purpose is hereby granted without fee,
+# provided that the above copyright notice appear in all copies and
+# that both that copyright notice and this permission notice appear in
+# supporting documentation.
+#
+# THE AUTHOR MICHAEL HUDSON DISCLAIMS ALL WARRANTIES WITH REGARD TO
+# THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS, IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL,
+# INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+# RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+# CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+# one impressive collections of imports:
+from pyrepl.completing_reader import CompletingReader
+from pyrepl.historical_reader import HistoricalReader
+from pyrepl import completing_reader, reader
+from pyrepl import commands, completer
+from pyrepl import module_lister
+import sys, os, re, code, atexit
+import warnings
+
+HISTORYFILE=u"c:\\pythonhistory.txt"
+
+try:
+    import cPickle as pickle
+except ImportError:
+    try:
+        import pickle
+    except ImportError:
+        try:
+            import dumbpickle as pickle
+        except ImportError:
+            print "No pickle module -> command history will not be saved."
+            pickle=None
+
+CommandCompiler = code.CommandCompiler
+
+def eat_it(*args):
+    """this function eats warnings, if you were wondering"""
+    pass
+
+class maybe_accept(commands.Command):
+    def do(self):
+        r = self.reader
+        text = r.get_unicode()
+        try:
+            # ooh, look at the hack:
+            code = r.compiler("#coding:utf-8\n"+text.encode('utf-8'))
+        except (OverflowError, SyntaxError, ValueError):
+            self.finish = 1
+        else:
+            if code is None:
+                r.insert("\n")
+            else:
+                self.finish = 1
+
+from_line_prog = re.compile(
+    "^from\s+(?P<mod>[A-Za-z_.0-9]*)\s+import\s+(?P<name>[A-Za-z_.0-9]*)")
+import_line_prog = re.compile(
+    "^(?:import|from)\s+(?P<mod>[A-Za-z_.0-9]*)\s*$")
+
+def mk_saver(reader):
+    def saver(reader=reader):
+        try:
+            file = open(HISTORYFILE, "w")
+        except IOError:
+            pass
+        else:
+            pickle.dump(reader.history, file)
+            file.close()
+    return saver
+
+def prhelp():
+    print '''
+This is pyrepl, a simple multiline editor with command history, tab
+completion and emacs-like key bindings.
+
+Most useful commands:
+(C-x means Control-x, M-x means Alt-x or Esc x)
+
+    C-p, C-n     previous/next history item
+    C-o          accept line and take next line from history
+                 (handy for repeating sequences of commands)
+    M-r          restore line to what it was when retrieved
+                 from history
+    C-r, C-s     incremental search back/forward
+    C-a, C-e     beginning/end of line
+    M-f, M-b     next/previous word
+    C-k          cut to end of line
+    C-w, M-d     cut word to left/right of cursor
+    C-y          paste
+    M-y          paste older (use after C-y)
+    M-return     insert literal Return
+    C-d          delete character/quit interpreter
+    C-xC-s       save history to file (also saved on graceful exit)
+    tab          complete item (attribute, method, variable...)
+'''
+
+class PythonicReader(CompletingReader, HistoricalReader):
+    def collect_keymap(self):
+        return super(PythonicReader, self).collect_keymap() + (
+            (r'\r', 'maybe-accept'),
+            (r'\M-\r', 'insert-nl'),
+            (r'\n', 'maybe-accept'),
+            (r'\M-\n', 'insert-nl'))
+    
+    def __init__(self, console, locals,
+                 compiler=None):
+        super(PythonicReader, self).__init__(console)
+        self.completer = completer.Completer(locals)
+        st = self.syntax_table
+        for c in "._0123456789":
+            st[c] = reader.SYNTAX_WORD
+        self.locals = locals
+        self.locals['prhelp']=prhelp
+        if compiler is None:
+            self.compiler = CommandCompiler()
+        else:
+            self.compiler = compiler
+        try:
+            file = open(HISTORYFILE)
+        except IOError:
+            pass
+        else:
+            try:
+                self.history = pickle.load(file)
+            except:
+                self.history = []
+            self.historyi = len(self.history)
+            file.close()
+        self.save_history=mk_saver(self)
+        atexit.register(self.save_history)
+        for c in [maybe_accept]:
+            self.commands[c.__name__] = c
+            self.commands[c.__name__.replace('_', '-')] = c        
+    
+    def get_completions(self, stem):
+        b = self.get_unicode()
+        m = import_line_prog.match(b)
+        if m:
+            mod = m.group("mod")
+            try:
+                return module_lister.find_modules(mod)
+            except ImportError:
+                pass
+        m = from_line_prog.match(b)
+        if m:
+            mod, name = m.group("mod", "name")
+            try:
+                l = module_lister._packages[mod]
+            except KeyError:
+                try:
+                    mod = __import__(mod, self.locals, self.locals, [''])
+                    return [x for x in dir(mod) if x.startswith(name)]
+                except ImportError:
+                    pass
+            else:
+                return [x[len(mod) + 1:]
+                        for x in l if x.startswith(mod + '.' + name)]
+        try:
+            l = completing_reader.uniqify(self.completer.complete(stem))
+            return l
+        except (NameError, AttributeError):
+            return []
+
+class ReaderConsole(code.InteractiveInterpreter):
+    II_init = code.InteractiveInterpreter.__init__
+    def __init__(self, console, locals=None):
+        self.II_init(locals)
+        self.compiler = CommandCompiler()
+        self.compile = self.compiler.compiler
+        self.reader = PythonicReader(console, locals, self.compiler)
+        self.locals['Reader'] = self.reader
+
+    def run_user_init_file(self):
+        for key in "PYREPLSTARTUP", "PYTHONSTARTUP":
+            initfile = os.environ.get(key)
+            if initfile is not None and os.path.exists(initfile):
+                break
+        else:
+            return
+        try:
+            execfile(initfile, self.locals, self.locals)
+        except:
+            etype, value, tb = sys.exc_info()
+            import traceback
+            traceback.print_exception(etype, value, tb.tb_next)
+
+    def execute(self, text):
+        try:
+            # ooh, look at the hack:            
+            code = self.compile("# coding:utf8\n"+text.encode('utf-8'),
+                                '<input>', 'single')
+        except (OverflowError, SyntaxError, ValueError):
+            self.showsyntaxerror("<input>")
+        else:
+            self.runcode(code)
+            sys.stdout.flush()
+
+    def interact(self):
+        while 1:
+            try:
+                try: # catches EOFError's and KeyboardInterrupts during execution
+                    try: # catches KeyboardInterrupts during editing
+                        try: # warning saver
+                            # can't have warnings spewed onto terminal
+                            sv = warnings.showwarning
+                            warnings.showwarning = eat_it
+                            l = unicode(self.reader.readline(), 'utf-8')
+                        finally:
+                            warnings.showwarning = sv
+                    except KeyboardInterrupt:
+                        print "KeyboardInterrupt"
+                    else:
+                        if l:
+                            self.execute(l)
+                except EOFError:
+                    break
+                except KeyboardInterrupt:
+                    continue
+            finally:
+                self.reader.save_history()
+
+    def prepare(self):
+        self.sv_sw = warnings.showwarning
+        warnings.showwarning = eat_it
+        self.reader.prepare()
+        self.reader.refresh() # we want :after methods...
+
+    def restore(self):
+        self.reader.restore()
+        warnings.showwarning = self.sv_sw
+
+    def handle1(self, block=1):
+        try:
+            r = 1
+            r = self.reader.handle1(block)
+        except KeyboardInterrupt:
+            self.restore()
+            print "KeyboardInterrupt"
+            self.prepare()
+        else:
+            if self.reader.finished:
+                text = self.reader.get_unicode()
+                self.restore()
+                if text:
+                    self.execute(text)
+                self.prepare()
+        return r
+
+