Author: Ronny Pfannschmidt <[email protected]>
Branch: 
Changeset: r172:a1bbaca0edcc
Date: 2012-05-03 10:21 +0200
http://bitbucket.org/pypy/pyrepl/changeset/a1bbaca0edcc/

Log:    merge py3k support branch

diff --git a/pyrepl/_minimal_curses.py b/pyrepl/_minimal_curses.py
new file mode 100644
--- /dev/null
+++ b/pyrepl/_minimal_curses.py
@@ -0,0 +1,69 @@
+"""Minimal '_curses' module, the low-level interface for curses module
+which is not meant to be used directly.
+
+Based on ctypes.  It's too incomplete to be really called '_curses', so
+to use it, you have to import it and stick it in sys.modules['_curses']
+manually.
+
+Note that there is also a built-in module _minimal_curses which will
+hide this one if compiled in.
+"""
+
+import ctypes, ctypes.util
+
+class error(Exception):
+    pass
+
+
+def _find_clib():
+    trylibs = ['ncurses', 'curses']
+
+    for lib in trylibs:
+        path = ctypes.util.find_library(lib)
+        if path:
+            return path
+    raise ImportError("curses library not found")
+
+_clibpath = _find_clib()
+clib = ctypes.cdll.LoadLibrary(_clibpath)
+
+clib.setupterm.argtypes = [ctypes.c_char_p, ctypes.c_int,
+                           ctypes.POINTER(ctypes.c_int)]
+clib.setupterm.restype = ctypes.c_int
+
+clib.tigetstr.argtypes = [ctypes.c_char_p]
+clib.tigetstr.restype = ctypes.POINTER(ctypes.c_char)
+
+clib.tparm.argtypes = [ctypes.c_char_p] + 9 * [ctypes.c_int]
+clib.tparm.restype = ctypes.c_char_p
+
+OK = 0
+ERR = -1
+
+# ____________________________________________________________
+
+try: from __pypy__ import builtinify
+except ImportError: builtinify = lambda f: f
+
+@builtinify
+def setupterm(termstr, fd):
+    err = ctypes.c_int(0)
+    result = clib.setupterm(termstr, fd, ctypes.byref(err))
+    if result == ERR:
+        raise error("setupterm() failed (err=%d)" % err.value)
+
+@builtinify
+def tigetstr(cap):
+    if not isinstance(cap, bytes):
+        cap = cap.encode('ascii')
+    result = clib.tigetstr(cap)
+    if ctypes.cast(result, ctypes.c_void_p).value == ERR:
+        return None
+    return ctypes.cast(result, ctypes.c_char_p).value
+
+@builtinify
+def tparm(str, i1=0, i2=0, i3=0, i4=0, i5=0, i6=0, i7=0, i8=0, i9=0):
+    result = clib.tparm(str, i1, i2, i3, i4, i5, i6, i7, i8, i9)
+    if result is None:
+        raise error("tparm() returned NULL")
+    return result
diff --git a/pyrepl/cmdrepl.py b/pyrepl/cmdrepl.py
--- a/pyrepl/cmdrepl.py
+++ b/pyrepl/cmdrepl.py
@@ -33,7 +33,7 @@
 which is in fact done by the `pythoni' script that comes with
 pyrepl."""
 
-from __future__ import nested_scopes
+from __future__ import print_function
 
 from pyrepl import completing_reader as cr, reader, completer
 from pyrepl.completing_reader import CompletingReader as CR
@@ -96,7 +96,7 @@
             if intro is not None:
                 self.intro = intro
             if self.intro:
-                print self.intro
+                print(self.intro)
             stop = None
             while not stop:
                 if self.cmdqueue:
diff --git a/pyrepl/commands.py b/pyrepl/commands.py
--- a/pyrepl/commands.py
+++ b/pyrepl/commands.py
@@ -33,10 +33,12 @@
 class Command(object):
     finish = 0
     kills_digit_arg = 1
-    def __init__(self, reader, (event_name, event)):
+
+    def __init__(self, reader, event_name, event):
         self.reader = reader
         self.event = event
         self.event_name = event_name
+
     def do(self):
         pass
 
diff --git a/pyrepl/completer.py b/pyrepl/completer.py
--- a/pyrepl/completer.py
+++ b/pyrepl/completer.py
@@ -17,7 +17,10 @@
 # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-import __builtin__
+try:
+    import __builtin__ as builtins
+except ImportError:
+    import builtins
 
 class Completer:
     def __init__(self, ns):
@@ -38,12 +41,11 @@
         """
         import keyword
         matches = []
-        n = len(text)
         for list in [keyword.kwlist,
-                     __builtin__.__dict__.keys(),
+                     builtins.__dict__.keys(),
                      self.ns.keys()]:
             for word in list:
-                if word[:n] == text and word != "__builtins__":
+                if word.startswith(text) and word != "__builtins__":
                     matches.append(word)
         return matches
 
diff --git a/pyrepl/completing_reader.py b/pyrepl/completing_reader.py
--- a/pyrepl/completing_reader.py
+++ b/pyrepl/completing_reader.py
@@ -168,7 +168,7 @@
             r.insert(completions[0][len(stem):])
         else:
             p = prefix(completions, len(stem))
-            if p <> '':
+            if p:
                 r.insert(p)
             if r.last_command_is(self.__class__):
                 if not r.cmpltn_menu_vis:
@@ -259,7 +259,7 @@
         p = self.pos - 1
         while p >= 0 and st.get(b[p], SW) == SW:
             p -= 1
-        return u''.join(b[p+1:self.pos])
+        return ''.join(b[p+1:self.pos])
 
     def get_completions(self, stem):
         return []
diff --git a/pyrepl/console.py b/pyrepl/console.py
--- a/pyrepl/console.py
+++ b/pyrepl/console.py
@@ -17,8 +17,9 @@
 # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-class Event:
+class Event(object):
     """An Event.  `evt' is 'key' or somesuch."""
