Author: Amaury Forgeot d'Arc <[email protected]>
Branch: py3k
Changeset: r58411:be161e2bc46a
Date: 2012-10-24 23:34 +0200
http://bitbucket.org/pypy/pypy/changeset/be161e2bc46a/
Log: Update pyrepl from upstream (py3k-readline branch)
diff --git a/lib_pypy/pyrepl/_minimal_curses.py
b/lib_pypy/pyrepl/_minimal_curses.py
new file mode 100644
--- /dev/null
+++ b/lib_pypy/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/lib_pypy/pyrepl/cmdrepl.py b/lib_pypy/pyrepl/cmdrepl.py
--- a/lib_pypy/pyrepl/cmdrepl.py
+++ b/lib_pypy/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/lib_pypy/pyrepl/commands.py b/lib_pypy/pyrepl/commands.py
--- a/lib_pypy/pyrepl/commands.py
+++ b/lib_pypy/pyrepl/commands.py
@@ -33,9 +33,12 @@
class Command(object):
finish = 0
kills_digit_arg = 1
- def __init__(self, reader, cmd):
+
+ def __init__(self, reader, event_name, event):
self.reader = reader
- self.event_name, self.event = cmd
+ self.event = event
+ self.event_name = event_name
+
def do(self):
pass
diff --git a/lib_pypy/pyrepl/completer.py b/lib_pypy/pyrepl/completer.py
--- a/lib_pypy/pyrepl/completer.py
+++ b/lib_pypy/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/lib_pypy/pyrepl/completing_reader.py
b/lib_pypy/pyrepl/completing_reader.py
--- a/lib_pypy/pyrepl/completing_reader.py
+++ b/lib_pypy/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/lib_pypy/pyrepl/console.py b/lib_pypy/pyrepl/console.py
--- a/lib_pypy/pyrepl/console.py
+++ b/lib_pypy/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/lib_pypy/pyrepl/copy_code.py b/lib_pypy/pyrepl/copy_code.py
--- a/lib_pypy/pyrepl/copy_code.py
+++ b/lib_pypy/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/lib_pypy/pyrepl/curses.py b/lib_pypy/pyrepl/curses.py
--- a/lib_pypy/pyrepl/curses.py
+++ b/lib_pypy/pyrepl/curses.py
@@ -19,21 +19,18 @@
# 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).
+
+import imp
+
try:
- import _curses
+ # Forces import of the builtin module. Seems necessary with PyPy.
+ _curses = imp.init_builtin('_minimal_curses')
+ if not _curses:
+ raise ImportError
+ setupterm = _curses.setupterm
+ tigetstr = _curses.tigetstr
+ tparm = _curses.tparm
+ error = _curses.error
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
+ raise
+ from ._minimal_curses import setupterm, tigetstr, tparm, error
diff --git a/lib_pypy/pyrepl/historical_reader.py
b/lib_pypy/pyrepl/historical_reader.py
--- a/lib_pypy/pyrepl/historical_reader.py
+++ b/lib_pypy/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/lib_pypy/pyrepl/input.py b/lib_pypy/pyrepl/input.py
--- a/lib_pypy/pyrepl/input.py
+++ b/lib_pypy/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
+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
@@ -58,8 +62,9 @@
if self.verbose:
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, end='')
@@ -74,7 +79,7 @@
if d is None:
if self.verbose:
print("invalid")
- if self.stack or len(key) > 1 or unicodedata_.category(key) ==
'C':
+ if self.stack or len(key) > 1 or unicodedata.category(key) ==
'C':
self.results.append(
(self.invalid_cls, self.stack + [key]))
else:
@@ -88,10 +93,12 @@
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/lib_pypy/pyrepl/keymap.py b/lib_pypy/pyrepl/keymap.py
--- a/lib_pypy/pyrepl/keymap.py
+++ b/lib_pypy/pyrepl/keymap.py
@@ -101,7 +101,7 @@
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":
@@ -137,7 +137,7 @@
if t == -1:
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(
@@ -170,13 +170,17 @@
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
+ if isinstance(key, bytes):
+ first = key[:1]
+ else:
+ first = key[0]
+ r.setdefault(first, {})[key[1:]] = value
for key, value in r.items():
if empty in value:
- if len(value) <> 1:
+ if len(value) != 1:
raise KeySpecError(
"key definitions for %s clash"%(value.values(),))
else:
diff --git a/lib_pypy/pyrepl/module_lister.py b/lib_pypy/pyrepl/module_lister.py
--- a/lib_pypy/pyrepl/module_lister.py
+++ b/lib_pypy/pyrepl/module_lister.py
@@ -45,13 +45,7 @@
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)
+ suffs.sort(reverse=True)
_packages[''] = list(sys.builtin_module_names)
for dir in sys.path:
if dir == '':
@@ -66,5 +60,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/lib_pypy/pyrepl/pygame_console.py
b/lib_pypy/pyrepl/pygame_console.py
--- a/lib_pypy/pyrepl/pygame_console.py
+++ b/lib_pypy/pyrepl/pygame_console.py
@@ -130,7 +130,7 @@
s.fill(c, [0, 600 - bmargin, 800, bmargin])
s.fill(c, [800 - rmargin, 0, lmargin, 600])
- def refresh(self, screen, cxy):
+ def refresh(self, screen, (cx, cy)):
self.screen = screen
self.pygame_screen.fill(colors.bg,
[0, tmargin + self.cur_top + self.scroll,
@@ -139,8 +139,8 @@
line_top = self.cur_top
width, height = self.fontsize
- self.cxy = cxy
- cp = self.char_pos(*cxy)
+ self.cxy = (cx, cy)
+ cp = self.char_pos(cx, cy)
if cp[1] < tmargin:
self.scroll = - (cy*self.fh + self.cur_top)
self.repaint()
@@ -148,7 +148,7 @@
self.scroll += (600 - bmargin) - (cp[1] + self.fh)
self.repaint()
if self.curs_vis:
- self.pygame_screen.blit(self.cursor, self.char_pos(*cxy))
+ self.pygame_screen.blit(self.cursor, self.char_pos(cx, cy))
for line in screen:
if 0 <= line_top + self.scroll <= (600 - bmargin - tmargin -
self.fh):
if line:
diff --git a/lib_pypy/pyrepl/python_reader.py b/lib_pypy/pyrepl/python_reader.py
--- a/lib_pypy/pyrepl/python_reader.py
+++ b/lib_pypy/pyrepl/python_reader.py
@@ -20,22 +20,23 @@
# 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 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
+ unicode
+except:
+ unicode = str
+
try:
- import imp
imp.find_module("twisted")
- from twisted.internet import reactor
- from twisted.internet.abstract import FileDescriptor
except ImportError:
default_interactmethod = "interact"
else:
@@ -47,13 +48,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 +83,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 +111,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,13 +187,13 @@
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:
self.runcode(code)
- sys.stdout.flush()
+ if sys.stdout and not sys.stdout.closed:
+ sys.stdout.flush()
def interact(self):
while 1:
@@ -192,7 +207,7 @@
finally:
warnings.showwarning = sv
except KeyboardInterrupt:
- print "KeyboardInterrupt"
+ print("KeyboardInterrupt")
else:
if l:
self.execute(l)
@@ -217,7 +232,7 @@
r = self.reader.handle1(block)
except KeyboardInterrupt:
self.restore()
- print "KeyboardInterrupt"
+ print("KeyboardInterrupt")
self.prepare()
else:
if self.reader.finished:
@@ -253,7 +268,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.
@@ -368,15 +383,15 @@
encoding = None
else:
encoding = None # so you get ASCII...
- con = UnixConsole(0, 1, None, encoding)
+ con = UnixConsole(os.dup(0), os.dup(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/lib_pypy/pyrepl/reader.py b/lib_pypy/pyrepl/reader.py
--- a/lib_pypy/pyrepl/reader.py
+++ b/lib_pypy/pyrepl/reader.py
@@ -19,64 +19,65 @@
# CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-import types
-from pyrepl import unicodedata_
+from __future__ import unicode_literals
+import unicodedata
from pyrepl import commands
from pyrepl import input
+try:
+ unicode
+except NameError:
+ unicode = str
+ unichr = chr
+
def _make_unctrl_map():
uc_map = {}
- for c in map(chr, range(256)):
- if unicodedata_.category(c)[0] <> 'C':
- uc_map[c] = c
+ for i in range(256):
+ c = unichr(i)
+ if unicodedata.category(c)[0] != 'C':
+ uc_map[i] = c
for i in range(32):
- c = chr(i)
- uc_map[c] = u'^' + chr(ord('A') + i - 1)
- uc_map['\t'] = ' ' # display TABs as 4 characters
- uc_map['\177'] = u'^?'
+ uc_map[i] = '^' + unichr(ord('A') + i - 1)
+ uc_map[ord(b'\t')] = ' ' # display TABs as 4 characters
+ uc_map[ord(b'\177')] = unicode('^?')
for i in range(256):
- c = chr(i)
- if c not in uc_map:
- uc_map[c] = u'\\%03o'%i
+ if i not in uc_map:
+ uc_map[i] = unicode('\\%03o') % i
return uc_map
-# disp_str proved to be a bottleneck for large inputs, so it's been
-# rewritten in C; it's not required though.
-try:
- raise ImportError # currently it's borked by the unicode support
- from _pyrepl_utils import disp_str, init_unctrl_map
+def _my_unctrl(c, u=_make_unctrl_map()):
+ if c in u:
+ return u[c]
+ else:
+ if unicodedata.category(c).startswith('C'):
+ return br'\u%04x' % ord(c)
+ else:
+ return c
- init_unctrl_map(_make_unctrl_map())
- del init_unctrl_map
-except ImportError:
- def _my_unctrl(c, u=_make_unctrl_map()):
- if c in u:
- return u[c]
- else:
- if unicodedata_.category(c).startswith('C'):
- return '\\u%04x'%(ord(c),)
- else:
- return c
+def disp_str(buffer, join=''.join, uc=_my_unctrl):
+ """ disp_str(buffer:string) -> (string, [int])
- def disp_str(buffer, join=''.join, uc=_my_unctrl):
- """ disp_str(buffer:string) -> (string, [int])
+ Return the string that should be the printed represenation of
+ |buffer| and a list detailing where the characters of |buffer|
+ get used up. E.g.:
- Return the string that should be the printed represenation of
- |buffer| and a list detailing where the characters of |buffer|
- get used up. E.g.:
+ >>> disp_str(chr(3))
+ ('^C', [1, 0])
- >>> disp_str(chr(3))
- ('^C', [1, 0])
+ the list always contains 0s or 1s at present; it could conceivably
+ go higher as and when unicode support happens."""
+ # disp_str proved to be a bottleneck for large inputs,
+ # so it needs to be rewritten in C; it's not required though.
+ 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
- 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))))
-
- del _my_unctrl
+del _my_unctrl
del _make_unctrl_map
@@ -86,6 +87,7 @@
SYNTAX_WORD,
SYNTAX_SYMBOL] = range(3)
+
def make_default_syntax_table():
# XXX perhaps should use some unicodedata here?
st = {}
@@ -93,7 +95,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 +143,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'),
@@ -155,12 +157,14 @@
(r'\<end>', 'end-of-line'), # was 'end'
(r'\<home>', 'beginning-of-line'), # was '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
+ (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
])
-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,
@@ -238,9 +242,9 @@
self.commands = {}
self.msg = ''
for v in vars(commands).values():
- if ( isinstance(v, type)
- and issubclass(v, commands.Command)
- and v.__name__[0].islower() ):
+ if (isinstance(v, type)
+ and issubclass(v, commands.Command)
+ and v.__name__[0].islower()):
self.commands[v.__name__] = v
self.commands[v.__name__.replace('_', '-')] = v
self.syntax_table = make_default_syntax_table()
@@ -281,18 +285,18 @@
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]))
+ screeninfo.append((lp, l2 + [1]))
else:
- screen.append(prompt + l[:w-lp] + "\\")
- screeninfo.append((lp, l2[:w-lp]))
- for i in range(-lp + w, -lp + wrapcount*w, w):
- screen.append(l[i:i+w] + "\\")
+ screen.append(prompt + l[:w - lp] + "\\")
+ screeninfo.append((lp, l2[:w - lp]))
+ for i in range(-lp + w, -lp + wrapcount * w, w):
+ screen.append(l[i:i + w] + "\\")
screeninfo.append((0, l2[i:i + w]))
- screen.append(l[wrapcount*w - lp:])
- screeninfo.append((0, l2[wrapcount*w - lp:]+[1]))
+ screen.append(l[wrapcount * w - lp:])
+ screeninfo.append((0, l2[wrapcount * w - lp:] + [1]))
self.screeninfo = screeninfo
self.cxy = self.pos2xy(self.pos)
if self.msg and self.msg_at_bottom:
@@ -320,9 +324,9 @@
if e == -1:
break
# Found start and end brackets, subtract from string length
- l = l - (e-s+1)
- out_prompt += prompt[pos:s] + prompt[s+1:e]
- pos = e+1
+ l = l - (e - s + 1)
+ out_prompt += prompt[pos:s] + prompt[s + 1:e]
+ pos = e + 1
out_prompt += prompt[pos:]
return out_prompt, l
@@ -337,7 +341,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 +357,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 +373,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 +385,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
@@ -398,7 +402,7 @@
"""Return what should be in the left-hand margin for line
`lineno'."""
if self.arg is not None and cursor_on_line:
- return "(arg: %s) "%self.arg
+ return "(arg: %s) " % self.arg
if "\n" in self.buffer:
if lineno == 0:
res = self.ps2
@@ -511,15 +515,17 @@
# this call sets up self.cxy, so call it first.
screen = self.calc_screen()
self.console.refresh(screen, self.cxy)
- self.dirty = 0 # forgot this for a while (blush)
+ self.dirty = 0 # forgot this for a while (blush)
def do_cmd(self, cmd):
#print cmd
if isinstance(cmd[0], str):
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)
+ cmd = cmd[0](self, *cmd)
+ else:
+ return # nothing to do
cmd.do()
@@ -549,9 +555,11 @@
while 1:
event = self.console.get_event(block)
- if not event: # can only happen if we're not blocking
+ 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 +567,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 +614,12 @@
def get_buffer(self, encoding=None):
if encoding is None:
encoding = self.console.encoding
- return u''.join(self.buffer).encode(self.console.encoding)
+ return self.get_unicode().encode(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
@@ -619,5 +631,5 @@
while reader.readline():
pass
-if __name__=='__main__':
+if __name__ == '__main__':
test()
diff --git a/lib_pypy/pyrepl/readline.py b/lib_pypy/pyrepl/readline.py
--- a/lib_pypy/pyrepl/readline.py
+++ b/lib_pypy/pyrepl/readline.py
@@ -32,6 +32,10 @@
from pyrepl.completing_reader import CompletingReader
from pyrepl.unix_console import UnixConsole, _error
+try:
+ unicode
+except NameError:
+ unicode = str
ENCODING = sys.getfilesystemencoding() or 'latin1' # XXX review
@@ -174,13 +178,15 @@
# ____________________________________________________________
class _ReadlineWrapper(object):
- f_in = 0
- f_out = 1
reader = None
saved_history_length = -1
startup_hook = None
config = ReadlineConfig()
+ def __init__(self):
+ self.f_in = os.dup(0)
+ self.f_ut = os.dup(1)
+
def get_reader(self):
if self.reader is None:
console = UnixConsole(self.f_in, self.f_out, encoding=ENCODING)
@@ -194,7 +200,7 @@
except _error:
return _old_raw_input(prompt)
reader.ps1 = prompt
- return reader.readline(startup_hook=self.startup_hook)
+ return reader.readline(reader, startup_hook=self.startup_hook)
def multiline_input(self, more_lines, ps1, ps2, returns_unicode=False):
"""Read an input on possibly multiple lines, asking for more
@@ -224,12 +230,14 @@
self.config.completer_delims = dict.fromkeys(string)
def get_completer_delims(self):
- chars = self.config.completer_delims.keys()
+ chars = list(self.config.completer_delims.keys())
chars.sort()
return ''.join(chars)
def _histline(self, line):
line = line.rstrip('\n')
+ if isinstance(line, unicode):
+ return line # on py3k
try:
return unicode(line, ENCODING)
except UnicodeDecodeError: # bah, silently fall back...
@@ -269,7 +277,9 @@
history = self.get_reader().get_trimmed_history(maxlength)
f = open(os.path.expanduser(filename), 'w')
for entry in history:
- if isinstance(entry, unicode):
+ # if we are on py3k, we don't need to encode strings before
+ # writing it to a file
+ if isinstance(entry, unicode) and sys.version_info < (3,):
try:
entry = entry.encode(ENCODING)
except UnicodeEncodeError: # bah, silently fall back...
@@ -417,9 +427,14 @@
else:
# this is not really what readline.c does. Better than nothing I guess
- import __builtin__
- _old_raw_input = __builtin__.raw_input
- __builtin__.raw_input = _wrapper.raw_input
+ if sys.version_info < (3,):
+ import __builtin__
+ _old_raw_input = __builtin__.raw_input
+ __builtin__.raw_input = _wrapper.raw_input
+ else:
+ import builtins
+ _old_raw_input = builtins.input
+ builtins.input = _wrapper.raw_input
_old_raw_input = None
_setup()
diff --git a/lib_pypy/pyrepl/simple_interact.py
b/lib_pypy/pyrepl/simple_interact.py
--- a/lib_pypy/pyrepl/simple_interact.py
+++ b/lib_pypy/pyrepl/simple_interact.py
@@ -33,15 +33,19 @@
return False
return True
+
def run_multiline_interactive_console(mainmodule=None):
import code
- if mainmodule is None:
- import __main__ as mainmodule
+ import __main__
+ mainmodule = mainmodule or __main__
console = code.InteractiveConsole(mainmodule.__dict__)
def more_lines(unicodetext):
# ooh, look at the hack:
- src = "#coding:utf-8\n"+unicodetext.encode('utf-8')
+ if sys.version_info < (3,):
+ src = "#coding:utf-8\n"+unicodetext.encode('utf-8')
+ else:
+ src = unicodetext
try:
code = console.compile(src, '<input>', 'single')
except (OverflowError, SyntaxError, ValueError):
diff --git a/lib_pypy/pyrepl/trace.py b/lib_pypy/pyrepl/trace.py
new file mode 100644
--- /dev/null
+++ b/lib_pypy/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/lib_pypy/pyrepl/unix_console.py b/lib_pypy/pyrepl/unix_console.py
--- a/lib_pypy/pyrepl/unix_console.py
+++ b/lib_pypy/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"
@@ -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)
@@ -156,16 +163,15 @@
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.encoding)
self.cursor_visible = 1
def change_encoding(self, encoding):
self.encoding = encoding
- def refresh(self, screen, cxy):
+ 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()
@@ -185,20 +191,9 @@
old_offset = offset = self.__offset
height = self.height
- if 0:
- global counter
- try:
- counter
- except NameError:
- counter = 0
- self.__write_code(curses.tigetstr("setaf"), counter)
- counter += 1
- if counter > 8:
- counter = 0
# we make sure the cursor is on the screen, and that we're
# using all of the screen if we can
- cx, cy = cxy
if cy < offset:
offset = cy
elif cy >= offset + height:
@@ -304,6 +299,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):
@@ -404,23 +400,8 @@
self.event_queue.insert(Event('resize', None))
def push_char(self, char):
- self.partial_char += char
- try:
- c = unicode(self.partial_char, self.encoding)
- except UnicodeError as e:
- if len(e.args) > 4 and \
- e.args[4] == 'unexpected end of data':
- pass
- else:
- # was: "raise". But it crashes pyrepl, and by extension the
- # pypy currently running, in which we are e.g. in the middle
- # of some debugging session. Argh. Instead just print an
- # error message to stderr and continue running, for now.
- self.partial_char = ''
- sys.stderr.write('\n%s: %s\n' % (e.__class__.__name__, e))
- else:
- self.partial_char = ''
- self.event_queue.push(c)
+ trace('push char {char!r}', char=char)
+ self.event_queue.push(char)
def get_event(self, block=1):
while self.event_queue.empty():
@@ -478,7 +459,7 @@
return int(os.environ["LINES"]), int(os.environ["COLUMNS"])
except KeyError:
height, width = struct.unpack(
- "hhhh", ioctl(self.input_fd, TIOCGWINSZ, "\000"*8))[0:2]
+ "hhhh", ioctl(self.input_fd, TIOCGWINSZ, b"\000"*8))[0:2]
if not height: return 25, 80
return height, width
else:
@@ -540,7 +521,7 @@
if FIONREAD:
def getpending(self):
- e = Event('key', '', '')
+ e = Event('key', '', b'')
while not self.event_queue.empty():
e2 = self.event_queue.get()
@@ -548,14 +529,15 @@
e.raw += e.raw
amount = struct.unpack(
- "i", ioctl(self.input_fd, FIONREAD, "\0\0\0\0"))[0]
- raw = unicode(os.read(self.input_fd, amount), self.encoding,
'replace')
- e.data += raw
+ "i", ioctl(self.input_fd, FIONREAD, b"\0\0\0\0"))[0]
+ raw = os.read(self.input_fd, amount)
+ data = unicode(raw, self.encoding, 'replace')
+ e.data += data
e.raw += raw
return e
else:
def getpending(self):
- e = Event('key', '', '')
+ e = Event('key', '', b'')
while not self.event_queue.empty():
e2 = self.event_queue.get()
@@ -563,8 +545,9 @@
e.raw += e.raw
amount = 10000
- raw = unicode(os.read(self.input_fd, amount), self.encoding,
'replace')
- e.data += raw
+ raw = os.read(self.input_fd, amount)
+ data = unicode(raw, self.encoding, 'replace')
+ e.data += data
e.raw += raw
return e
diff --git a/lib_pypy/pyrepl/unix_eventqueue.py
b/lib_pypy/pyrepl/unix_eventqueue.py
--- a/lib_pypy/pyrepl/unix_eventqueue.py
+++ b/lib_pypy/pyrepl/unix_eventqueue.py
@@ -24,8 +24,14 @@
from pyrepl import keymap
from pyrepl.console import Event
from pyrepl import curses
+from .trace import trace
from termios import tcgetattr, VERASE
import os
+try:
+ unicode
+except NameError:
+ unicode = str
+
_keynames = {
"delete" : "kdch1",
@@ -46,41 +52,70 @@
"up" : "kcuu1",
}
-class EventQueue(object):
- def __init__(self, fd):
- our_keycodes = {}
- for key, tiname in _keynames.items():
- keycode = curses.tigetstr(tiname)
- if keycode:
- our_keycodes[keycode] = str(key)
- if os.isatty(fd):
- our_keycodes[tcgetattr(fd)[6][VERASE]] = u'backspace'
- self.k = self.ck = keymap.compile_keymap(our_keycodes)
+def general_keycodes():
+ keycodes = {}
+ for key, tiname in _keynames.items():
+ keycode = curses.tigetstr(tiname)
+ trace('key {key} tiname {tiname} keycode {keycode!r}', **locals())
+ if keycode:
+ keycodes[keycode] = key
+ return keycodes
+
+
+
+def EventQueue(fd, encoding):
+ keycodes = general_keycodes()
+ if os.isatty(fd):
+ backspace = tcgetattr(fd)[6][VERASE]
+ keycodes[backspace] = unicode('backspace')
+ k = keymap.compile_keymap(keycodes)
+ trace('keymap {k!r}', k=k)
+ return EncodedQueue(k, encoding)
+
+class EncodedQueue(object):
+ def __init__(self, keymap, encoding):
+ self.k = self.ck = keymap
self.events = []
- self.buf = []
+ self.buf = bytearray()
+ self.encoding=encoding
+
def get(self):
if self.events:
return self.events.pop(0)
else:
return None
+
def empty(self):
return not self.events
+
+ def flush_buf(self):
+ old = self.buf
+ self.buf = bytearray()
+ return bytes(old)
+
def insert(self, event):
+ trace('added event {event}', event=event)
self.events.append(event)
+
def push(self, char):
+ self.buf.append(ord(char))
if char in self.k:
+ if self.k is self.ck:
+ #sanity check, buffer is empty when a special key comes
+ assert len(self.buf) == 1
k = self.k[char]
+ trace('found map {k!r}', k=k)
if isinstance(k, dict):
- self.buf.append(char)
self.k = k
else:
- self.events.append(Event('key', k, ''.join(self.buf) + char))
- self.buf = []
+ self.insert(Event('key', k, self.flush_buf()))
self.k = self.ck
- elif self.buf:
- self.events.extend([Event('key', c, c) for c in self.buf])
- self.buf = []
+
+ else:
+ try:
+ decoded = bytes(self.buf).decode(self.encoding)
+ except:
+ return
+
+ self.insert(Event('key', decoded, self.flush_buf()))
self.k = self.ck
- self.push(char)
- else:
- self.events.append(Event('key', char, char))
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit