Commits

Ronny Pfannschmidt committed 6a364c6

inline pyrepl

  • Participants
  • Parent commits 7b56ae2
  • Branches subrepo-removal

Comments (0)

Files changed (29)

lib_pypy/pyrepl/__init__.py

+#   Copyright 2000-2008 Michael Hudson-Doyle <micahel@gmail.com>
+#                       Armin Rigo
+#
+#                        All Rights Reserved
+#
+#
+# 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.

lib_pypy/pyrepl/cmdrepl.py

+#   Copyright 2000-2007 Michael Hudson-Doyle <micahel@gmail.com>
+#                       Maciek Fijalkowski
+#
+#                        All Rights Reserved
+#
+#
+# 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.
+
+"""Wedge pyrepl behaviour into cmd.Cmd-derived classes.
+
+replize, when given a subclass of cmd.Cmd, returns a class that
+behaves almost identically to the supplied class, except that it uses
+pyrepl instead if raw_input.
+
+It was designed to let you do this:
+
+>>> import pdb
+>>> from pyrepl import replize
+>>> pdb.Pdb = replize(pdb.Pdb)
+
+which is in fact done by the `pythoni' script that comes with
+pyrepl."""
+
+from __future__ import nested_scopes
+
+from pyrepl import completing_reader as cr, reader, completer
+from pyrepl.completing_reader import CompletingReader as CR
+import cmd
+
+class CmdReader(CR):
+    def collect_keymap(self):
+        return super(CmdReader, self).collect_keymap() + (
+            ("\\M-\\n", "invalid-key"),
+            ("\\n", "accept"))
+    
+    CR_init = CR.__init__
+    def __init__(self, completions):
+        self.CR_init(self)
+        self.completions = completions
+
+    def get_completions(self, stem):
+        if len(stem) != self.pos:
+            return []
+        return cr.uniqify([s for s in self.completions
+                           if s.startswith(stem)])
+
+def replize(klass, history_across_invocations=1):
+
+    """Return a subclass of the cmd.Cmd-derived klass that uses
+    pyrepl instead of readline.
+
+    Raises a ValueError if klass does not derive from cmd.Cmd.
+
+    The optional history_across_invocations parameter (default 1)
+    controls whether instances of the returned class share
+    histories."""
+
+    completions = [s[3:]
+                   for s in completer.get_class_members(klass)
+                   if s.startswith("do_")]
+
+    if not issubclass(klass, cmd.Cmd):
+        raise Exception
+#    if klass.cmdloop.im_class is not cmd.Cmd:
+#        print "this may not work"
+
+    class CmdRepl(klass):
+        k_init = klass.__init__
+
+        if history_across_invocations:
+            _CmdRepl__history = []
+            def __init__(self, *args, **kw):
+                self.k_init(*args, **kw)
+                self.__reader = CmdReader(completions)
+                self.__reader.history = CmdRepl._CmdRepl__history
+                self.__reader.historyi = len(CmdRepl._CmdRepl__history)
+        else:
+            def __init__(self, *args, **kw):
+                self.k_init(*args, **kw)
+                self.__reader = CmdReader(completions)
+        
+        def cmdloop(self, intro=None):
+            self.preloop()
+            if intro is not None:
+                self.intro = intro
+            if self.intro:
+                print self.intro
+            stop = None
+            while not stop:
+                if self.cmdqueue:
+                    line = self.cmdqueue[0]
+                    del self.cmdqueue[0]
+                else:
+                    try:
+                        self.__reader.ps1 = self.prompt
+                        line = self.__reader.readline()
+                    except EOFError:
+                        line = "EOF"
+                line = self.precmd(line)
+                stop = self.onecmd(line)
+                stop = self.postcmd(stop, line)
+            self.postloop()
+
+    CmdRepl.__name__ = "replize(%s.%s)"%(klass.__module__, klass.__name__)
+    return CmdRepl
+

lib_pypy/pyrepl/commands.py

