Author: Ronny Pfannschmidt <[email protected]> Branch: commands-as-functions Changeset: r232:f60cd4b09eb6 Date: 2013-02-24 23:14 +0100 http://bitbucket.org/pypy/pyrepl/changeset/f60cd4b09eb6/
Log: merge from default diff --git a/MANIFEST.in b/MANIFEST.in --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,1 +1,3 @@ include TODO CREDITS CHANGES pythoni encopyright.py LICENSE +include MANIFEST.in +recursive-include testing *.py diff --git a/pyrepl/copy_code.py b/pyrepl/copy_code.py deleted file mode 100644 --- a/pyrepl/copy_code.py +++ /dev/null @@ -1,73 +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. - -from types import CodeType - -def copy_code_with_changes(codeobject, - argcount=None, - nlocals=None, - stacksize=None, - flags=None, - code=None, - consts=None, - names=None, - varnames=None, - filename=None, - name=None, - firstlineno=None, - lnotab=None): - if argcount is None: argcount = codeobject.co_argcount - if nlocals is None: nlocals = codeobject.co_nlocals - if stacksize is None: stacksize = codeobject.co_stacksize - if flags is None: flags = codeobject.co_flags - if code is None: code = codeobject.co_code - if consts is None: consts = codeobject.co_consts - if names is None: names = codeobject.co_names - if varnames is None: varnames = codeobject.co_varnames - if filename is None: filename = codeobject.co_filename - if name is None: name = codeobject.co_name - if firstlineno is None: firstlineno = codeobject.co_firstlineno - if lnotab is None: lnotab = codeobject.co_lnotab - return CodeType(argcount, - nlocals, - stacksize, - flags, - code, - consts, - names, - varnames, - filename, - name, - firstlineno, - lnotab) - -code_attrs=['argcount', - 'nlocals', - 'stacksize', - 'flags', - 'code', - 'consts', - 'names', - 'varnames', - 'filename', - 'name', - 'firstlineno', - 'lnotab'] - - diff --git a/pyrepl/reader.py b/pyrepl/reader.py --- a/pyrepl/reader.py +++ b/pyrepl/reader.py @@ -143,11 +143,11 @@ (r'\M-8', 'digit-arg'), (r'\M-9', 'digit-arg'), #(r'\M-\n', 'insert-nl'), - ('\\\\', 'self-insert')] + \ + ('\\\\', '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()] + \ + for c in map(chr, range(128, 256)) if c.isalpha()] + [(r'\<up>', 'up'), (r'\<down>', 'down'), (r'\<left>', 'left'), @@ -244,9 +244,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() diff --git a/pyrepl/readline.py b/pyrepl/readline.py --- a/pyrepl/readline.py +++ b/pyrepl/readline.py @@ -26,7 +26,8 @@ extensions for multiline input. """ -import sys, os +import sys +import os from pyrepl import commands from pyrepl.historical_reader import HistoricalReader from pyrepl.completing_reader import CompletingReader @@ -35,41 +36,43 @@ ENCODING = sys.getfilesystemencoding() or 'latin1' # XXX review -__all__ = ['add_history', - 'clear_history', - 'get_begidx', - 'get_completer', - 'get_completer_delims', - 'get_current_history_length', - 'get_endidx', - 'get_history_item', - 'get_history_length', - 'get_line_buffer', - 'insert_text', - 'parse_and_bind', - 'read_history_file', - 'read_init_file', - 'redisplay', - 'remove_history_item', - 'replace_history_item', - 'set_completer', - 'set_completer_delims', - 'set_history_length', - 'set_pre_input_hook', - 'set_startup_hook', - 'write_history_file', - # ---- multiline extensions ---- - 'multiline_input', - ] +__all__ = [ + 'add_history', + 'clear_history', + 'get_begidx', + 'get_completer', + 'get_completer_delims', + 'get_current_history_length', + 'get_endidx', + 'get_history_item', + 'get_history_length', + 'get_line_buffer', + 'insert_text', + 'parse_and_bind', + 'read_history_file', + 'read_init_file', + 'redisplay', + 'remove_history_item', + 'replace_history_item', + 'set_completer', + 'set_completer_delims', + 'set_history_length', + 'set_pre_input_hook', + 'set_startup_hook', + 'write_history_file', + # ---- multiline extensions ---- + 'multiline_input', +] # ____________________________________________________________ + class ReadlineConfig(object): readline_completer = None completer_delims = dict.fromkeys(' \t\n`~!@#$%^&*()-=+[{]}\\|;:\'",<>/?') + class ReadlineAlikeReader(HistoricalReader, CompletingReader): - assume_immutable_completions = False use_brackets = False sort_in_column = True @@ -156,10 +159,11 @@ if self.pos > len(self.buffer): self.pos = len(self.buffer) + class maybe_accept(commands.Command): def do(self): r = self.reader - r.dirty = 1 # this is needed to hide the completion menu, if visible + r.dirty = 1 # this is needed to hide the completion menu, if visible # # if there are already several lines and the cursor # is not on the last one, always insert a new \n. @@ -171,7 +175,6 @@ else: self.finish = 1 -# ____________________________________________________________ class _ReadlineWrapper(object): reader = None @@ -179,9 +182,9 @@ startup_hook = None config = ReadlineConfig() - def __init__(self): - self.f_in = os.dup(0) - self.f_out = os.dup(1) + def __init__(self, f_in=None, f_out=None): + self.f_in = f_in if f_in is not None else os.dup(0) + self.f_out = f_out if f_out is not None else os.dup(1) def get_reader(self): if self.reader is None: @@ -369,6 +372,7 @@ # ____________________________________________________________ # Stubs + def _make_stub(_name, _ret): def stub(*args, **kwds): import warnings @@ -380,16 +384,16 @@ ('read_init_file', None), ('redisplay', None), ('set_pre_input_hook', None), - ]: +]: assert _name not in globals(), _name _make_stub(_name, _ret) -# ____________________________________________________________ def _setup(): global _old_raw_input if _old_raw_input is not None: - return # don't run _setup twice + return + # don't run _setup twice try: f_in = sys.stdin.fileno() diff --git a/pyrepl/simple_interact.py b/pyrepl/simple_interact.py --- a/pyrepl/simple_interact.py +++ b/pyrepl/simple_interact.py @@ -26,6 +26,7 @@ import sys from pyrepl.readline import multiline_input, _error, _get_reader + def check(): # returns False if there is a problem initializing the state try: _get_reader() @@ -33,6 +34,7 @@ return False return True + def run_multiline_interactive_console(mainmodule=None): import code import __main__ diff --git a/pyrepl/unix_console.py b/pyrepl/unix_console.py --- a/pyrepl/unix_console.py +++ b/pyrepl/unix_console.py @@ -19,8 +19,15 @@ # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -import termios, select, os, struct, errno -import signal, re, time, sys +import termios +import select +import os +import struct +import errno +import signal +import re +import time +import sys from fcntl import ioctl from . import curses from .fancy_termios import tcgetattr, tcsetattr @@ -28,6 +35,7 @@ from .unix_eventqueue import EventQueue from .trace import trace + class InvalidTerminal(RuntimeError): pass @@ -44,16 +52,18 @@ FIONREAD = getattr(termios, "FIONREAD", None) TIOCGWINSZ = getattr(termios, "TIOCGWINSZ", None) + 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) + "terminal doesn't have the required '%s' capability" % cap) return r + # at this point, can we say: AAAAAAAAAAAAAAAAAAAAAARGH! def maybe_add_baudrate(dict, rate): - name = 'B%d'%rate + name = 'B%d' % rate if hasattr(termios, name): dict[getattr(termios, name)] = rate @@ -74,19 +84,28 @@ class poll: def __init__(self): pass + def register(self, fd, flag): self.fd = fd + def poll(self, timeout=None): - r,w,e = select.select([self.fd],[],[],timeout) + r, w, e = select.select([self.fd], [], [], timeout) return r POLLIN = getattr(select, "POLLIN", None) + +required_curses_tistrings = 'bel clear cup el' +optional_curses_tistrings = ( + 'civis cnorm cub cub1 cud cud1 cud cud1 cuf ' + 'cuf1 cuu cuu1 dch dch1 hpa ich ich1 ind pad ri rmkx smkx') + + class UnixConsole(Console): def __init__(self, f_in=0, f_out=1, term=None, encoding=None): if encoding is None: encoding = sys.getdefaultencoding() - + self.encoding = encoding if isinstance(f_in, int): @@ -98,40 +117,21 @@ self.output_fd = f_out else: self.output_fd = f_out.fileno() - self.pollob = poll() self.pollob.register(self.input_fd, POLLIN) curses.setupterm(term, self.output_fd) self.term = term - - self._bel = _my_getstr("bel") - self._civis = _my_getstr("civis", optional=1) - self._clear = _my_getstr("clear") - self._cnorm = _my_getstr("cnorm", optional=1) - self._cub = _my_getstr("cub", optional=1) - self._cub1 = _my_getstr("cub1", 1) - self._cud = _my_getstr("cud", 1) - self._cud1 = _my_getstr("cud1", 1) - self._cuf = _my_getstr("cuf", 1) - self._cuf1 = _my_getstr("cuf1", 1) - self._cup = _my_getstr("cup") - self._cuu = _my_getstr("cuu", 1) - self._cuu1 = _my_getstr("cuu1", 1) - self._dch1 = _my_getstr("dch1", 1) - self._dch = _my_getstr("dch", 1) - self._el = _my_getstr("el") - self._hpa = _my_getstr("hpa", 1) - self._ich = _my_getstr("ich", 1) - self._ich1 = _my_getstr("ich1", 1) - self._ind = _my_getstr("ind", 1) - self._pad = _my_getstr("pad", 1) - self._ri = _my_getstr("ri", 1) - self._rmkx = _my_getstr("rmkx", 1) - self._smkx = _my_getstr("smkx", 1) - + + for name in required_curses_tistrings.split(): + setattr(self, '_' + name, _my_getstr(name)) + + for name in optional_curses_tistrings.split(): + setattr(self, '_' + name, _my_getstr(name, optional=1)) + ## work out how we're going to sling the cursor around - if 0 and self._hpa: # hpa don't work in windows telnet :-( + # hpa don't work in windows telnet :-( + if 0 and self._hpa: self.__move_x = self.__move_x_hpa elif self._cub and self._cuf: self.__move_x = self.__move_x_cub_cuf @@ -166,9 +166,6 @@ 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, c_xy): # this function is still too long (over 90 lines) cx, cy = c_xy @@ -181,7 +178,7 @@ self.screen.append("") else: while len(self.screen) < len(screen): - self.screen.append("") + self.screen.append("") if len(screen) > self.height: self.__gone_tall = 1 @@ -191,7 +188,6 @@ old_offset = offset = self.__offset height = self.height - # we make sure the cursor is on the screen, and that we're # using all of the screen if we can if cy < offset: @@ -230,7 +226,7 @@ newscr): if oldline != newline: self.__write_changed_line(y, oldline, newline, px) - + y = len(newscr) while y < len(oldscr): self.__hide_cursor() @@ -240,7 +236,7 @@ y += 1 self.__show_cursor() - + self.screen = screen self.move_cursor(cx, cy) self.flushoutput() @@ -256,11 +252,12 @@ # reuse the oldline as much as possible, but stop as soon as we # encounter an ESCAPE, because it might be the start of an escape # sequene + #XXX unicode check! while x < minlen and oldline[x] == newline[x] and newline[x] != '\x1b': x += 1 if oldline[x:] == newline[x+1:] and self.ich1: - if ( y == self.__posxy[1] and x > self.__posxy[0] - and oldline[px:x] == newline[px+1:x+1] ): + if (y == self.__posxy[1] and x > self.__posxy[0] and + oldline[px:x] == newline[px+1:x+1]): x = px self.__move(x, y) self.__write_code(self.ich1) @@ -288,7 +285,8 @@ self.__write_code(self._el) self.__write(newline[x:]) self.__posxy = len(newline), y - + + #XXX: check for unicode mess if '\x1b' in newline: # ANSI escape characters are present, so we can't assume # anything about the position of the cursor. Moving the cursor @@ -359,13 +357,13 @@ self.__svtermstate = tcgetattr(self.input_fd) raw = self.__svtermstate.copy() raw.iflag |= termios.ICRNL - raw.iflag &=~ (termios.BRKINT | termios.INPCK | + raw.iflag &= ~(termios.BRKINT | termios.INPCK | termios.ISTRIP | termios.IXON) - raw.oflag &=~ (termios.OPOST) - raw.cflag &=~ (termios.CSIZE|termios.PARENB) - raw.cflag |= (termios.CS8) - raw.lflag &=~ (termios.ICANON|termios.ECHO| - termios.IEXTEN|(termios.ISIG*1)) + raw.oflag &= ~termios.OPOST + raw.cflag &= ~(termios.CSIZE | termios.PARENB) + raw.cflag |= (termios.CS8) + raw.lflag &= ~(termios.ICANON | termios.ECHO | + termios.IEXTEN | (termios.ISIG * 1)) raw.cc[termios.VMIN] = 1 raw.cc[termios.VTIME] = 0 tcsetattr(self.input_fd, termios.TCSADRAIN, raw) @@ -374,7 +372,7 @@ self.height, self.width = self.getheightwidth() self.__buffer = [] - + self.__posxy = 0, 0 self.__gone_tall = 0 self.__move = self.__move_short @@ -395,6 +393,7 @@ if hasattr(self, 'old_sigwinch'): signal.signal(signal.SIGWINCH, self.old_sigwinch) + del self.old_sigwinch def __sigwinch(self, signum, frame): self.height, self.width = self.getheightwidth() @@ -403,10 +402,11 @@ def push_char(self, char): trace('push char {char!r}', char=char) self.event_queue.push(char) - + def get_event(self, block=1): while self.event_queue.empty(): - while 1: # All hail Unix! + while 1: + # All hail Unix! try: self.push_char(os.read(self.input_fd, 1)) except (IOError, OSError) as err: @@ -461,7 +461,8 @@ except KeyError: height, width = struct.unpack( "hhhh", ioctl(self.input_fd, TIOCGWINSZ, "\000"*8))[0:2] - if not height: return 25, 80 + if not height: + return 25, 80 return height, width else: def getheightwidth(self): @@ -528,10 +529,12 @@ e2 = self.event_queue.get() e.data += e2.data 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') + data = os.read(self.input_fd, amount) + raw = unicode(data, self.encoding, 'replace') + #XXX: something is wrong here e.data += raw e.raw += raw return e @@ -543,9 +546,11 @@ e2 = self.event_queue.get() e.data += e2.data e.raw += e.raw - + amount = 10000 - raw = unicode(os.read(self.input_fd, amount), self.encoding, 'replace') + data = os.read(self.input_fd, amount) + raw = unicode(data, self.encoding, 'replace') + #XXX: something is wrong here e.data += raw e.raw += raw return e @@ -556,4 +561,3 @@ self.__move = self.__move_tall self.__posxy = 0, 0 self.screen = [] - diff --git a/pyrepl/unix_eventqueue.py b/pyrepl/unix_eventqueue.py --- a/pyrepl/unix_eventqueue.py +++ b/pyrepl/unix_eventqueue.py @@ -100,7 +100,7 @@ self.events.append(event) def push(self, char): - self.buf.append(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 diff --git a/testing/infrastructure.py b/testing/infrastructure.py --- a/testing/infrastructure.py +++ b/testing/infrastructure.py @@ -21,11 +21,15 @@ from pyrepl.reader import Reader from pyrepl.console import Console, Event + class EqualsAnything(object): def __eq__(self, other): return True + + EA = EqualsAnything() + class TestConsole(Console): height = 24 width = 80 @@ -38,7 +42,7 @@ def refresh(self, screen, xy): if self.next_screen is not None: - assert screen == self.next_screen, "[ %s != %s after %r ]"%( + assert screen == self.next_screen, "[ %s != %s after %r ]" % ( screen, self.next_screen, self.last_event_name) def get_event(self, block=1): @@ -51,18 +55,19 @@ 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 + 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() - diff --git a/testing/test_basic.py b/testing/test_basic.py --- a/testing/test_basic.py +++ b/testing/test_basic.py @@ -24,41 +24,48 @@ read_spec([(('self-insert', 'a'), ['a']), ( 'accept', ['a'])]) + def test_repeat(): read_spec([(('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_unix_line_discard(): read_spec([(('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_backward_kill_word(): read_spec([(('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 ']), @@ -68,18 +75,20 @@ ( 'yank-pop', ['cd ']), ( 'accept', ['cd '])]) + def test_interrupt(): with pytest.raises(KeyboardInterrupt): - read_spec([( 'interrupt', [''])]) + read_spec([('interrupt', [''])]) + # 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_down(): read_spec([(('self-insert', 'ab\ncd'), ['ab', 'cd']), ( 'up', ['ab', 'cd']), @@ -88,12 +97,14 @@ (('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_right(): read_spec([(('self-insert', 'ab'), ['ab']), ( 'left', ['ab']), @@ -101,4 +112,3 @@ ( 'right', ['acb']), (('self-insert', 'd'), ['acbd']), ( 'accept', ['acbd'])]) - diff --git a/testing/test_bugs.py b/testing/test_bugs.py --- a/testing/test_bugs.py +++ b/testing/test_bugs.py @@ -25,13 +25,17 @@ import pytest + class HistoricalTestReader(HistoricalReader, TestReader): pass + @pytest.mark.xfail(reason='event missing', run=False) def test_transpose_at_start(): - read_spec([( 'transpose', [EA, '']), - ( 'accept', [''])]) + read_spec([ + ('transpose', [EA, '']), + ('accept', [''])]) + def test_cmd_instantiation_crash(): spec = [ @@ -40,3 +44,29 @@ ('accept', ['']) ] read_spec(spec, HistoricalTestReader) + + +def test_signal_failure(monkeypatch): + import os + import pty + import signal + from pyrepl.unix_console import UnixConsole + + def failing_signal(a, b): + raise ValueError + + def really_failing_signal(a, b): + raise AssertionError + + mfd, sfd = pty.openpty() + try: + c = UnixConsole(sfd, sfd) + c.prepare() + c.restore() + monkeypatch.setattr(signal, 'signal', failing_signal) + c.prepare() + monkeypatch.setattr(signal, 'signal', really_failing_signal) + c.restore() + finally: + os.close(mfd) + os.close(sfd) diff --git a/testing/test_functional.py b/testing/test_functional.py --- a/testing/test_functional.py +++ b/testing/test_functional.py @@ -6,6 +6,7 @@ import pytest import sys + def pytest_funcarg__child(request): try: pexpect = pytest.importorskip('pexpect') @@ -17,8 +18,8 @@ child.sendline('main()') return child + def test_basic(child): child.sendline('a = 3') child.sendline('a') child.expect('3') - diff --git a/testing/test_keymap.py b/testing/test_keymap.py --- a/testing/test_keymap.py +++ b/testing/test_keymap.py @@ -1,4 +1,3 @@ -import pytest from pyrepl.keymap import compile_keymap diff --git a/testing/test_readline.py b/testing/test_readline.py --- a/testing/test_readline.py +++ b/testing/test_readline.py @@ -1,13 +1,15 @@ from pyrepl.readline import _ReadlineWrapper -import os, pty +import os +import pty + def test_raw_input(): - readline_wrapper = _ReadlineWrapper() master, slave = pty.openpty() - readline_wrapper.f_in = slave - os.write(master, 'input\n') - result = readline_wrapper.raw_input('prompt:') + readline_wrapper = _ReadlineWrapper(slave, slave) + os.write(master, b'input\n') + + result = readline_wrapper.get_reader().readline() + #result = readline_wrapper.raw_input('prompt:') assert result == 'input' # A bytes string on python2, a unicode string on python3. assert isinstance(result, str) - diff --git a/testing/test_unix_reader.py b/testing/test_unix_reader.py --- a/testing/test_unix_reader.py +++ b/testing/test_unix_reader.py @@ -1,6 +1,7 @@ from __future__ import unicode_literals from pyrepl.unix_eventqueue import EncodedQueue + def test_simple(): q = EncodedQueue({}, 'utf-8') @@ -13,4 +14,3 @@ assert q.get() is None assert event.data == a assert event.raw == b - _______________________________________________ pypy-commit mailing list [email protected] http://mail.python.org/mailman/listinfo/pypy-commit