+    __slots__ = 'evt', 'data', 'raw'
 
     def __init__(self, evt, data, raw=''):
         self.evt = evt
@@ -28,7 +29,7 @@
     def __repr__(self):
         return 'Event(%r, %r)'%(self.evt, self.data)
 
-class Console:
+class Console(object):
     """Attributes:
 
     screen,
diff --git a/pyrepl/copy_code.py b/pyrepl/copy_code.py
--- a/pyrepl/copy_code.py
+++ b/pyrepl/copy_code.py
@@ -17,7 +17,7 @@
 # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-import new
+from types import CodeType
 
 def copy_code_with_changes(codeobject,
                            argcount=None,
@@ -44,7 +44,7 @@
     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,
+    return CodeType(argcount,
                     nlocals,
                     stacksize,
                     flags,
diff --git a/pyrepl/curses.py b/pyrepl/curses.py
--- a/pyrepl/curses.py
+++ b/pyrepl/curses.py
@@ -19,21 +19,5 @@
 # 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
+from ._minimal_curses import setupterm, tigetstr, tparm, error
diff --git a/pyrepl/historical_reader.py b/pyrepl/historical_reader.py
--- a/pyrepl/historical_reader.py
+++ b/pyrepl/historical_reader.py
@@ -33,7 +33,8 @@
      (r'\C-g', 'isearch-cancel'),
      (r'\<backspace>', 'isearch-backspace')])
 
-del c
+if 'c' in globals():
+    del c
 
 ISEARCH_DIRECTION_NONE = ''
 ISEARCH_DIRECTION_BACKWARDS = 'r'
@@ -230,7 +231,7 @@
         self.dirty = 1
 
     def get_item(self, i):
-        if i <> len(self.history):
+        if i != len(self.history):
             return self.transient_history.get(i, self.history[i])
         else:
             return self.transient_history.get(i, self.get_unicode())
@@ -253,7 +254,7 @@
             raise
 
     def get_prompt(self, lineno, cursor_on_line):
-        if cursor_on_line and self.isearch_direction <> ISEARCH_DIRECTION_NONE:
+        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:
diff --git a/pyrepl/input.py b/pyrepl/input.py
--- a/pyrepl/input.py
+++ b/pyrepl/input.py
@@ -32,8 +32,10 @@
 # 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 __future__ import print_function
+from pyrepl import unicodedata_
+from collections import deque
 
-from pyrepl import unicodedata_
 
 class InputTranslator(object):
     def push(self, evt):
@@ -43,7 +45,9 @@
     def empty(self):
         pass
 
+
 class KeymapTranslator(InputTranslator):
+
     def __init__(self, keymap, verbose=0,
                  invalid_cls=None, character_cls=None):
         self.verbose = verbose
@@ -56,24 +60,25 @@
             keyseq = tuple(parse_keys(keyspec))
             d[keyseq] = command
         if self.verbose:
-            print d
+            print(d)
         self.k = self.ck = compile_keymap(d, ())
-        self.results = []
+        self.results = deque()
         self.stack = []
+
     def push(self, evt):
         if self.verbose:
-            print "pushed", evt.data,
+            print("pushed", evt.data, end='')
         key = evt.data
         d = self.k.get(key)
         if isinstance(d, dict):
             if self.verbose:
-                print "transition"
+                print("transition")
             self.stack.append(key)
             self.k = d
         else:
             if d is None:
                 if self.verbose:
-                    print "invalid"
+                    print("invalid")
                 if self.stack or len(key) > 1 or unicodedata_.category(key) == 
'C':
                     self.results.append(
                         (self.invalid_cls, self.stack + [key]))
@@ -84,14 +89,16 @@
                         (self.character_cls, [key]))
             else:
                 if self.verbose:
-                    print "matched", d
+                    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)
+            return self.results.popleft()
         else:
             return None
+
     def empty(self):
         return not self.results
diff --git a/pyrepl/keymap.py b/pyrepl/keymap.py
--- a/pyrepl/keymap.py
+++ b/pyrepl/keymap.py
@@ -101,27 +101,27 @@
     while not ret and s < len(key):
         if key[s] == '\\':
             c = key[s+1].lower()
-            if _escapes.has_key(c):
+            if c in _escapes:
                 ret = _escapes[c]
                 s += 2
             elif c == "c":
                 if key[s + 2] != '-':
-                    raise KeySpecError, \
+                    raise KeySpecError(
                               "\\C must be followed by `-' (char %d of %s)"%(
-                        s + 2, repr(key))
+                        s + 2, repr(key)))
                 if ctrl:
-                    raise KeySpecError, "doubled \\C- (char %d of %s)"%(
-                        s + 1, repr(key))
+                    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, \
+                    raise KeySpecError(
                               "\\M must be followed by `-' (char %d of %s)"%(
-                        s + 2, repr(key))
+                        s + 2, repr(key)))
                 if meta:
-                    raise KeySpecError, "doubled \\M- (char %d of %s)"%(
-                        s + 1, repr(key))
+                    raise KeySpecError("doubled \\M- (char %d of %s)"%(
+                        s + 1, repr(key)))
                 meta = 1
                 s += 3
             elif c.isdigit():
@@ -135,26 +135,26 @@
             elif c == '<':
                 t = key.find('>', s)
                 if t == -1:
-                    raise KeySpecError, \
+                    raise KeySpecError(
                               "unterminated \\< starting at char %d of %s"%(
-                        s + 1, repr(key))                        
+                        s + 1, repr(key)))
                 ret = key[s+2:t].lower()
                 if ret not in _keynames:
-                    raise KeySpecError, \
+                    raise KeySpecError(
                               "unrecognised keyname `%s' at char %d of %s"%(
-                        ret, s + 2, repr(key))
+                        ret, s + 2, repr(key)))
                 ret = _keynames[ret]
                 s = t + 1
             else:
-                raise KeySpecError, \
+                raise KeySpecError(
                           "unknown backslash escape %s at char %d of %s"%(
-                    `c`, s + 2, repr(key))
+                    repr(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"
+            raise KeySpecError("\\C- must be followed by a character")
         ret = chr(ord(ret) & 0x1f)   # curses.ascii.ctrl()
     if meta:
         ret = ['\033', ret]
@@ -170,15 +170,15 @@
         r.extend(k)
     return r
 
-def compile_keymap(keymap, empty=''):
+def compile_keymap(keymap, empty=b''):
     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(),)
+            if len(value) != 1:
+                raise KeySpecError(
+                      "key definitions for %s clash"%(value.values(),))
             else:
                 r[key] = value[empty]
         else:
diff --git a/pyrepl/module_lister.py b/pyrepl/module_lister.py
--- a/pyrepl/module_lister.py
+++ b/pyrepl/module_lister.py
@@ -66,5 +66,5 @@
     try:
         mods = _packages[pack]
     except KeyError:
-        raise ImportError, "can't find \"%s\" package"%pack
+        raise ImportError("can't find \"%s\" package" % pack)
     return [mod for mod in mods if mod.startswith(stem)]
diff --git a/pyrepl/python_reader.py b/pyrepl/python_reader.py
--- a/pyrepl/python_reader.py
+++ b/pyrepl/python_reader.py
@@ -20,17 +20,25 @@
 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 # one impressive collections of imports:
+from __future__ import print_function
+from __future__ import unicode_literals
 from pyrepl.completing_reader import CompletingReader
 from pyrepl.historical_reader import HistoricalReader
 from pyrepl import completing_reader, reader
 from pyrepl import copy_code, commands, completer
 from pyrepl import module_lister
-import new, sys, os, re, code, traceback
+import imp, sys, os, re, code, traceback
 import atexit, warnings
 try:
     import cPickle as pickle
 except ImportError:
     import pickle
+
+try:
+    unicode
+except:
+    unicode = str
+
 try:
     import imp
     imp.find_module("twisted")
@@ -47,13 +55,28 @@
     """this function eats warnings, if you were wondering"""
     pass
 
+if sys.version_info >= (3,0):
+    def _reraise(cls, val, tb):
+        __tracebackhide__ = True
+        assert hasattr(val, '__traceback__')
+        raise val
+else:
+    exec ("""
+def _reraise(cls, val, tb):
+    __tracebackhide__ = True
+    raise cls, val, tb
+""")
+
+
+
+
 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'))
+            code = r.compiler(text)
         except (OverflowError, SyntaxError, ValueError):
             self.finish = 1
         else:
@@ -67,16 +90,14 @@
 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(os.path.expanduser("~/.pythoni.hist"), "w")
-        except IOError:
-            pass
-        else:
-            pickle.dump(reader.history, file)
-            file.close()
-    return saver
+def saver(reader=reader):
+    try:
+        with open(os.path.expanduser("~/.pythoni.hist"), "wb") as fp:
+            fp.write(b'\n'.join(item.encode('unicode_escape')
+                                for item in reader.history))
+    except IOError as e:
+        print(e)
+        pass
 
 class PythonicReader(CompletingReader, HistoricalReader):
     def collect_keymap(self):
@@ -97,17 +118,18 @@
         else:
             self.compiler = compiler
         try:
-            file = open(os.path.expanduser("~/.pythoni.hist"))
+            file = open(os.path.expanduser("~/.pythoni.hist"), 'rb')
         except IOError:
-            pass
+            self.history = []
         else:
             try:
-                self.history = pickle.load(file)
+                lines = file.readlines()
+                self.history = [ x.rstrip(b'\n').decode('unicode_escape') for 
x in lines]
             except:
                 self.history = []
             self.historyi = len(self.history)
             file.close()
-        atexit.register(mk_saver(self))
+        atexit.register(lambda: saver(self))
         for c in [maybe_accept]:
             self.commands[c.__name__] = c
             self.commands[c.__name__.replace('_', '-')] = c        
@@ -172,8 +194,7 @@
     def execute(self, text):
         try:
             # ooh, look at the hack:            
-            code = self.compile("# coding:utf8\n"+text.encode('utf-8'),
-                                '<input>', 'single')
+            code = self.compile(text, '<input>', 'single')
         except (OverflowError, SyntaxError, ValueError):
             self.showsyntaxerror("<input>")
         else:
@@ -192,7 +213,7 @@
                     finally:
                         warnings.showwarning = sv
                 except KeyboardInterrupt:
-                    print "KeyboardInterrupt"
+                    print("KeyboardInterrupt")
                 else:
                     if l:
                         self.execute(l)
@@ -217,7 +238,7 @@
             r = self.reader.handle1(block)
         except KeyboardInterrupt:
             self.restore()
-            print "KeyboardInterrupt"
+            print("KeyboardInterrupt")
             self.prepare()
         else:
             if self.reader.finished:
@@ -253,7 +274,7 @@
             if self.exc_info:
                 type, value, tb = self.exc_info
                 self.exc_info = None
-                raise type, value, tb
+                _reraise(type, value, tb)
         
     def tkinteract(self):
         """Run a Tk-aware Python interactive session.
@@ -370,13 +391,13 @@
                     encoding = None # so you get ASCII...
             con = UnixConsole(0, 1, None, encoding)
         if print_banner:
-            print "Python", sys.version, "on", sys.platform
-            print 'Type "help", "copyright", "credits" or "license" '\
-                  'for more information.'
+            print("Python", sys.version, "on", sys.platform)
+            print('Type "help", "copyright", "credits" or "license" '\
+                  'for more information.')
         sys.path.insert(0, os.getcwd())
 
         if clear_main and __name__ != '__main__':
-            mainmod = new.module('__main__')
+            mainmod = imp.new_module('__main__')
             sys.modules['__main__'] = mainmod
         else:
             mainmod = sys.modules['__main__']
diff --git a/pyrepl/reader.py b/pyrepl/reader.py
--- a/pyrepl/reader.py
+++ b/pyrepl/reader.py
@@ -19,25 +19,31 @@
 # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-import types
+from __future__ import unicode_literals
 from pyrepl import unicodedata_
 from pyrepl import commands
 from pyrepl import input
+try:
+    unicode
+except NameError:
+    unicode = str
+    unichr = chr
+    basestring = bytes, str
 
 def _make_unctrl_map():
     uc_map = {}
     for c in map(unichr, range(256)):
-        if unicodedata_.category(c)[0] <> 'C':
+        if unicodedata_.category(c)[0] != 'C':
             uc_map[c] = c
     for i in range(32):
         c = unichr(i)
-        uc_map[c] = u'^' + unichr(ord('A') + i - 1)
-    uc_map['\t'] = '    ' # display TABs as 4 characters
-    uc_map['\177'] = u'^?'
+        uc_map[c] = '^' + unichr(ord('A') + i - 1)
+    uc_map[b'\t'] = '    ' # display TABs as 4 characters
+    uc_map[b'\177'] = unicode('^?')
     for i in range(256):
         c = unichr(i)
-        if not uc_map.has_key(c):
-            uc_map[c] = u'\\%03o'%i
+        if c not in uc_map:
+            uc_map[c] = unicode('\\%03o')%i 
     return uc_map
 
 # disp_str proved to be a bottleneck for large inputs, so it's been
@@ -56,7 +62,7 @@
             return u[c]
         else:
             if unicodedata_.category(c).startswith('C'):
-                return '\u%04x'%(ord(c),)
+                return b'\u%04x'%(ord(c))
             else:
                 return c
 
@@ -72,9 +78,12 @@
 
         the list always contains 0s or 1s at present; it could conceivably
         go higher as and when unicode support happens."""
-        s = map(uc, buffer)
-        return (join(s),
-                map(ord, join(map(lambda x:'\001'+(len(x)-1)*'\000', s))))
+        s = [uc(x) for x in buffer]
+        b = [] #XXX: bytearray
+        for x in s:
+            b.append(1)
+            b.extend([0]*(len(x)-1))
+        return join(s), b
 
     del _my_unctrl
 
@@ -93,7 +102,7 @@
         st[c] = SYNTAX_SYMBOL
     for c in [a for a in map(unichr, range(256)) if a.isalpha()]:
         st[c] = SYNTAX_WORD
-    st[u'\n'] = st[u' '] = SYNTAX_WHITESPACE
+    st[unicode('\n')] = st[unicode(' ')] = SYNTAX_WHITESPACE
     return st
 
 default_keymap = tuple(
@@ -141,7 +150,7 @@
      #(r'\M-\n', 'insert-nl'),
      ('\\\\', 'self-insert')] + \
     [(c, 'self-insert')
-     for c in map(chr, range(32, 127)) if c <> '\\'] + \
+     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'),
@@ -160,7 +169,8 @@
                         # workaround
      ])
 
-del c # from the listcomps
+if 'c' in globals():  # only on python 2.x
+    del c # from the listcomps
 
 class Reader(object):
     """The Reader class implements the bare bones of a command reader,
@@ -281,7 +291,7 @@
             p -= ll + 1
             prompt, lp = self.process_prompt(prompt)
             l, l2 = disp_str(line)
-            wrapcount = (len(l) + lp) / w
+            wrapcount = (len(l) + lp) // w
             if wrapcount == 0:
                 screen.append(prompt + l)
                 screeninfo.append((lp, l2+[1]))
@@ -337,7 +347,7 @@
         st = self.syntax_table
         b = self.buffer
         p -= 1
-        while p >= 0 and st.get(b[p], SYNTAX_WORD) <> SYNTAX_WORD:
+        while p >= 0 and st.get(b[p], SYNTAX_WORD) != SYNTAX_WORD:
             p -= 1
         while p >= 0 and st.get(b[p], SYNTAX_WORD) == SYNTAX_WORD:
             p -= 1
@@ -353,7 +363,7 @@
             p = self.pos
         st = self.syntax_table
         b = self.buffer
-        while p < len(b) and st.get(b[p], SYNTAX_WORD) <> SYNTAX_WORD:
+        while p < len(b) and st.get(b[p], SYNTAX_WORD) != SYNTAX_WORD:
             p += 1
         while p < len(b) and st.get(b[p], SYNTAX_WORD) == SYNTAX_WORD:
             p += 1
@@ -369,7 +379,7 @@
             p = self.pos
         b = self.buffer
         p -= 1
-        while p >= 0 and b[p] <> '\n':
+        while p >= 0 and b[p] != '\n':
             p -= 1
         return p + 1
 
@@ -381,7 +391,7 @@
         if p is None:
             p = self.pos
         b = self.buffer
-        while p < len(b) and b[p] <> '\n':
+        while p < len(b) and b[p] != '\n':
             p += 1
         return p
 
@@ -515,11 +525,13 @@
 
     def do_cmd(self, cmd):
         #print cmd
-        if isinstance(cmd[0], str):
+        if isinstance(cmd[0], basestring): #XXX: unify to text
             cmd = self.commands.get(cmd[0],
-                                    commands.invalid_command)(self, cmd)
+                                    commands.invalid_command)(self, *cmd)
         elif isinstance(cmd[0], type):
             cmd = cmd[0](self, cmd)
+        else:
+            return # nothing to do
 
         cmd.do()
 
