Skip to content

Commit 19d90c9

Browse files
basic stdin.readline working!
Yet todo on this topic: * no display of input as being entered * no raw_input prompt displayed * raw input still includes the newline * \r instead of \n as it probably should be --HG-- branch : scroll-frontend
1 parent 8a7295d commit 19d90c9

File tree

1 file changed

+89
-8
lines changed

1 file changed

+89
-8
lines changed

bpython/scrollfrontend/repl.py

Lines changed: 89 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
import logging
55
import code
66
import threading
7+
import Queue
78
from cStringIO import StringIO
9+
import traceback
810

911
from bpython.autocomplete import Autocomplete, SIMPLE
1012
from bpython.repl import Repl as BpythonRepl
@@ -21,6 +23,7 @@
2123
from fmtstr.bpythonparse import parse as bpythonparse
2224
from fmtstr.bpythonparse import func_for_letter
2325

26+
from bpython.scrollfrontend.manual_readline import char_sequences as rl_char_sequences
2427
from bpython.scrollfrontend.manual_readline import get_updated_char_sequences
2528
from bpython.scrollfrontend.abbreviate import substitute_abbreviations
2629
from bpython.scrollfrontend.interaction import StatusBar
@@ -46,6 +49,56 @@
4649

4750
from bpython.keys import cli_key_dispatch as key_dispatch
4851

52+
class FakeStdin(object):
53+
def __init__(self, on_receive_tuple):
54+
self.request_from_executing_code = Queue.Queue(maxsize=1)
55+
self.response_queue = Queue.Queue(maxsize=1)
56+
self.has_focus = False
57+
self.current_line = ''
58+
self.cursor_offset_in_line = 0
59+
self.on_receive_tuple = on_receive_tuple
60+
61+
def wait_for_request_or_finish(self):
62+
logging.debug('waiting for message from running code (stack depth %r)', len(traceback.format_stack()))
63+
msg = self.request_from_executing_code.get(timeout=3)
64+
logging.debug('FakeStdin received message %r', msg)
65+
self.process_request(msg)
66+
67+
def process_request(self, msg):
68+
if msg == 'can haz stdin line plz':
69+
logging.debug('setting focus to stdin')
70+
self.has_focus = True
71+
elif isinstance(msg, tuple):
72+
logging.debug('calling on_finish_running_code')
73+
self.on_receive_tuple(*msg)
74+
else:
75+
logging.debug('unrecognized message!')
76+
assert False
77+
78+
def process_event(self, e):
79+
assert self.has_focus
80+
if e in rl_char_sequences:
81+
self.cursor_offset_in_line, self.current_line = rl_char_sequences[e](self.cursor_offset_in_line, self._current_line)
82+
#TODO EOF on ctrl-d
83+
else: # add normal character
84+
logging.debug('adding normal char %r to current line', e)
85+
self.current_line = (self.current_line[:self.cursor_offset_in_line] +
86+
e +
87+
self.current_line[self.cursor_offset_in_line:])
88+
self.cursor_offset_in_line += 1
89+
90+
if self.current_line.endswith(("\n", "\r")):
91+
self.has_focus = False
92+
self.response_queue.put(self.current_line)
93+
self.current_line = ''
94+
self.cursor_offset_in_line = 0
95+
self.wait_for_request_or_finish()
96+
97+
def readline(self):
98+
self.request_from_executing_code.put('can haz stdin line plz')
99+
return self.response_queue.get(timeout=15)
100+
101+
49102
class Repl(BpythonRepl):
50103
"""
51104
@@ -100,6 +153,8 @@ def __init__(self):
100153
self.cursor_offset_in_line = 0 # from the left, 0 means first char
101154
self.done = True
102155

156+
self.stdin = FakeStdin(self.on_finish_running_code)
157+
103158
self.paste_mode = False
104159

105160
self.width = None # will both be set by a window resize event
@@ -109,8 +164,10 @@ def __init__(self):
109164
def __enter__(self):
110165
self.orig_stdout = sys.stdout
111166
self.orig_stderr = sys.stderr
167+
self.orig_stdin = sys.stdin
112168
sys.stdout = StringIO()
113169
sys.stderr = StringIO()
170+
sys.stdin = self.stdin
114171
return self
115172

116173
def __exit__(self, *args):
@@ -147,6 +204,8 @@ def process_event(self, e):
147204
return
148205
if self.status_bar.has_focus:
149206
return self.status_bar.process_event(e)
207+
if self.stdin.has_focus:
208+
return self.stdin.process_event(e)
150209

151210
if e in self.rl_char_sequences:
152211
self.cursor_offset_in_line, self._current_line = self.rl_char_sequences[e](self.cursor_offset_in_line, self._current_line)
@@ -224,15 +283,18 @@ def on_enter(self):
224283
self.rl_history.append(self._current_line)
225284
self.rl_history.last()
226285
self.history.append(self._current_line)
227-
output, err, self.done, indent = self.push(self._current_line)
286+
self.push(self._current_line)
287+
288+
def on_finish_running_code(self, output, error, done, indent):
228289
if output:
229290
self.display_lines.extend(sum([paint.display_linize(line, self.width) for line in output.split('\n')], []))
230-
if err:
291+
if error:
231292
self.display_lines.extend([func_for_letter(self.config.color_scheme['error'])(line)
232293
for line in sum([paint.display_linize(line, self.width)
233-
for line in err.split('\n')], [])])
294+
for line in error.split('\n')], [])])
234295
self._current_line = ' '*indent
235296
self.cursor_offset_in_line = len(self._current_line)
297+
self.done = done
236298

237299
def on_tab(self, back=False):
238300
"""Do something on tab key
@@ -314,7 +376,21 @@ def push(self, line):
314376
"""Push a line of code onto the buffer, run the buffer
315377
316378
If the interpreter successfully runs the code, clear the buffer
317-
Return ("for stdout", "for_stderr", finished?)
379+
"""
380+
t = threading.Thread(target=self.runsource, args=(line,))
381+
t.daemon = True
382+
t.start()
383+
logging.debug('push() is now waiting')
384+
self.stdin.wait_for_request_or_finish()
385+
logging.debug('push() done waiting')
386+
387+
def runsource(self, line):
388+
"""Push a line of code on to the buffer, run the buffer, clean up
389+
390+
Makes requests for input and announces being done as necessary via threadsafe queue
391+
sends messages:
392+
* request for readline
393+
* (stdoutput, error, done?, amount_to_indent_next_line)
318394
"""
319395
self.buffer.append(line)
320396
indent = len(re.match(r'[ ]*', line).group())
@@ -327,7 +403,7 @@ def push(self, line):
327403
indent = max(0, indent - self.config.tab_length)
328404
out_spot = sys.stdout.tell()
329405
err_spot = sys.stderr.tell()
330-
#logging.debug('running %r in interpreter', self.buffer)
406+
logging.debug('running %r in interpreter', self.buffer)
331407
unfinished = self.interp.runsource('\n'.join(self.buffer))
332408