+#   Copyright 2000-2010 Michael Hudson-Doyle <micahel@gmail.com>
+#                       Antonio Cuni
+#                       Armin Rigo
+#
+#                        All Rights Reserved
+#
+#
+# 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.
+
+import sys, os
+
+# Catgories of actions:
+#  killing
+#  yanking
+#  motion
+#  editing
+#  history
+#  finishing
+# [completion]
+
+class Command(object):
+    finish = 0
+    kills_digit_arg = 1
+    def __init__(self, reader, (event_name, event)):
+        self.reader = reader
+        self.event = event
+        self.event_name = event_name
+    def do(self):
+        pass
+
+class KillCommand(Command):
+    def kill_range(self, start, end):
+        if start == end:
+            return
+        r = self.reader
+        b = r.buffer
+        text = b[start:end]
+        del b[start:end]
+        if is_kill(r.last_command):
+            if start < r.pos:
+                r.kill_ring[-1] = text + r.kill_ring[-1]
+            else:
+                r.kill_ring[-1] = r.kill_ring[-1] + text
+        else:
+            r.kill_ring.append(text)
+        r.pos = start
+        r.dirty = 1
+
+class YankCommand(Command):
+    pass
+
+class MotionCommand(Command):
+    pass
+
+class EditCommand(Command):
+    pass
+
+class FinishCommand(Command):
+    finish = 1
+    pass
+
+def is_kill(command):
+    return command and issubclass(command, KillCommand)
+
+def is_yank(command):
+    return command and issubclass(command, YankCommand)
+
+# etc
+
+class digit_arg(Command):
+    kills_digit_arg = 0
+    def do(self):
+        r = self.reader
+        c = self.event[-1]
+        if c == "-":
+            if r.arg is not None:
+                r.arg = -r.arg
+            else:
+                r.arg = -1
+        else:
+            d = int(c)
+            if r.arg is None:
+                r.arg = d
+            else:
+                if r.arg < 0:
+                    r.arg = 10*r.arg - d
+                else:
+                    r.arg = 10*r.arg + d
+        r.dirty = 1
+
+class clear_screen(Command):
+    def do(self):
+        r = self.reader
+        r.console.clear()
+        r.dirty = 1
+
+class refresh(Command):
+    def do(self):
+        self.reader.dirty = 1
+
+class repaint(Command):
+    def do(self):
+        self.reader.dirty = 1
+        self.reader.console.repaint_prep()
+
+class kill_line(KillCommand):
+    def do(self):
+        r = self.reader
+        b = r.buffer
+        eol = r.eol()
+        for c in b[r.pos:eol]:
+            if not c.isspace():
+                self.kill_range(r.pos, eol)
+                return
+        else:
+            self.kill_range(r.pos, eol+1)
+
+class unix_line_discard(KillCommand):
+    def do(self):
+        r = self.reader
+        self.kill_range(r.bol(), r.pos)
+
+# XXX unix_word_rubout and backward_kill_word should actually
+# do different things...
+
+class unix_word_rubout(KillCommand):
+    def do(self):
+        r = self.reader
+        for i in range(r.get_arg()):
+            self.kill_range(r.bow(), r.pos)
+
+class kill_word(KillCommand):
+    def do(self):
+        r = self.reader
+        for i in range(r.get_arg()):
+            self.kill_range(r.pos, r.eow())
+
+class backward_kill_word(KillCommand):
+    def do(self):
+        r = self.reader
+        for i in range(r.get_arg()):
+            self.kill_range(r.bow(), r.pos)
+
+class yank(YankCommand):
+    def do(self):
+        r = self.reader
+        if not r.kill_ring:
+            r.error("nothing to yank")
+            return
+        r.insert(r.kill_ring[-1])
+
+class yank_pop(YankCommand):
+    def do(self):
+        r = self.reader
+        b = r.buffer
+        if not r.kill_ring:
+            r.error("nothing to yank")
+            return
+        if not is_yank(r.last_command):
+            r.error("previous command was not a yank")
+            return
+        repl = len(r.kill_ring[-1])
+        r.kill_ring.insert(0, r.kill_ring.pop())
+        t = r.kill_ring[-1]
+        b[r.pos - repl:r.pos] = t
+        r.pos = r.pos - repl + len(t)
+        r.dirty = 1
+
+class interrupt(FinishCommand):
+    def do(self):
+        import signal
+        self.reader.console.finish()
+        os.kill(os.getpid(), signal.SIGINT)
+
+class suspend(Command):
+    def do(self):
+        import signal
+        r = self.reader
+        p = r.pos
+        r.console.finish()
+        os.kill(os.getpid(), signal.SIGSTOP)
+        ## this should probably be done
+        ## in a handler for SIGCONT?
+        r.console.prepare()
+        r.pos = p
+        r.posxy = 0, 0
+        r.dirty = 1
+        r.console.screen = []
+
+class up(MotionCommand):
+    def do(self):
+        r = self.reader
+        for i in range(r.get_arg()):
+            bol1 = r.bol()
+            if bol1 == 0:
+                if r.historyi > 0:
+                    r.select_item(r.historyi - 1)
+                    return
+                r.pos = 0
+                r.error("start of buffer")
+                return
+            bol2 = r.bol(bol1-1)
+            line_pos = r.pos - bol1
+            if line_pos > bol1 - bol2 - 1:
+                r.sticky_y = line_pos
+                r.pos = bol1 - 1
+            else:
+                r.pos = bol2 + line_pos
+
+class down(MotionCommand):
+    def do(self):
+        r = self.reader
+        b = r.buffer
+        for i in range(r.get_arg()):
+            bol1 = r.bol()
+            eol1 = r.eol()
+            if eol1 == len(b):
+                if r.historyi < len(r.history):
+                    r.select_item(r.historyi + 1)
+                    r.pos = r.eol(0)
+                    return
+                r.pos = len(b)
+                r.error("end of buffer")
+                return
+            eol2 = r.eol(eol1+1)
+            if r.pos - bol1 > eol2 - eol1 - 1:
+                r.pos = eol2
+            else:
+                r.pos = eol1 + (r.pos - bol1) + 1
+
+class left(MotionCommand):
+    def do(self):
+        r = self.reader
+        for i in range(r.get_arg()):        
+            p = r.pos - 1
+            if p >= 0:
+                r.pos = p
+            else:
+                self.reader.error("start of buffer")
+
+class right(MotionCommand):
+    def do(self):
+        r = self.reader
+        b = r.buffer
+        for i in range(r.get_arg()):
+            p = r.pos + 1
+            if p <= len(b):
+                r.pos = p
+            else:
+                self.reader.error("end of buffer")
+
+class beginning_of_line(MotionCommand):
+    def do(self):
+        self.reader.pos = self.reader.bol()
+
+class end_of_line(MotionCommand):
+    def do(self):
+        r = self.reader
+        self.reader.pos = self.reader.eol()
+
+class home(MotionCommand):
+    def do(self):
+        self.reader.pos = 0
+        
+class end(MotionCommand):
+    def do(self):
+        self.reader.pos = len(self.reader.buffer)
+        
+class forward_word(MotionCommand):
+    def do(self):
+        r = self.reader
+        for i in range(r.get_arg()):
+            r.pos = r.eow()
+    
+class backward_word(MotionCommand):
+    def do(self):
+        r = self.reader
+        for i in range(r.get_arg()):
+            r.pos = r.bow()
+
+class self_insert(EditCommand):
+    def do(self):
+        r = self.reader
+        r.insert(self.event * r.get_arg())
+
+class insert_nl(EditCommand):
+    def do(self):
+        r = self.reader
+        r.insert("\n" * r.get_arg())
+
+class transpose_characters(EditCommand):
+    def do(self):
+        r = self.reader
+        b = r.buffer
+        s = r.pos - 1
+        if s < 0:
+            r.error("cannot transpose at start of buffer")
+        else:
+            if s == len(b):
+                s -= 1
+            t = min(s + r.get_arg(), len(b) - 1)
+            c = b[s]
+            del b[s]
+            b.insert(t, c)
+            r.pos = t
+            r.dirty = 1
+
+class backspace(EditCommand):
+    def do(self):
+        r = self.reader
+        b = r.buffer
+        for i in range(r.get_arg()):
+            if r.pos > 0:
+                r.pos -= 1
+                del b[r.pos]
+                r.dirty = 1
+            else:
+                self.reader.error("can't backspace at start")
+
+class delete(EditCommand):
+    def do(self):
+        r = self.reader
+        b = r.buffer
+        if  ( r.pos == 0 and len(b) == 0 # this is something of a hack
+              and self.event[-1] == "\004"):
+            r.update_screen()
+            r.console.finish()
+            raise EOFError
+        for i in range(r.get_arg()):
+            if r.pos != len(b):
+                del b[r.pos]
+                r.dirty = 1
+            else:
+                self.reader.error("end of buffer")
+
+class accept(FinishCommand):
+    def do(self):
+        pass
+
+class help(Command):
+    def do(self):
+        self.reader.msg = self.reader.help_text
+        self.reader.dirty = 1
+
+class invalid_key(Command):
+    def do(self):
+        pending = self.reader.console.getpending()
+        s = ''.join(self.event) + pending.data
+        self.reader.error("`%r' not bound"%s)
+
+class invalid_command(Command):
+    def do(self):
+        s = self.event_name
+        self.reader.error("command `%s' not known"%s)
+
+class qIHelp(Command):
+    def do(self):
+        r = self.reader
+        r.insert((self.event + r.console.getpending().data) * r.get_arg())
+        r.pop_input_trans()
+
+from pyrepl import input
+
+class QITrans(object):
+    def push(self, evt):
+        self.evt = evt
+    def get(self):
+        return ('qIHelp', self.evt.raw)
+
+class quoted_insert(Command):
+    kills_digit_arg = 0
+    def do(self):
+        self.reader.push_input_trans(QITrans())

lib_pypy/pyrepl/completer.py