@@ -552,6 +564,8 @@
             if not event: # can only happen if we're not blocking
                 return None
 
+            translate = True
+
             if event.evt == 'key':
                 self.input_trans.push(event)
             elif event.evt == 'scroll':
@@ -559,9 +573,12 @@
             elif event.evt == 'resize':
                 self.refresh()
             else:
-                pass
+                translate = False
 
-            cmd = self.input_trans.get()
+            if translate:
+                cmd = self.input_trans.get()
+            else:
+                cmd = event.evt, event.data
 
             if cmd is None:
                 if block:
@@ -603,11 +620,11 @@
     def get_buffer(self, encoding=None):
         if encoding is None:
             encoding = self.console.encoding
-        return u''.join(self.buffer).encode(self.console.encoding)
+        return unicode('').join(self.buffer).encode(self.console.encoding)
 
     def get_unicode(self):
         """Return the current buffer as a unicode string."""
-        return u''.join(self.buffer)
+        return unicode('').join(self.buffer)
 
 def test():
     from pyrepl.unix_console import UnixConsole
diff --git a/pyrepl/tests/__init__.py b/pyrepl/tests/__init__.py
deleted file mode 100644
--- a/pyrepl/tests/__init__.py
+++ /dev/null
@@ -1,20 +0,0 @@
-#   Copyright 2000-2004 Michael Hudson-Doyle <[email protected]>
-#
-#                        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.
-
-# moo
diff --git a/pyrepl/trace.py b/pyrepl/trace.py
new file mode 100644
--- /dev/null
+++ b/pyrepl/trace.py
@@ -0,0 +1,17 @@
+import os
+
+trace_filename = os.environ.get("PYREPL_TRACE")
+
+if trace_filename is not None:
+    trace_file = open(trace_filename, 'a')
+else:
+    trace_file = None
+
+def trace(line, *k, **kw):
+    if trace_file is None:
+        return
+    if k or kw:
+        line = line.format(*k, **kw)
+    trace_file.write(line+'\n')
+    trace_file.flush()
+
diff --git a/pyrepl/unix_console.py b/pyrepl/unix_console.py
--- a/pyrepl/unix_console.py
+++ b/pyrepl/unix_console.py
@@ -22,14 +22,20 @@
 import termios, select, os, struct, errno
 import signal, re, time, sys
 from fcntl import ioctl
-from pyrepl import curses
-from pyrepl.fancy_termios import tcgetattr, tcsetattr
-from pyrepl.console import Console, Event
-from pyrepl import unix_eventqueue
+from . import curses
+from .fancy_termios import tcgetattr, tcsetattr
+from .console import Console, Event
+from .unix_eventqueue import EventQueue
+from .trace import trace
 
 class InvalidTerminal(RuntimeError):
     pass
 
+try:
+    unicode
+except NameError:
+    unicode = str
+
 _error = (termios.error, curses.error, InvalidTerminal)
 
 # there are arguments for changing this to "refresh"
@@ -41,8 +47,8 @@
 def _my_getstr(cap, optional=0):
     r = curses.tigetstr(cap)
     if not optional and r is None:
-        raise InvalidTerminal, \
-              "terminal doesn't have the required '%s' capability"%cap
+        raise InvalidTerminal(
+              "terminal doesn't have the required '%s' capability"%cap)
     return r
 
 # at this point, can we say: AAAAAAAAAAAAAAAAAAAAAARGH!
@@ -58,7 +64,7 @@
 
 del r, maybe_add_baudrate
 
-delayprog = re.compile("\\$<([0-9]+)((?:/|\\*){0,2})>")
+delayprog = re.compile(b"\\$<([0-9]+)((?:/|\\*){0,2})>")
 
 try:
     poll = select.poll
@@ -93,6 +99,7 @@
         else:
             self.output_fd = f_out.fileno()
         
+
         self.pollob = poll()
         self.pollob.register(self.input_fd, POLLIN)
         curses.setupterm(term, self.output_fd)
@@ -131,14 +138,14 @@
         elif self._cub1 and self._cuf1:
             self.__move_x = self.__move_x_cub1_cuf1
         else:
-            raise RuntimeError, "insufficient terminal (horizontal)"
+            raise RuntimeError("insufficient terminal (horizontal)")
 
         if self._cuu and self._cud:
             self.__move_y = self.__move_y_cuu_cud
         elif self._cuu1 and self._cud1:
             self.__move_y = self.__move_y_cuu1_cud1
         else:
-            raise RuntimeError, "insufficient terminal (vertical)"
+            raise RuntimeError("insufficient terminal (vertical)")
 
         if self._dch1:
             self.dch1 = self._dch1
@@ -156,16 +163,16 @@
 
         self.__move = self.__move_short
 
-        self.event_queue = unix_eventqueue.EventQueue(self.input_fd)
-        self.partial_char = ''
+        self.event_queue = EventQueue(self.input_fd)
+        self.partial_char = b''
         self.cursor_visible = 1
 
     def change_encoding(self, encoding):
         self.encoding = encoding
     
-    def refresh(self, screen, (cx, cy)):
+    def refresh(self, screen, c_xy):
         # this function is still too long (over 90 lines)
-
+        cx, cy = c_xy
         if not self.__gone_tall:
             while len(self.screen) < min(len(screen), self.height):
                 self.__hide_cursor()
@@ -303,6 +310,7 @@
         self.__buffer.append((text, 0))
 
     def __write_code(self, fmt, *args):
+
         self.__buffer.append((curses.tparm(fmt, *args), 1))
 
     def __maybe_write_code(self, fmt, *args):
@@ -403,10 +411,11 @@
         self.event_queue.insert(Event('resize', None))
 
     def push_char(self, char):
+        trace('push char {char!r}', char=char)
         self.partial_char += char
         try:
-            c = unicode(self.partial_char, self.encoding)
-        except UnicodeError, e:
+            c = self.partial_char.decode(self.encoding)
+        except UnicodeError as e:
             if len(e.args) > 4 and \
                    e.args[4] == 'unexpected end of data':
                 pass
@@ -418,7 +427,7 @@
                 self.partial_char = ''
                 sys.stderr.write('\n%s: %s\n' % (e.__class__.__name__, e))
         else:
-            self.partial_char = ''
+            self.partial_char = b''
             self.event_queue.push(c)
         
     def get_event(self, block=1):
@@ -426,7 +435,7 @@
             while 1: # All hail Unix!
                 try:
                     self.push_char(os.read(self.input_fd, 1))
-                except (IOError, OSError), err:
+                except (IOError, OSError) as err:
                     if err.errno == errno.EINTR:
                         if not self.event_queue.empty():
                             return self.event_queue.get()
diff --git a/pyrepl/unix_eventqueue.py b/pyrepl/unix_eventqueue.py
--- a/pyrepl/unix_eventqueue.py
+++ b/pyrepl/unix_eventqueue.py
@@ -26,6 +26,11 @@
 from pyrepl import curses
 from termios import tcgetattr, VERASE
 import os
+try:
+    unicode
+except NameError:
+    unicode = str
+
 
 _keynames = {
     "delete" : "kdch1",
@@ -54,7 +59,7 @@
             if keycode:
                 our_keycodes[keycode] = unicode(key)
         if os.isatty(fd):
-            our_keycodes[tcgetattr(fd)[6][VERASE]] = u'backspace'
+            our_keycodes[tcgetattr(fd)[6][VERASE]] = unicode('backspace')
         self.k = self.ck = keymap.compile_keymap(our_keycodes)
         self.events = []
         self.buf = []
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -39,7 +39,7 @@
     license = "MIT X11 style",
     description = "A library for building flexible command line interfaces",
     platforms = ["unix", "linux"],
-    packages = ["pyrepl", "pyrepl.tests"],
+    packages = ["pyrepl" ],
     #ext_modules = [Extension("_pyrepl_utils", ["pyrepl_utilsmodule.c"])],
     scripts = ["pythoni", "pythoni1"],
     long_description = long_desc,
diff --git a/testing/__init__.py b/testing/__init__.py
new file mode 100644
diff --git a/pyrepl/tests/infrastructure.py b/testing/infrastructure.py
rename from pyrepl/tests/infrastructure.py
rename to testing/infrastructure.py
--- a/pyrepl/tests/infrastructure.py
+++ b/testing/infrastructure.py
@@ -17,10 +17,9 @@
 # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
+from __future__ import print_function
 from pyrepl.reader import Reader
 from pyrepl.console import Console, Event
-import unittest
-import sys
 
 class EqualsAnything(object):
     def __eq__(self, other):
@@ -32,51 +31,38 @@
     width = 80
     encoding = 'utf-8'
 
-    def __init__(self, events, testcase, verbose=False):
+    def __init__(self, events, verbose=False):
         self.events = events
         self.next_screen = None
         self.verbose = verbose
-        self.testcase = testcase
 
     def refresh(self, screen, xy):
         if self.next_screen is not None:
-            self.testcase.assertEqual(
-                screen, self.next_screen,
-                "[ %s != %s after %r ]"%(screen, self.next_screen,
-                                         self.last_event_name))
+                assert screen == self.next_screen, "[ %s != %s after %r ]"%(
+                    screen, self.next_screen, self.last_event_name)
 
     def get_event(self, block=1):
         ev, sc = self.events.pop(0)
         self.next_screen = sc
         if not isinstance(ev, tuple):
-            ev = (ev,)
+            ev = (ev, None)
         self.last_event_name = ev[0]
         if self.verbose:
-            print "event", ev
+            print("event", ev)
         return Event(*ev)
 
 class TestReader(Reader):
+
     def get_prompt(self, lineno, cursor_on_line):
         return ''
+    
     def refresh(self):
         Reader.refresh(self)
         self.dirty = True
 
-class ReaderTestCase(unittest.TestCase):
-    def run_test(self, test_spec, reader_class=TestReader):
-        # remember to finish your test_spec with 'accept' or similar!
-        con = TestConsole(test_spec, self)
-        reader = reader_class(con)
-        reader.readline()
+def read_spec(test_spec, reader_class=TestReader):
+    # remember to finish your test_spec with 'accept' or similar!
+    con = TestConsole(test_spec, verbose=True)
+    reader = reader_class(con)
+    reader.readline()
 
-class BasicTestRunner:
-    def run(self, test):
-        result = unittest.TestResult()
-        test(result)
-        return result
-
-def run_testcase(testclass):
-    suite = unittest.makeSuite(testclass)
-    runner = unittest.TextTestRunner(sys.stdout, verbosity=1)
-    result = runner.run(suite)
-    
diff --git a/pyrepl/tests/basic.py b/testing/test_basic.py
rename from pyrepl/tests/basic.py
rename to testing/test_basic.py
--- a/pyrepl/tests/basic.py
+++ b/testing/test_basic.py
@@ -16,100 +16,89 @@
 # 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 pytest
+from .infrastructure import read_spec, EA
 
-from pyrepl.console import Event
-from pyrepl.tests.infrastructure import ReaderTestCase, EA, run_testcase
 
-class SimpleTestCase(ReaderTestCase):
+def test_basic():
+    read_spec([(('self-insert', 'a'), ['a']),
+               ( 'accept',            ['a'])])
 
-    def test_basic(self):
-        self.run_test([(('self-insert', 'a'), ['a']),
-                       ( 'accept',            ['a'])])
+def test_repeat():
+    read_spec([(('digit-arg', '3'),   ['']),
+               (('self-insert', 'a'), ['aaa']),
+               ( 'accept',            ['aaa'])])
 
-    def test_repeat(self):
-        self.run_test([(('digit-arg', '3'),   ['']),
-                       (('self-insert', 'a'), ['aaa']),
-                       ( 'accept',            ['aaa'])])
+def test_kill_line():
+    read_spec([(('self-insert', 'abc'), ['abc']),
+               ( 'left',                None),
+               ( 'kill-line',           ['ab']),
+               ( 'accept',              ['ab'])])
 
-    def test_kill_line(self):
-        self.run_test([(('self-insert', 'abc'), ['abc']),
-                       ( 'left',                None),
-                       ( 'kill-line',           ['ab']),
-                       ( 'accept',              ['ab'])])
+def test_unix_line_discard():
+    read_spec([(('self-insert', 'abc'), ['abc']),
+               ( 'left',                None),
+               ( 'unix-word-rubout',    ['c']),
+               ( 'accept',              ['c'])])
 
-    def test_unix_line_discard(self):
-        self.run_test([(('self-insert', 'abc'), ['abc']),
-                       ( 'left',                None),
-                       ( 'unix-word-rubout',    ['c']),
-                       ( 'accept',              ['c'])])
+def test_kill_word():
+    read_spec([(('self-insert', 'ab cd'), ['ab cd']),
+               ( 'beginning-of-line',     ['ab cd']),
+               ( 'kill-word',             [' cd']),
+               ( 'accept',                [' cd'])])
 
-    def test_kill_word(self):
-        self.run_test([(('self-insert', 'ab cd'), ['ab cd']),
-                       ( 'beginning-of-line',     ['ab cd']),
-                       ( 'kill-word',             [' cd']),
-                       ( 'accept',                [' cd'])])
+def test_backward_kill_word():
+    read_spec([(('self-insert', 'ab cd'), ['ab cd']),
+               ( 'backward-kill-word',    ['ab ']),
+               ( 'accept',                ['ab '])])
 
-    def test_backward_kill_word(self):
-        self.run_test([(('self-insert', 'ab cd'), ['ab cd']),
-                       ( 'backward-kill-word',    ['ab ']),
-                       ( 'accept',                ['ab '])])
+def test_yank():
+    read_spec([(('self-insert', 'ab cd'), ['ab cd']),
+               ( 'backward-kill-word',    ['ab ']),
+               ( 'beginning-of-line',     ['ab ']),
+               ( 'yank',                  ['cdab ']),
+               ( 'accept',                ['cdab '])])
+    
+def test_yank_pop():
+    read_spec([(('self-insert', 'ab cd'), ['ab cd']),
+               ( 'backward-kill-word',    ['ab ']),
+               ( 'left',                  ['ab ']),
+               ( 'backward-kill-word',    [' ']),
+               ( 'yank',                  ['ab ']),
+               ( 'yank-pop',              ['cd ']),
+               ( 'accept',                ['cd '])])
 
-    def test_yank(self):
-        self.run_test([(('self-insert', 'ab cd'), ['ab cd']),
-                       ( 'backward-kill-word',    ['ab ']),
-                       ( 'beginning-of-line',     ['ab ']),
-                       ( 'yank',                  ['cdab ']),
-                       ( 'accept',                ['cdab '])])
-        
-    def test_yank_pop(self):
-        self.run_test([(('self-insert', 'ab cd'), ['ab cd']),
-                       ( 'backward-kill-word',    ['ab ']),
-                       ( 'left',                  ['ab ']),
-                       ( 'backward-kill-word',    [' ']),
-                       ( 'yank',                  ['ab ']),
-                       ( 'yank-pop',              ['cd ']),
-                       ( 'accept',                ['cd '])])
+def test_interrupt():
+    with pytest.raises(KeyboardInterrupt):
+        read_spec([( 'interrupt',  [''])])
 
-    def test_interrupt(self):
-        try:
-            self.run_test([( 'interrupt',  [''])])
-        except KeyboardInterrupt:
-            pass
-        else:
-            self.fail('KeyboardInterrupt got lost')
+# test_suspend -- hah
 
-    # test_suspend -- hah
+def test_up():
+    read_spec([(('self-insert', 'ab\ncd'), ['ab', 'cd']),
+               ( 'up',                     ['ab', 'cd']),
+               (('self-insert', 'e'),      ['abe', 'cd']),
+               ( 'accept',                 ['abe', 'cd'])])
 
-    def test_up(self):
-        self.run_test([(('self-insert', 'ab\ncd'), ['ab', 'cd']),
-                       ( 'up',                     ['ab', 'cd']),
-                       (('self-insert', 'e'),      ['abe', 'cd']),
-                       ( 'accept',                 ['abe', 'cd'])])
+def test_down():
+    read_spec([(('self-insert', 'ab\ncd'), ['ab', 'cd']),
+               ( 'up',                     ['ab', 'cd']),
+               (('self-insert', 'e'),      ['abe', 'cd']),
+               ( 'down',                   ['abe', 'cd']),
+               (('self-insert', 'f'),      ['abe', 'cdf']),
+               ( 'accept',                 ['abe', 'cdf'])])
 
-    def test_down(self):
-        self.run_test([(('self-insert', 'ab\ncd'), ['ab', 'cd']),
-                       ( 'up',                     ['ab', 'cd']),
-                       (('self-insert', 'e'),      ['abe', 'cd']),
-                       ( 'down',                   ['abe', 'cd']),
-                       (('self-insert', 'f'),      ['abe', 'cdf']),
-                       ( 'accept',                 ['abe', 'cdf'])])
+def test_left():
+    read_spec([(('self-insert', 'ab'), ['ab']),
+               ( 'left',               ['ab']),
+               (('self-insert', 'c'),  ['acb']),
+               ( 'accept',             ['acb'])])
 
-    def test_left(self):
-        self.run_test([(('self-insert', 'ab'), ['ab']),
-                       ( 'left',               ['ab']),
-                       (('self-insert', 'c'),  ['acb']),
-                       ( 'accept',             ['acb'])])
+def test_right():
+    read_spec([(('self-insert', 'ab'), ['ab']),
+               ( 'left',               ['ab']),
+               (('self-insert', 'c'),  ['acb']),
+               ( 'right',              ['acb']),
+               (('self-insert', 'd'),  ['acbd']),
+               ( 'accept',             ['acbd'])])
 
-    def test_right(self):
-        self.run_test([(('self-insert', 'ab'), ['ab']),
-                       ( 'left',               ['ab']),
-                       (('self-insert', 'c'),  ['acb']),
-                       ( 'right',              ['acb']),
-                       (('self-insert', 'd'),  ['acbd']),
-                       ( 'accept',             ['acbd'])])
-        
-def test():
-    run_testcase(SimpleTestCase)
-
-if __name__ == '__main__':
-    test()
diff --git a/pyrepl/tests/bugs.py b/testing/test_bugs.py
rename from pyrepl/tests/bugs.py
rename to testing/test_bugs.py
--- a/pyrepl/tests/bugs.py
+++ b/testing/test_bugs.py
@@ -17,20 +17,15 @@
 # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-from pyrepl.console import Event
-from pyrepl.tests.infrastructure import ReaderTestCase, EA, run_testcase
+from .infrastructure import EA, read_spec
 
 # this test case should contain as-verbatim-as-possible versions of
 # (applicable) bug reports
 
-class BugsTestCase(ReaderTestCase):
+import pytest
 
-    def test_transpose_at_start(self):
-        self.run_test([( 'transpose', [EA, '']),
-                       ( 'accept',    [''])])
[email protected](reason='event missing', run=False)
+def test_transpose_at_start():
+    read_spec([( 'transpose', [EA, '']),
+               ( 'accept',    [''])])
 
-def test():
-    run_testcase(BugsTestCase)
-
-if __name__ == '__main__':
-    test()
diff --git a/pyrepl/test/test_functional.py b/testing/test_functional.py
rename from pyrepl/test/test_functional.py
rename to testing/test_functional.py
--- a/pyrepl/test/test_functional.py
+++ b/testing/test_functional.py
@@ -20,31 +20,22 @@
 
 # some functional tests, to see if this is really working
 
-import py
+import pytest
 import sys
 
-class TestTerminal(object):
-    def _spawn(self, *args, **kwds):
-        try:
-            import pexpect
-        except ImportError, e:
-            py.test.skip(str(e))
-        kwds.setdefault('timeout', 10)
-        child = pexpect.spawn(*args, **kwds)
-        child.logfile = sys.stdout
-        return child
+def pytest_funcarg__child(request):
+    try:
+        pexpect = pytest.importorskip('pexpect')
+    except SyntaxError:
+        pytest.skip('pexpect wont work on py3k')
+    child = pexpect.spawn(sys.executable, ['-S'], timeout=10)
+    child.logfile = sys.stdout
+    child.sendline('from pyrepl.python_reader import main')
+    child.sendline('main()')
+    return child
 
-    def spawn(self, argv=[]):
-        # avoid running start.py, cause it might contain
-        # things like readline or rlcompleter(2) included
-        child = self._spawn(sys.executable, ['-S'] + argv)
-        child.sendline('from pyrepl.python_reader import main')
-        child.sendline('main()')
-        return child
+def test_basic(child):
+    child.sendline('a = 3')
+    child.sendline('a')
+    child.expect('3')
 
-    def test_basic(self):
-        child = self.spawn()
-        child.sendline('a = 3')
-        child.sendline('a')
-        child.expect('3')
-        
diff --git a/testing/test_keymap.py b/testing/test_keymap.py
new file mode 100644
--- /dev/null
+++ b/testing/test_keymap.py
@@ -0,0 +1,12 @@
+import pytest
+from pyrepl.keymap import compile_keymap
+
+
[email protected]('completely wrong')
+def test_compile_keymap():
+    k = compile_keymap({
+        b'a': 'test',
+        b'bc': 'test2',
+    })
+
+    assert k == {b'a': 'test', b'b': { b'c': 'test2'}}
diff --git a/pyrepl/tests/wishes.py b/testing/test_wishes.py
rename from pyrepl/tests/wishes.py
rename to testing/test_wishes.py
--- a/pyrepl/tests/wishes.py
+++ b/testing/test_wishes.py
@@ -17,22 +17,15 @@
 # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-from pyrepl.console import Event
-from pyrepl.tests.infrastructure import ReaderTestCase, EA, run_testcase
+from .infrastructure import EA, read_spec
 
 # this test case should contain as-verbatim-as-possible versions of
 # (applicable) feature requests
 
-class WishesTestCase(ReaderTestCase):
 
-    def test_quoted_insert_repeat(self):
-        self.run_test([(('digit-arg', '3'),      ['']),
-                       ( 'quoted-insert',        ['']),
-                       (('self-insert', '\033'), ['^[^[^[']),
-                       ( 'accept',               None)])
+def test_quoted_insert_repeat():
+   read_spec([(('digit-arg', '3'),      ['']),
+              ( 'quoted-insert',        ['']),
+              (('self-insert', '\033'), ['^[^[^[']),
+              ( 'accept',               None)])
 
-def test():
-    run_testcase(WishesTestCase)
-
-if __name__ == '__main__':
-    test()
diff --git a/tox.ini b/tox.ini
--- a/tox.ini
+++ b/tox.ini
@@ -1,6 +1,9 @@
 [tox]
 envlist= py27, py32
 
+[pytest]
+codechecks = pep8 pyflakes
+
 [testenv]
 deps=
     pytest
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to