333409
#current line not added to display buffer if quitting
@@ -349,15 +425,17 @@ def push(self, line):
349425

350426
if unfinished and not err:
351427
logging.debug('unfinished - line added to buffer')
352-
return (None, None, False, indent)
428+
self.stdin.request_from_executing_code.put((None, None, False, indent))
353429
else:
354430
logging.debug('finished - buffer cleared')
355431
self.display_lines.extend(self.display_buffer_lines)
356432
self.display_buffer = []
357433
self.buffer = []
358434
if err:
359435
indent = 0
360-
return (out[:-1], err[:-1], True, indent)
436+
logging.debug('sending output info')
437+
self.stdin.request_from_executing_code.put((out[:-1], err[:-1], True, indent))
438+
logging.debug('sent output info')
361439

362440
def unhighlight_paren(self):
363441
"""modify line in self.display_buffer to unhighlight a paren if possible"""
@@ -380,7 +458,9 @@ def current_line_formatted(self):
380458
fs = bpythonparse(format(self.tokenize(self._current_line), self.formatter))
381459
else:
382460
fs = fmtstr(self._current_line)
383-
logging.debug('calculating current formatted line: %r', repr(fs))
461+
if hasattr(self, 'old_fs') and str(fs) != str(self.old_fs):
462+
logging.debug('calculating current formatted line: %r', repr(fs))
463+
self.old_fs = fs
384464
return fs
385465

386466
@property
@@ -482,6 +562,7 @@ def paint(self, about_to_exit=False):
482562
cursor_column = (self.cursor_offset_in_line + len(self.display_line_with_prompt) - len(self._current_line)) % width
483563

484564
if self.list_win_visible:
565+
#TODO infobox not properly expanding window! try reduce( docs about halfway down a 80x24 terminal
485566
logging.debug('infobox display code running')
486567
visible_space_above = history.height
487568
visible_space_below = min_height - cursor_row

0 commit comments

Comments
 (0)