+#   Copyright 2000-2004 Michael Hudson-Doyle <micahel@gmail.com>
+#
+#                        All Rights Reserved
+#
+#
+# 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.
+
+import __builtin__
+
+class Completer:
+    def __init__(self, ns):
+        self.ns = ns
+
+    def complete(self, text):
+        if "." in text:
+            return self.attr_matches(text)
+        else:
+            return self.global_matches(text)
+
+    def global_matches(self, text):
+        """Compute matches when text is a simple name.
+
+        Return a list of all keywords, built-in functions and names
+        currently defines in __main__ that match.
+
+        """
+        import keyword
+        matches = []
+        n = len(text)
+        for list in [keyword.kwlist,
+                     __builtin__.__dict__.keys(),
+                     self.ns.keys()]:
+            for word in list:
+                if word[:n] == text and word != "__builtins__":
+                    matches.append(word)
+        return matches
+
+    def attr_matches(self, text):
+        """Compute matches when text contains a dot.
+
+        Assuming the text is of the form NAME.NAME....[NAME], and is
+        evaluatable in the globals of __main__, it will be evaluated
+        and its attributes (as revealed by dir()) are used as possible
+        completions.  (For class instances, class members are are also
+        considered.)
+
+        WARNING: this can still invoke arbitrary C code, if an object
+        with a __getattr__ hook is evaluated.
+
+        """
+        import re
+        m = re.match(r"(\w+(\.\w+)*)\.(\w*)", text)
+        if not m:
+            return []
+        expr, attr = m.group(1, 3)
+        object = eval(expr, self.ns)
+        words = dir(object)
+        if hasattr(object, '__class__'):
+            words.append('__class__')
+            words = words + get_class_members(object.__class__)
+        matches = []
+        n = len(attr)
+        for word in words:
+            if word[:n] == attr and word != "__builtins__":
+                matches.append("%s.%s" % (expr, word))
+        return matches
+
+def get_class_members(klass):
+    ret = dir(klass)
+    if hasattr(klass, '__bases__'):
+        for base in klass.__bases__:
+            ret = ret + get_class_members(base)
+    return ret
+
+

lib_pypy/pyrepl/completing_reader.py

