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