Refactor FakeStdin
@@ -49,7 +49,7 @@ from thonny.common import (
get_augmented_system_path,
update_system_path,
is_same_path,
-)
+ EOFCommand)
import queue
BEFORE_STATEMENT_MARKER = "_thonny_hidden_before_stmt"
@@ -1092,27 +1092,66 @@ class VM:
self._vm._exit_io_function()
class FakeInputStream(FakeStream):
- def _generic_read(self, method, limit=-1):
- # is there some queued input?
- if not self._vm._input_queue.empty():
- cmd = self._vm._input_queue.get()
- self._processed_symbol_count += len(cmd.data)
- return cmd.data
-
- # new input needs to be requested
+ def __init__(self, vm, target_stream):
+ super().__init__(vm, target_stream)
+ self._buffer = ""
+ self._eof = False
+
+ def _generic_read(self, method, original_limit):
+ if original_limit is None:
+ effective_limit = -1
+ elif method == "readlines" and original_limit > -1:
+ # NB! size hint is defined in weird way
+ # "no more lines will be read if the total size (in bytes/characters)
+ # of all lines so far **exceeds** the hint".
+ effective_limit = original_limit + 1
+ else:
+ effective_limit = original_limit
+
try:
self._vm._enter_io_function()
- self._vm.send_message(BackendEvent("InputRequest", method=method, limit=limit))
-
while True:
+ if effective_limit == 0:
+ result = ""
+ break
+
+ elif effective_limit > 0 and len(self._buffer) >= effective_limit:
+ result = self._buffer[:effective_limit]
+ self._buffer = self._buffer[effective_limit:]
+ if method == "readlines" and not result.endswith("\n") and "\n" in self._buffer:
+ # limit is just a hint
+ # https://docs.python.org/3/library/io.html#io.IOBase.readlines
+ extra = self._buffer[:self._buffer.find("\n") + 1]
+ result += extra
+ self._buffer = self._buffer[len(extra):]
+ break
+
+ elif method == "readline" and "\n" in self._buffer:
+ pos = self._buffer.find("\n") + 1
+ result = self._buffer[:pos]
+ self._buffer = self._buffer[pos:]
+ break
+
+ elif self._eof:
+ result = self._buffer
+ self._buffer = ""
+ self._eof = False # That's how official implementation does
+ break
+
+ else:
+ self._vm.send_message(BackendEvent("InputRequest", method=method, limit=original_limit))
cmd = self._vm._fetch_command()
if isinstance(cmd, InputSubmission):
- self._processed_symbol_count += len(cmd.data)
- return cmd.data
+ self._buffer += cmd.data
+ elif isinstance(cmd, EOFCommand):
+ self._eof = True
elif isinstance(cmd, InlineCommand):
self._vm.handle_command(cmd)
else:
raise RuntimeError("Wrong type of command when waiting for input")
+
+ return result
+
finally:
self._vm._exit_io_function()
@@ -1123,7 +1162,7 @@ class VM:
return self._generic_read("readline", limit)
def readlines(self, limit=-1):
- return self._generic_read("readlines", limit)
+ return self._generic_read("readlines", limit).splitlines(True)
def __next__(self):
return self.readline()