+#   Copyright 2000-2010 Michael Hudson-Doyle <micahel@gmail.com>
+#                       Antonio Cuni
+#
+#                        All Rights Reserved
+#
+#
+# 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.
+
+from pyrepl import commands, reader
+from pyrepl.reader import Reader
+
+def uniqify(l):
+    d = {}
+    for i in l:
+        d[i] = 1
+    r = d.keys()
+    r.sort()
+    return r
+
+def prefix(wordlist, j = 0):
+    d = {}
+    i = j
+    try:
+        while 1:
+            for word in wordlist:
+                d[word[i]] = 1
+            if len(d) > 1:
+                return wordlist[0][j:i]
+            i += 1
+            d = {}
+    except IndexError:
+        return wordlist[0][j:i]
+
+import re
+def stripcolor(s):
+    return stripcolor.regexp.sub('', s)
+stripcolor.regexp = re.compile(r"\x1B\[([0-9]{1,3}(;[0-9]{1,2})?)?[m|K]")
+
+def real_len(s):
+    return len(stripcolor(s))
+
+def left_align(s, maxlen):
+    stripped = stripcolor(s)
+    if len(stripped) > maxlen:
+        # too bad, we remove the color
+        return stripped[:maxlen]
+    padding = maxlen - len(stripped)
+    return s + ' '*padding
+
+def build_menu(cons, wordlist, start, use_brackets, sort_in_column):
+    if use_brackets:
+        item = "[ %s ]"
+        padding = 4
+    else:
+        item = "%s  "
+        padding = 2
+    maxlen = min(max(map(real_len, wordlist)), cons.width - padding)
+    cols = cons.width / (maxlen + padding)
+    rows = (len(wordlist) - 1)/cols + 1
+
+    if sort_in_column:
+        # sort_in_column=False (default)     sort_in_column=True
+        #          A B C                       A D G
+        #          D E F                       B E 
+        #          G                           C F
+        #
+        # "fill" the table with empty words, so we always have the same amout
+        # of rows for each column
+        missing = cols*rows - len(wordlist)
+        wordlist = wordlist + ['']*missing
+        indexes = [(i%cols)*rows + i//cols for i in range(len(wordlist))]
+        wordlist = [wordlist[i] for i in indexes]
+    menu = []
+    i = start
+    for r in range(rows):
+        row = []
+        for col in range(cols):
+            row.append(item % left_align(wordlist[i], maxlen))
+            i += 1
+            if i >= len(wordlist):
+                break
+        menu.append( ''.join(row) )
+        if i >= len(wordlist):
+            i = 0
+            break
+        if r + 5 > cons.height:
+            menu.append("   %d more... "%(len(wordlist) - i))
+            break
+    return menu, i    
+
+# this gets somewhat user interface-y, and as a result the logic gets
+# very convoluted.
+#
+#  To summarise the summary of the summary:- people are a problem.
+#                  -- The Hitch-Hikers Guide to the Galaxy, Episode 12
+
+#### Desired behaviour of the completions commands.
+# the considerations are:
+# (1) how many completions are possible
+# (2) whether the last command was a completion
+# (3) if we can assume that the completer is going to return the same set of
+#     completions: this is controlled by the ``assume_immutable_completions``
+#     variable on the reader, which is True by default to match the historical
+#     behaviour of pyrepl, but e.g. False in the ReadlineAlikeReader to match
+#     more closely readline's semantics (this is needed e.g. by
+#     fancycompleter)
+#
+# if there's no possible completion, beep at the user and point this out.
+# this is easy.
+#
+# if there's only one possible completion, stick it in.  if the last thing
+# user did was a completion, point out that he isn't getting anywhere, but
+# only if the ``assume_immutable_completions`` is True.
+#
+# now it gets complicated.
+# 
+# for the first press of a completion key:
+#  if there's a common prefix, stick it in.
+
+#  irrespective of whether anything got stuck in, if the word is now
+#  complete, show the "complete but not unique" message
+
+#  if there's no common prefix and if the word is not now complete,
+#  beep.
+
+#        common prefix ->    yes          no
+#        word complete \/
+#            yes           "cbnu"      "cbnu"
+#            no              -          beep
+
+# for the second bang on the completion key
+#  there will necessarily be no common prefix
+#  show a menu of the choices.
+
+# for subsequent bangs, rotate the menu around (if there are sufficient
+# choices).
+
+class complete(commands.Command):
+    def do(self):
+        r = self.reader
+        stem = r.get_stem()
+        if r.assume_immutable_completions and \
+                r.last_command_is(self.__class__):
+            completions = r.cmpltn_menu_choices
+        else:
+            r.cmpltn_menu_choices = completions = \
+                                        r.get_completions(stem)
+        if len(completions) == 0:
+            r.error("no matches")
+        elif len(completions) == 1:
+            if r.assume_immutable_completions and \
+                   len(completions[0]) == len(stem) and \
+                   r.last_command_is(self.__class__):
+                r.msg = "[ sole completion ]"
+                r.dirty = 1
+            r.insert(completions[0][len(stem):])
+        else:
+            p = prefix(completions, len(stem))
+            if p <> '':
+                r.insert(p)
+            if r.last_command_is(self.__class__):
+                if not r.cmpltn_menu_vis:
+                    r.cmpltn_menu_vis = 1
+                r.cmpltn_menu, r.cmpltn_menu_end = build_menu(
+                    r.console, completions, r.cmpltn_menu_end,
+                    r.use_brackets, r.sort_in_column)
+                r.dirty = 1
+            elif stem + p in completions:
+                r.msg = "[ complete but not unique ]"
+                r.dirty = 1
+            else:
+                r.msg = "[ not unique ]"
+                r.dirty = 1
+
+class self_insert(commands.self_insert):
+    def do(self):
+        commands.self_insert.do(self)
+        r = self.reader
+        if r.cmpltn_menu_vis:
+            stem = r.get_stem()
+            if len(stem) < 1:
+                r.cmpltn_reset()
+            else:
+                completions = [w for w in r.cmpltn_menu_choices
+                               if w.startswith(stem)]
+                if completions:
+                    r.cmpltn_menu, r.cmpltn_menu_end = build_menu(
+                        r.console, completions, 0,
+                        r.use_brackets, r.sort_in_column)
+                else:
+                    r.cmpltn_reset()
+
+class CompletingReader(Reader):
+    """Adds completion support
+
+    Adds instance variables:
+      * cmpltn_menu, cmpltn_menu_vis, cmpltn_menu_end, cmpltn_choices:
+      *
+    """
+    # see the comment for the complete command
+    assume_immutable_completions = True
+    use_brackets = True # display completions inside []
+    sort_in_column = False
+    
+    def collect_keymap(self):
+        return super(CompletingReader, self).collect_keymap() + (
+            (r'\t', 'complete'),)
+    
+    def __init__(self, console):
+        super(CompletingReader, self).__init__(console)
+        self.cmpltn_menu = ["[ menu 1 ]", "[ menu 2 ]"]
+        self.cmpltn_menu_vis = 0
+        self.cmpltn_menu_end = 0
+        for c in [complete, self_insert]:
+            self.commands[c.__name__] = c
+            self.commands[c.__name__.replace('_', '-')] = c        
+
+    def after_command(self, cmd):
+        super(CompletingReader, self).after_command(cmd)
+        if not isinstance(cmd, complete) and not isinstance(cmd, self_insert):
+            self.cmpltn_reset()
+
+    def calc_screen(self):
+        screen = super(CompletingReader, self).calc_screen()
+        if self.cmpltn_menu_vis:
+            ly = self.lxy[1]
+            screen[ly:ly] = self.cmpltn_menu
+            self.screeninfo[ly:ly] = [(0, [])]*len(self.cmpltn_menu)
+            self.cxy = self.cxy[0], self.cxy[1] + len(self.cmpltn_menu)
+        return screen
+
+    def finish(self):
+        super(CompletingReader, self).finish()
+        self.cmpltn_reset()
+
+    def cmpltn_reset(self):
+        self.cmpltn_menu = []
+        self.cmpltn_menu_vis = 0
+        self.cmpltn_menu_end = 0
+        self.cmpltn_menu_choices = []        
+
+    def get_stem(self):
+        st = self.syntax_table
+        SW = reader.SYNTAX_WORD
+        b = self.buffer
+        p = self.pos - 1
+        while p >= 0 and st.get(b[p], SW) == SW:
+            p -= 1
+        return u''.join(b[p+1:self.pos])
+
+    def get_completions(self, stem):
+        return []
+
+def test():
+    class TestReader(CompletingReader):
+        def get_completions(self, stem):
+            return [s for l in map(lambda x:x.split(),self.history)
+                    for s in l if s and s.startswith(stem)]
+    reader = TestReader()
+    reader.ps1 = "c**> "
+    reader.ps2 = "c/*> "
+    reader.ps3 = "c|*> "
+    reader.ps4 = "c\*> "
+    while reader.readline():
+        pass
+
+if __name__=='__main__':
+    test()

lib_pypy/pyrepl/console.py

+#   Copyright 2000-2004 Michael Hudson-Doyle <micahel@gmail.com>
+#
+#                        All Rights Reserved
+#
+#
+# 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.
+
+class Event:
+    """An Event.  `evt' is 'key' or somesuch."""
+
+    def __init__(self, evt, data, raw=''):
+        self.evt = evt
+        self.data = data
+        self.raw = raw
+
+    def __repr__(self):
+        return 'Event(%r, %r)'%(self.evt, self.data)
+
+class Console:
+    """Attributes:
+
+    screen,
+    height,
+    width,
+    """
+    
+    def refresh(self, screen, xy):
+        pass
+
+    def prepare(self):
+        pass
+
+    def restore(self):
+        pass
+
+    def move_cursor(self, x, y):
+        pass
+
+    def set_cursor_vis(self, vis):
+        pass
+
+    def getheightwidth(self):
+        """Return (height, width) where height and width are the height
+        and width of the terminal window in characters."""
+        pass
+
+    def get_event(self, block=1):
+        """Return an Event instance.  Returns None if |block| is false
+        and there is no event pending, otherwise waits for the
+        completion of an event."""
+        pass
+
+    def beep(self):
+        pass
+
+    def clear(self):
+        """Wipe the screen"""
+        pass
+
+    def finish(self):
+        """Move the cursor to the end of the display and otherwise get
+        ready for end.  XXX could be merged with restore?  Hmm."""
+        pass
+
+    def flushoutput(self):
+        """Flush all output to the screen (assuming there's some
+        buffering going on somewhere)."""
+        pass
+
+    def forgetinput(self):
+        """Forget all pending, but not yet processed input."""
+        pass
+
+    def getpending(self):
+        """Return the characters that have been typed but not yet
+        processed."""
+        pass
+
+    def wait(self):
+        """Wait for an event."""
+        pass

lib_pypy/pyrepl/copy_code.py

+#   Copyright 2000-2004 Michael Hudson-Doyle <micahel@gmail.com>
+#
+#                        All Rights Reserved
+#
+#
+# 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.
+
+import new
+
+def copy_code_with_changes(codeobject,
+                           argcount=None,
+                           nlocals=None,
+                           stacksize=None,
+                           flags=None,
+                           code=None,
+                           consts=None,
+                           names=None,
+                           varnames=None,
+                           filename=None,
+                           name=None,
+                           firstlineno=None,
+                           lnotab=None):
+    if argcount    is None: argcount    = codeobject.co_argcount
+    if nlocals     is None: nlocals     = codeobject.co_nlocals
+    if stacksize   is None: stacksize   = codeobject.co_stacksize
+    if flags       is None: flags       = codeobject.co_flags
+    if code        is None: code        = codeobject.co_code
+    if consts      is None: consts      = codeobject.co_consts
+    if names       is None: names       = codeobject.co_names
+    if varnames    is None: varnames    = codeobject.co_varnames
+    if filename    is None: filename    = codeobject.co_filename
+    if name        is None: name        = codeobject.co_name
+    if firstlineno is None: firstlineno = codeobject.co_firstlineno
+    if lnotab      is None: lnotab      = codeobject.co_lnotab
+    return new.code(argcount,
+                    nlocals,
+                    stacksize,
+                    flags,
+                    code,
+                    consts,
+                    names,
+                    varnames,
+                    filename,
+                    name,
+                    firstlineno,
+                    lnotab)
+
+code_attrs=['argcount',
+            'nlocals',
+            'stacksize',
+            'flags',
+            'code',
+            'consts',
+            'names',
+            'varnames',
+            'filename',
+            'name',
+            'firstlineno',
+            'lnotab']
+
+

lib_pypy/pyrepl/curses.py

+
+#   Copyright 2000-2010 Michael Hudson-Doyle <micahel@gmail.com>
+#                       Armin Rigo
+#
+#                        All Rights Reserved
+#
+#
+# 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.
+
+# Some try-import logic for two purposes: avoiding to bring in the whole
+# pure Python curses package if possible; and, in _curses is not actually
+# present, falling back to _minimal_curses (which is either a ctypes-based
+# pure Python module or a PyPy built-in module).
+try:
+    import _curses
+except ImportError:
+    try:
+        import _minimal_curses as _curses
+    except ImportError:
+        # Who knows, maybe some environment has "curses" but not "_curses".
+        # If not, at least the following import gives a clean ImportError.
+        import _curses
+
+setupterm = _curses.setupterm
+tigetstr = _curses.tigetstr
+tparm = _curses.tparm
+error = _curses.error

lib_pypy/pyrepl/fancy_termios.py

+#   Copyright 2000-2004 Michael Hudson-Doyle <micahel@gmail.com>
+#
+#                        All Rights Reserved
+#
+#
+# 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.
+
+import termios
+
+class TermState:
+    def __init__(self, tuples):
+        self.iflag, self.oflag, self.cflag, self.lflag, \
+                    self.ispeed, self.ospeed, self.cc = tuples
+    def as_list(self):
+        return [self.iflag, self.oflag, self.cflag, self.lflag,
+                self.ispeed, self.ospeed, self.cc]
+
+    def copy(self):
+        return self.__class__(self.as_list())
+
+def tcgetattr(fd):
+    return TermState(termios.tcgetattr(fd))
+
+def tcsetattr(fd, when, attrs):
+    termios.tcsetattr(fd, when, attrs.as_list())
+
+class Term(TermState):
+    TS__init__ = TermState.__init__
+    def __init__(self, fd=0):
+        self.TS__init__(termios.tcgetattr(fd))
+        self.fd = fd
+        self.stack = []
+    def save(self):
+        self.stack.append( self.as_list() )
+    def set(self, when=termios.TCSANOW):
+        termios.tcsetattr(self.fd, when, self.as_list())
+    def restore(self):
+        self.TS__init__(self.stack.pop())
+        self.set()
+        

lib_pypy/pyrepl/historical_reader.py

+#   Copyright 2000-2004 Michael Hudson-Doyle <micahel@gmail.com>
+#
+#                        All Rights Reserved
+#
+#
+# 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.
+
+from pyrepl import reader, commands
+from pyrepl.reader import Reader as R
+
+isearch_keymap = tuple(
+    [('\\%03o'%c, 'isearch-end') for c in range(256) if chr(c) != '\\'] + \
+    [(c, 'isearch-add-character')
+     for c in map(chr, range(32, 127)) if c != '\\'] + \
+    [('\\%03o'%c, 'isearch-add-character')
+     for c in range(256) if chr(c).isalpha() and chr(c) != '\\'] + \
+    [('\\\\', 'self-insert'),
+     (r'\C-r', 'isearch-backwards'),
+     (r'\C-s', 'isearch-forwards'),
+     (r'\C-c', 'isearch-cancel'),
+     (r'\C-g', 'isearch-cancel'),
+     (r'\<backspace>', 'isearch-backspace')])
+
+del c
+
+ISEARCH_DIRECTION_NONE = ''
+ISEARCH_DIRECTION_BACKWARDS = 'r'
+ISEARCH_DIRECTION_FORWARDS = 'f'
+
+class next_history(commands.Command):
+    def do(self):
+        r = self.reader
+        if r.historyi == len(r.history):
+            r.error("end of history list")
+            return
+        r.select_item(r.historyi + 1)
+
+class previous_history(commands.Command):
+    def do(self):
+        r = self.reader
+        if r.historyi == 0:
+            r.error("start of history list")
+            return
+        r.select_item(r.historyi - 1)
+
+class restore_history(commands.Command):
+    def do(self):
+        r = self.reader
+        if r.historyi != len(r.history):
+            if r.get_unicode() != r.history[r.historyi]:
+                r.buffer = list(r.history[r.historyi])
+                r.pos = len(r.buffer)
+                r.dirty = 1
+
+class first_history(commands.Command):
+    def do(self):
+        self.reader.select_item(0)
+
+class last_history(commands.Command):
+    def do(self):
+        self.reader.select_item(len(self.reader.history))
+
+class operate_and_get_next(commands.FinishCommand):
+    def do(self):
+        self.reader.next_history = self.reader.historyi + 1
+
+class yank_arg(commands.Command):
+    def do(self):
+        r = self.reader
+        if r.last_command is self.__class__:
+            r.yank_arg_i += 1
+        else:
+            r.yank_arg_i = 0
+        if r.historyi < r.yank_arg_i:
+            r.error("beginning of history list")
+            return
+        a = r.get_arg(-1)
+        # XXX how to split?
+        words = r.get_item(r.historyi - r.yank_arg_i - 1).split()
+        if a < -len(words) or a >= len(words):
+            r.error("no such arg")
+            return
+        w = words[a]
+        b = r.buffer
+        if r.yank_arg_i > 0:
+            o = len(r.yank_arg_yanked)
+        else:
+            o = 0
+        b[r.pos - o:r.pos] = list(w)
+        r.yank_arg_yanked = w
+        r.pos += len(w) - o
+        r.dirty = 1
+
+class forward_history_isearch(commands.Command):
+    def do(self):
+        r = self.reader
+        r.isearch_direction = ISEARCH_DIRECTION_FORWARDS
+        r.isearch_start = r.historyi, r.pos
+        r.isearch_term = ''
+        r.dirty = 1
+        r.push_input_trans(r.isearch_trans)
+        
+
+class reverse_history_isearch(commands.Command):
+    def do(self):
+        r = self.reader
+        r.isearch_direction = ISEARCH_DIRECTION_BACKWARDS
+        r.dirty = 1
+        r.isearch_term = ''
+        r.push_input_trans(r.isearch_trans)
+        r.isearch_start = r.historyi, r.pos
+
+class isearch_cancel(commands.Command):
+    def do(self):
+        r = self.reader
+        r.isearch_direction = ISEARCH_DIRECTION_NONE
+        r.pop_input_trans()
+        r.select_item(r.isearch_start[0])
+        r.pos = r.isearch_start[1]
+        r.dirty = 1
+
+class isearch_add_character(commands.Command):
+    def do(self):
+        r = self.reader
+        b = r.buffer
+        r.isearch_term += self.event[-1]
+        r.dirty = 1
+        p = r.pos + len(r.isearch_term) - 1
+        if b[p:p+1] != [r.isearch_term[-1]]:
+            r.isearch_next()
+
+class isearch_backspace(commands.Command):
+    def do(self):
+        r = self.reader
+        if len(r.isearch_term) > 0:
+            r.isearch_term = r.isearch_term[:-1]
+            r.dirty = 1
+        else:
+            r.error("nothing to rubout")
+
+class isearch_forwards(commands.Command):
+    def do(self):
+        r = self.reader
+        r.isearch_direction = ISEARCH_DIRECTION_FORWARDS
+        r.isearch_next()
+
+class isearch_backwards(commands.Command):
+    def do(self):
+        r = self.reader
+        r.isearch_direction = ISEARCH_DIRECTION_BACKWARDS
+        r.isearch_next()
+
+class isearch_end(commands.Command):
+    def do(self):
+        r = self.reader
+        r.isearch_direction = ISEARCH_DIRECTION_NONE
+        r.console.forgetinput()
+        r.pop_input_trans()
+        r.dirty = 1
+
+class HistoricalReader(R):
+    """Adds history support (with incremental history searching) to the
+    Reader class.
+
+    Adds the following instance variables:
+      * history:
+        a list of strings
+      * historyi:
+      * transient_history:
+      * next_history:
+      * isearch_direction, isearch_term, isearch_start:
+      * yank_arg_i, yank_arg_yanked:
+        used by the yank-arg command; not actually manipulated by any
+        HistoricalReader instance methods.
+    """
+
+    def collect_keymap(self):
+        return super(HistoricalReader, self).collect_keymap() + (
+            (r'\C-n', 'next-history'),
+            (r'\C-p', 'previous-history'),
+            (r'\C-o', 'operate-and-get-next'),
+            (r'\C-r', 'reverse-history-isearch'),
+            (r'\C-s', 'forward-history-isearch'),
+            (r'\M-r', 'restore-history'),
+            (r'\M-.', 'yank-arg'),
+            (r'\<page down>', 'last-history'),
+            (r'\<page up>', 'first-history'))
+
+
+    def __init__(self, console):
+        super(HistoricalReader, self).__init__(console)
+        self.history = []
+        self.historyi = 0
+        self.transient_history = {}
+        self.next_history = None
+        self.isearch_direction = ISEARCH_DIRECTION_NONE
+        for c in [next_history, previous_history, restore_history,
+                  first_history, last_history, yank_arg,
+                  forward_history_isearch, reverse_history_isearch,
+                  isearch_end, isearch_add_character, isearch_cancel,
+                  isearch_add_character, isearch_backspace,
+                  isearch_forwards, isearch_backwards, operate_and_get_next]:
+            self.commands[c.__name__] = c
+            self.commands[c.__name__.replace('_', '-')] = c
+        from pyrepl import input
+        self.isearch_trans = input.KeymapTranslator(
+            isearch_keymap, invalid_cls=isearch_end,
+            character_cls=isearch_add_character)
+        
+    def select_item(self, i):
+        self.transient_history[self.historyi] = self.get_unicode()
+        buf = self.transient_history.get(i)
+        if buf is None:
+            buf = self.history[i]
+        self.buffer = list(buf)
+        self.historyi = i
+        self.pos = len(self.buffer)
+        self.dirty = 1
+
+    def get_item(self, i):
+        if i <> len(self.history):
+            return self.transient_history.get(i, self.history[i])
+        else:
+            return self.transient_history.get(i, self.get_unicode())
+
+    def prepare(self):
+        super(HistoricalReader, self).prepare()
+        try:
+            self.transient_history = {}
+            if self.next_history is not None \
+               and self.next_history < len(self.history):
+                self.historyi = self.next_history
+                self.buffer[:] = list(self.history[self.next_history])
+                self.pos = len(self.buffer)
+                self.transient_history[len(self.history)] = ''
+            else:
+                self.historyi = len(self.history)
+            self.next_history = None
+        except:
+            self.restore()
+            raise
+
+    def get_prompt(self, lineno, cursor_on_line):
+        if cursor_on_line and self.isearch_direction <> ISEARCH_DIRECTION_NONE:
+            d = 'rf'[self.isearch_direction == ISEARCH_DIRECTION_FORWARDS]
+            return "(%s-search `%s') "%(d, self.isearch_term)
+        else:
+            return super(HistoricalReader, self).get_prompt(lineno, cursor_on_line)
+
+    def isearch_next(self):
+        st = self.isearch_term
+        p = self.pos
+        i = self.historyi
+        s = self.get_unicode()
+        forwards = self.isearch_direction == ISEARCH_DIRECTION_FORWARDS
+        while 1:
+            if forwards:
+                p = s.find(st, p + 1)
+            else:
+                p = s.rfind(st, 0, p + len(st) - 1)
+            if p != -1:
+                self.select_item(i)
+                self.pos = p
+                return
+            elif ((forwards and i == len(self.history) - 1)
+                  or (not forwards and i == 0)):
+                self.error("not found")
+                return
+            else:
+                if forwards:
+                    i += 1
+                    s = self.get_item(i)
+                    p = -1
+                else:
+                    i -= 1
+                    s = self.get_item(i)
+                    p = len(s)
+
+    def finish(self):
+        super(HistoricalReader, self).finish()
+        ret = self.get_unicode()
+        for i, t in self.transient_history.items():
+            if i < len(self.history) and i != self.historyi:
+                self.history[i] = t
+        if ret:
+            self.history.append(ret)
+
+def test():
+    from pyrepl.unix_console import UnixConsole
+    reader = HistoricalReader(UnixConsole())
+    reader.ps1 = "h**> "
+    reader.ps2 = "h/*> "
+    reader.ps3 = "h|*> "
+    reader.ps4 = "h\*> "
+    while reader.readline():
+        pass
+
+if __name__=='__main__':
+    test()

lib_pypy/pyrepl/input.py

+#   Copyright 2000-2004 Michael Hudson-Doyle <micahel@gmail.com>
+#
+#                        All Rights Reserved
+#
+#
+# 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.
+
+# (naming modules after builtin functions is not such a hot idea...)
+
+# an KeyTrans instance translates Event objects into Command objects
+
+# hmm, at what level do we want [C-i] and [tab] to be equivalent?
+# [meta-a] and [esc a]?  obviously, these are going to be equivalent
+# for the UnixConsole, but should they be for PygameConsole?
+
+# it would in any situation seem to be a bad idea to bind, say, [tab]
+# and [C-i] to *different* things... but should binding one bind the
+# other?
+
+# executive, temporary decision: [tab] and [C-i] are distinct, but
+# [meta-key] is identified with [esc key].  We demand that any console
+# class does quite a lot towards emulating a unix terminal.
+
+from pyrepl import unicodedata_
+
+class InputTranslator(object):
+    def push(self, evt):
+        pass
+    def get(self):
+        pass
+    def empty(self):
+        pass
+
+class KeymapTranslator(InputTranslator):
+    def __init__(self, keymap, verbose=0,
+                 invalid_cls=None, character_cls=None):
+        self.verbose = verbose
+        from pyrepl.keymap import compile_keymap, parse_keys
+        self.keymap = keymap
+        self.invalid_cls = invalid_cls
+        self.character_cls = character_cls
+        d = {}
+        for keyspec, command in keymap:
+            keyseq = tuple(parse_keys(keyspec))
+            d[keyseq] = command
+        if self.verbose:
+            print d
+        self.k = self.ck = compile_keymap(d, ())
+        self.results = []
+        self.stack = []
+    def push(self, evt):
+        if self.verbose:
+            print "pushed", evt.data,
+        key = evt.data
+        d = self.k.get(key)
+        if isinstance(d, dict):
+            if self.verbose:
+                print "transition"
+            self.stack.append(key)
+            self.k = d
+        else:
+            if d is None:
+                if self.verbose:
+                    print "invalid"
+                if self.stack or len(key) > 1 or unicodedata_.category(key) == 'C':
+                    self.results.append(
+                        (self.invalid_cls, self.stack + [key]))
+                else:
+                    # small optimization:
+                    self.k[key] = self.character_cls
+                    self.results.append(
+                        (self.character_cls, [key]))
+            else:
+                if self.verbose:
+                    print "matched", d
+                self.results.append((d, self.stack + [key]))
+            self.stack = []
+            self.k = self.ck
+    def get(self):
+        if self.results:
+            return self.results.pop(0)
+        else:
+            return None
+    def empty(self):
+        return not self.results

lib_pypy/pyrepl/keymap.py

+#   Copyright 2000-2008 Michael Hudson-Doyle <micahel@gmail.com>
+#                       Armin Rigo
+#
+#                        All Rights Reserved
+#
+#
+# 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.
+
+"""
+functions for parsing keyspecs
+
+Support for turning keyspecs into appropriate sequences.
+
+pyrepl uses it's own bastardized keyspec format, which is meant to be
+a strict superset of readline's \"KEYSEQ\" format (which is to say
+that if you can come up with a spec readline accepts that this
+doesn't, you've found a bug and should tell me about it).
+
+Note that this is the `\\C-o' style of readline keyspec, not the
+`Control-o' sort.
+
+A keyspec is a string representing a sequence of keypresses that can
+be bound to a command.
+
+All characters other than the backslash represent themselves.  In the
+traditional manner, a backslash introduces a escape sequence.
+
+The extension to readline is that the sequence \\<KEY> denotes the
+sequence of charaters produced by hitting KEY.
+
+Examples:
+
+`a'     - what you get when you hit the `a' key
+`\\EOA'  - Escape - O - A (up, on my terminal)
+`\\<UP>' - the up arrow key
+`\\<up>' - ditto (keynames are case insensitive)
+`\\C-o', `\\c-o'  - control-o
+`\\M-.'  - meta-period
+`\\E.'   - ditto (that's how meta works for pyrepl)
+`\\<tab>', `\\<TAB>', `\\t', `\\011', '\\x09', '\\X09', '\\C-i', '\\C-I'
+   - all of these are the tab character.  Can you think of any more?
+"""
+
+_escapes = {
+    '\\':'\\',
+    "'":"'",
+    '"':'"',
+    'a':'\a',
+    'b':'\h',
+    'e':'\033',
+    'f':'\f',
+    'n':'\n',
+    'r':'\r',
+    't':'\t',
+    'v':'\v'
+    }
+
+_keynames = {
+    'backspace': 'backspace',
+    'delete':    'delete',
+    'down':      'down',
+    'end':       'end',
+    'enter':     '\r',
+    'escape':    '\033',
+    'f1' : 'f1',   'f2' : 'f2',   'f3' : 'f3',   'f4' : 'f4',
+    'f5' : 'f5',   'f6' : 'f6',   'f7' : 'f7',   'f8' : 'f8',
+    'f9' : 'f9',   'f10': 'f10',  'f11': 'f11',  'f12': 'f12',
+    'f13': 'f13',  'f14': 'f14',  'f15': 'f15',  'f16': 'f16',
+    'f17': 'f17',  'f18': 'f18',  'f19': 'f19',  'f20': 'f20',
+    'home':      'home',
+    'insert':    'insert',
+    'left':      'left',
+    'page down': 'page down',
+    'page up':   'page up',
+    'return':    '\r',
+    'right':     'right',
+    'space':     ' ',
+    'tab':       '\t',
+    'up':        'up',
+    }
+
+class KeySpecError(Exception):
+    pass
+
+def _parse_key1(key, s):
+    ctrl = 0
+    meta = 0
+    ret = ''
+    while not ret and s < len(key):
+        if key[s] == '\\':
+            c = key[s+1].lower()
+            if _escapes.has_key(c):
+                ret = _escapes[c]
+                s += 2
+            elif c == "c":
+                if key[s + 2] != '-':
+                    raise KeySpecError, \
+                              "\\C must be followed by `-' (char %d of %s)"%(
+                        s + 2, repr(key))
+                if ctrl:
+                    raise KeySpecError, "doubled \\C- (char %d of %s)"%(
+                        s + 1, repr(key))
+                ctrl = 1
+                s += 3
+            elif c == "m":
+                if key[s + 2] != '-':
+                    raise KeySpecError, \
+                              "\\M must be followed by `-' (char %d of %s)"%(
+                        s + 2, repr(key))
+                if meta:
+                    raise KeySpecError, "doubled \\M- (char %d of %s)"%(
+                        s + 1, repr(key))
+                meta = 1
+                s += 3
+            elif c.isdigit():
+                n = key[s+1:s+4]
+                ret = chr(int(n, 8))
+                s += 4
+            elif c == 'x':
+                n = key[s+2:s+4]
+                ret = chr(int(n, 16))
+                s += 4
+            elif c == '<':
+                t = key.find('>', s)
+                if t == -1:
+                    raise KeySpecError, \
+                              "unterminated \\< starting at char %d of %s"%(
+                        s + 1, repr(key))                        
+                ret = key[s+2:t].lower()
+                if ret not in _keynames:
+                    raise KeySpecError, \
+                              "unrecognised keyname `%s' at char %d of %s"%(
+                        ret, s + 2, repr(key))
+                ret = _keynames[ret]
+                s = t + 1
+            else:
+                raise KeySpecError, \
+                          "unknown backslash escape %s at char %d of %s"%(
+                    `c`, s + 2, repr(key))
+        else:
+            ret = key[s]
+            s += 1
+    if ctrl:
+        if len(ret) > 1:
+            raise KeySpecError, "\\C- must be followed by a character"
+        ret = chr(ord(ret) & 0x1f)   # curses.ascii.ctrl()
+    if meta:
+        ret = ['\033', ret]
+    else:
+        ret = [ret]
+    return ret, s
+
+def parse_keys(key):
+    s = 0
+    r = []
+    while s < len(key):
+        k, s = _parse_key1(key, s)
+        r.extend(k)
+    return r
+
+def compile_keymap(keymap, empty=''):
+    r = {}
+    for key, value in keymap.items():
+        r.setdefault(key[0], {})[key[1:]] = value
+    for key, value in r.items():
+        if empty in value:
+            if len(value) <> 1:
+                raise KeySpecError, \
+                      "key definitions for %s clash"%(value.values(),)
+            else:
+                r[key] = value[empty]
+        else:
+            r[key] = compile_keymap(value, empty)
+    return r

lib_pypy/pyrepl/keymaps.py

+#   Copyright 2000-2004 Michael Hudson-Doyle <micahel@gmail.com>
+#
+#                        All Rights Reserved
+#
+#
+# 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.
+
+reader_emacs_keymap = tuple(
+    [(r'\C-a', 'beginning-of-line'),
+     (r'\C-b', 'left'),
+     (r'\C-c', 'interrupt'),
+     (r'\C-d', 'delete'),
+     (r'\C-e', 'end-of-line'),
+     (r'\C-f', 'right'),
+     (r'\C-g', 'cancel'),
+     (r'\C-h', 'backspace'),
+     (r'\C-j', 'self-insert'),
+     (r'\<return>', 'accept'),
+     (r'\C-k', 'kill-line'),
+     (r'\C-l', 'clear-screen'),
+#     (r'\C-m', 'accept'),
+     (r'\C-q', 'quoted-insert'),
+     (r'\C-t', 'transpose-characters'),
+     (r'\C-u', 'unix-line-discard'),
+     (r'\C-v', 'quoted-insert'),
+     (r'\C-w', 'unix-word-rubout'),
+     (r'\C-x\C-u', 'upcase-region'),
+     (r'\C-y', 'yank'),
+     (r'\C-z', 'suspend'),
+     
+     (r'\M-b', 'backward-word'),
+     (r'\M-c', 'capitalize-word'),
+     (r'\M-d', 'kill-word'),
+     (r'\M-f', 'forward-word'),
+     (r'\M-l', 'downcase-word'),
+     (r'\M-t', 'transpose-words'),
+     (r'\M-u', 'upcase-word'),
+     (r'\M-y', 'yank-pop'),
+     (r'\M--', 'digit-arg'),
+     (r'\M-0', 'digit-arg'),
+     (r'\M-1', 'digit-arg'),
+     (r'\M-2', 'digit-arg'),
+     (r'\M-3', 'digit-arg'),
+     (r'\M-4', 'digit-arg'),
+     (r'\M-5', 'digit-arg'),
+     (r'\M-6', 'digit-arg'),
+     (r'\M-7', 'digit-arg'),
+     (r'\M-8', 'digit-arg'),
+     (r'\M-9', 'digit-arg'),
+     (r'\M-\n', 'self-insert'),
+     (r'\<backslash>', 'self-insert')] + \
+    [(c, 'self-insert')
+     for c in map(chr, range(32, 127)) if c <> '\\'] + \
+    [(c, 'self-insert')
+     for c in map(chr, range(128, 256)) if c.isalpha()] + \
+    [(r'\<up>', 'up'),
+     (r'\<down>', 'down'),
+     (r'\<left>', 'left'),
+     (r'\<right>', 'right'),
+     (r'\<insert>', 'quoted-insert'),
+     (r'\<delete>', 'delete'),
+     (r'\<backspace>', 'backspace'),
+     (r'\M-\<backspace>', 'backward-kill-word'),
+     (r'\<end>', 'end'),
+     (r'\<home>', 'home'),
+     (r'\<f1>', 'help'),
+     (r'\EOF', 'end'),  # the entries in the terminfo database for xterms
+     (r'\EOH', 'home'), # seem to be wrong.  this is a less than ideal
+                        # workaround
+     ])
+
+hist_emacs_keymap = reader_emacs_keymap + (
+    (r'\C-n', 'next-history'),
+    (r'\C-p', 'previous-history'),
+    (r'\C-o', 'operate-and-get-next'),
+    (r'\C-r', 'reverse-history-isearch'),
+    (r'\C-s', 'forward-history-isearch'),
+    (r'\M-r', 'restore-history'),
+    (r'\M-.', 'yank-arg'),
+    (r'\<page down>', 'last-history'),
+    (r'\<page up>', 'first-history'))
+
+comp_emacs_keymap = hist_emacs_keymap + (
+    (r'\t', 'complete'),)
+
+python_emacs_keymap = comp_emacs_keymap + (
+    (r'\n', 'maybe-accept'),
+    (r'\M-\n', 'self-insert'))
+    
+reader_vi_insert_keymap = tuple(
+    [(c, 'self-insert')
+     for c in map(chr, range(32, 127)) if c <> '\\'] + \
+    [(c, 'self-insert')
+     for c in map(chr, range(128, 256)) if c.isalpha()] + \
+    [(r'\C-d', 'delete'),
+     (r'\<backspace>', 'backspace'),
+     ('')])
+
+reader_vi_command_keymap = tuple(
+    [
+    ('E', 'enter-emacs-mode'),
+    ('R', 'enter-replace-mode'),
+    ('dw', 'delete-word'),
+    ('dd', 'delete-line'),
+    
+    ('h', 'left'),
+    ('i', 'enter-insert-mode'),
+    ('j', 'down'),
+    ('k', 'up'),
+    ('l', 'right'),
+    ('r', 'replace-char'),
+    ('w', 'forward-word'),
+    ('x', 'delete'),
+    ('.', 'repeat-edit'), # argh!
+    (r'\<insert>', 'enter-insert-mode'),
+     ] + 
+    [(c, 'digit-arg') for c in '01234567689'] +
+    [])
+   
+
+reader_keymaps = {
+    'emacs' : reader_emacs_keymap,
+    'vi-insert' : reader_vi_insert_keymap,
+    'vi-command' : reader_vi_command_keymap
+    }
+
+del c # from the listcomps
+

lib_pypy/pyrepl/module_lister.py

+#   Copyright 2000-2004 Michael Hudson-Doyle <micahel@gmail.com>
+#
+#                        All Rights Reserved
+#
+#
+# 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.
+
+from pyrepl.completing_reader import uniqify
+import os, sys
+
+# for the completion support.
+# this is all quite nastily written.
+_packages = {}
+
+def _make_module_list_dir(dir, suffs, prefix=''):
+    l = []
+    for fname in os.listdir(dir):
+        file = os.path.join(dir, fname)
+        if os.path.isfile(file):
+            for suff in suffs:
+                if fname.endswith(suff):
+                    l.append( prefix + fname[:-len(suff)] )
+                    break
+        elif os.path.isdir(file) \
+             and os.path.exists(os.path.join(file, "__init__.py")):
+            l.append( prefix + fname )
+            _packages[prefix + fname] = _make_module_list_dir(
+                file, suffs, prefix + fname + '.' )
+    l = uniqify(l)
+    l.sort()
+    return l
+
+def _make_module_list():
+    import imp
+    suffs = [x[0] for x in imp.get_suffixes() if x[0] != '.pyc']
+    def compare(x, y):
+        c = -cmp(len(x), len(y))
+        if c:
+            return c
+        else:
+            return -cmp(x, y)
+    suffs.sort(compare)
+    _packages[''] = list(sys.builtin_module_names)
+    for dir in sys.path:
+        if dir == '':
+            dir = '.'
+        if os.path.isdir(dir):
+            _packages[''] += _make_module_list_dir(dir, suffs)
+    _packages[''].sort()
+
+def find_modules(stem):
+    l = stem.split('.')
+    pack = '.'.join(l[:-1])
+    try:
+        mods = _packages[pack]
+    except KeyError:
+        raise ImportError, "can't find \"%s\" package"%pack
+    return [mod for mod in mods if mod.startswith(stem)]

lib_pypy/pyrepl/pygame_console.py

+#   Copyright 2000-2004 Michael Hudson-Doyle <micahel@gmail.com>
+#
+#                        All Rights Reserved
+#
+#
+# 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.
+
+# the pygame console is currently thoroughly broken.
+
+# there's a fundamental difference from the UnixConsole: here we're
+# the terminal emulator too, in effect.  This means, e.g., for pythoni
+# we really need a separate process (or thread) to monitor for ^C
+# during command execution and zap the executor process.  Making this
+# work on non-Unix is expected to be even more entertaining.
+
+from pygame.locals import *
+from pyrepl.console import Console, Event
+from pyrepl import pygame_keymap
+import pygame
+import types
+
+lmargin = 5
+rmargin = 5
+tmargin = 5
+bmargin = 5
+
+try:
+    bool
+except NameError:
+    def bool(x):
+        return not not x
+
+modcolors = {K_LCTRL:1,
+             K_RCTRL:1,
+             K_LMETA:1,
+             K_RMETA:1,
+             K_LALT:1,
+             K_RAL