diff --git a/Lib/idlelib/run.py b/Lib/idlelib/run.py index 47c4cbdcb8..c5323b14ff 100644 --- a/Lib/idlelib/run.py +++ b/Lib/idlelib/run.py @@ -145,6 +145,10 @@ def main(del_exitfunc=False): args=((LOCALHOST, port),)) sockthread.daemon = True sockthread.start() + + global buffer_handler + buffer_handler = BufferOutputHandler() + while 1: try: if exit_now: @@ -300,7 +304,8 @@ def cleanup_traceback(tb, exclude): tb[i] = fn, ln, nm, line def flush_stdout(): - """XXX How to do this now?""" + buffer_handler.flush() + def exit(): """Exit subprocess, possibly after first clearing exit functions. @@ -515,6 +520,68 @@ def close(self): self.shell.close() +class BufferOutputHandler: + LIMIT_BACKPRESSURE = 1000 + + def __init__(self): + self.buffer_output = queue.Queue() + self.ev_backpressure = threading.Event() + self.ev_backpressure.set() + + self.th = threading.Thread( + name='BufferOutputHandler', + target=self.forward_buffer, + daemon=True) + self.th.start() + + def forward_buffer(self): + active = 'stdout' + parts = [] + + def _flush_active(): + s = ''.join(parts) + stream = getattr(sys, active) + StdOutputFile.write(stream, s) + parts.clear() + + while True: + time.sleep(0.05) # allow time for buffer to collect content + + while self.buffer_output.qsize(): + sel, content = self.buffer_output.get() + if sel == active: + parts.append(content) + else: + _flush_active() + active = sel + parts.append(content) + if parts: + _flush_active() + self.ev_backpressure.set() + + def put(self, tags, s): + self.buffer_output.put((tags, s)) + if self.buffer_output.qsize() > self.LIMIT_BACKPRESSURE: + self.ev_backpressure.clear() + self.ev_backpressure.wait() + + def flush(self): + while True: + if self.buffer_output.qsize() == 0: + break + time.sleep(0.050) + + +class BufferStdOutputFile(StdOutputFile): + def write(self, s): + if self.tags not in ('stdout', 'stderr'): + # fallback on regular call + return super().write(s) + + buffer_handler.put(self.tags, s) + return len(s) + + class MyHandler(rpc.RPCHandler): def handle(self): @@ -524,9 +591,9 @@ def handle(self): self.console = self.get_remote_proxy("console") sys.stdin = StdInputFile(self.console, "stdin", iomenu.encoding, iomenu.errors) - sys.stdout = StdOutputFile(self.console, "stdout", + sys.stdout = BufferStdOutputFile(self.console, "stdout", iomenu.encoding, iomenu.errors) - sys.stderr = StdOutputFile(self.console, "stderr", + sys.stderr = BufferStdOutputFile(self.console, "stderr", iomenu.encoding, "backslashreplace") sys.displayhook = rpc.displayhook @@ -601,8 +668,8 @@ def runcode(self, code): jit = self.rpchandler.console.getvar("<>") if jit: self.rpchandler.interp.open_remote_stack_viewer() - else: - flush_stdout() + + flush_stdout() def interrupt_the_server(self): if interruptable: