Skip to content

Commit e156b6f

Browse files
pi-anlandrewleech
authored andcommitted
micropython/aiorepl: Allow passing in stream to use.
Defaults to sys.stdin/out if not provided. Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
1 parent 96bd01e commit e156b6f

File tree

1 file changed

+60
-55
lines changed

1 file changed

+60
-55
lines changed

micropython/aiorepl/aiorepl.py

Lines changed: 60 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,13 @@ async def kbd_intr_task(exec_task, s):
9393

9494

9595
# REPL task. Invoke this with an optional mutable globals dict.
96-
async def task(g=None, prompt="--> "):
96+
async def task(g=None, prompt="--> ", s_in=sys.stdin, s_out=sys.stdout):
9797
print("Starting asyncio REPL...")
9898
if g is None:
9999
g = __import__("__main__").__dict__
100100
try:
101101
micropython.kbd_intr(-1)
102-
s = asyncio.StreamReader(sys.stdin)
102+
s = asyncio.StreamReader(s_in)
103103
# clear = True
104104
hist = [None] * _HISTORY_LIMIT
105105
hist_i = 0 # Index of most recent entry.
@@ -108,7 +108,7 @@ async def task(g=None, prompt="--> "):
108108
t = 0 # timestamp of most recent character.
109109
while True:
110110
hist_b = 0 # How far back in the history are we currently.
111-
sys.stdout.write(prompt)
111+
s_out.write(prompt)
112112
cmd: str = ""
113113
paste = False
114114
curs = 0 # cursor offset from end of cmd buffer
@@ -122,7 +122,7 @@ async def task(g=None, prompt="--> "):
122122
if c == 0x0A:
123123
# LF
124124
if paste:
125-
sys.stdout.write(b)
125+
s_out.write(b)
126126
cmd += b
127127
continue
128128
# If the previous character was also LF, and was less
@@ -132,9 +132,9 @@ async def task(g=None, prompt="--> "):
132132
continue
133133
if curs:
134134
# move cursor to end of the line
135-
sys.stdout.write("\x1b[{}C".format(curs))
135+
s_out.write("\x1b[{}C".format(curs))
136136
curs = 0
137-
sys.stdout.write("\n")
137+
s_out.write("\n")
138138
if cmd:
139139
# Push current command.
140140
hist[hist_i] = cmd
@@ -144,46 +144,46 @@ async def task(g=None, prompt="--> "):
144144

145145
result = await execute(cmd, g, s)
146146
if result is not None:
147-
sys.stdout.write(repr(result))
148-
sys.stdout.write("\n")
147+
s_out.write(repr(result))
148+
s_out.write("\n")
149149
break
150150
elif c == 0x08 or c == 0x7F:
151151
# Backspace.
152152
if cmd:
153153
if curs:
154154
cmd = "".join((cmd[: -curs - 1], cmd[-curs:]))
155-
sys.stdout.write(
155+
s_out.write(
156156
"\x08\x1b[K"
157157
) # move cursor back, erase to end of line
158-
sys.stdout.write(cmd[-curs:]) # redraw line
159-
sys.stdout.write("\x1b[{}D".format(curs)) # reset cursor location
158+
s_out.write(cmd[-curs:]) # redraw line
159+
s_out.write("\x1b[{}D".format(curs)) # reset cursor location
160160
else:
161161
cmd = cmd[:-1]
162-
sys.stdout.write("\x08 \x08")
162+
s_out.write("\x08 \x08")
163163
elif c == CHAR_CTRL_A:
164-
await raw_repl(s, g)
164+
await raw_repl(s_in, s_out, g)
165165
break
166166
elif c == CHAR_CTRL_B:
167167
continue
168168
elif c == CHAR_CTRL_C:
169169
if paste:
170170
break
171-
sys.stdout.write("\n")
171+
s_out.write("\n")
172172
break
173173
elif c == CHAR_CTRL_D:
174174
if paste:
175175
result = await execute(cmd, g, s)
176176
if result is not None:
177-
sys.stdout.write(repr(result))
178-
sys.stdout.write("\n")
177+
s_out.write(repr(result))
178+
s_out.write("\n")
179179
break
180180

181-
sys.stdout.write("\n")
181+
s_out.write("\n")
182182
# Shutdown asyncio.
183183
asyncio.new_event_loop()
184184
return
185185
elif c == CHAR_CTRL_E:
186-
sys.stdout.write("paste mode; Ctrl-C to cancel, Ctrl-D to finish\n===\n")
186+
s_out.write("paste mode; Ctrl-C to cancel, Ctrl-D to finish\n===\n")
187187
paste = True
188188
elif c == 0x1B:
189189
# Start of escape sequence.
@@ -193,66 +193,66 @@ async def task(g=None, prompt="--> "):
193193
hist[(hist_i - hist_b) % _HISTORY_LIMIT] = cmd
194194
# Clear current command.
195195
b = "\x08" * len(cmd)
196-
sys.stdout.write(b)
197-
sys.stdout.write(" " * len(cmd))
198-
sys.stdout.write(b)
196+
s_out.write(b)
197+
s_out.write(" " * len(cmd))
198+
s_out.write(b)
199199
# Go backwards or forwards in the history.
200200
if key == "[A":
201201
hist_b = min(hist_n, hist_b + 1)
202202
else:
203203
hist_b = max(0, hist_b - 1)
204204
# Update current command.
205205
cmd = hist[(hist_i - hist_b) % _HISTORY_LIMIT]
206-
sys.stdout.write(cmd)
206+
s_out.write(cmd)
207207
elif key == "[D": # left
208208
if curs < len(cmd) - 1:
209209
curs += 1
210-
sys.stdout.write("\x1b")
211-
sys.stdout.write(key)
210+
s_out.write("\x1b")
211+
s_out.write(key)
212212
elif key == "[C": # right
213213
if curs:
214214
curs -= 1
215-
sys.stdout.write("\x1b")
216-
sys.stdout.write(key)
215+
s_out.write("\x1b")
216+
s_out.write(key)
217217
elif key == "[H": # home
218218
pcurs = curs
219219
curs = len(cmd)
220-
sys.stdout.write("\x1b[{}D".format(curs - pcurs)) # move cursor left
220+
s_out.write("\x1b[{}D".format(curs - pcurs)) # move cursor left
221221
elif key == "[F": # end
222222
pcurs = curs
223223
curs = 0
224-
sys.stdout.write("\x1b[{}C".format(pcurs)) # move cursor right
224+
s_out.write("\x1b[{}C".format(pcurs)) # move cursor right
225225
else:
226-
# sys.stdout.write("\\x")
227-
# sys.stdout.write(hex(c))
226+
# s_out.write("\\x")
227+
# s_out.write(hex(c))
228228
pass
229229
else:
230230
if curs:
231231
# inserting into middle of line
232232
cmd = "".join((cmd[:-curs], b, cmd[-curs:]))
233-
sys.stdout.write(cmd[-curs - 1 :]) # redraw line to end
234-
sys.stdout.write("\x1b[{}D".format(curs)) # reset cursor location
233+
s_out.write(cmd[-curs - 1 :]) # redraw line to end
234+
s_out.write("\x1b[{}D".format(curs)) # reset cursor location
235235
else:
236-
sys.stdout.write(b)
236+
s_out.write(b)
237237
cmd += b
238238
finally:
239239
micropython.kbd_intr(3)
240240

241241

242-
async def raw_paste(s, g, window=512):
243-
sys.stdout.write("R\x01") # supported
244-
sys.stdout.write(bytearray([window & 0xFF, window >> 8, 0x01]).decode())
242+
def raw_paste(s_in, s_out, window=512):
243+
s_out.write("R\x01") # supported
244+
s_out.write(bytearray([window & 0xFF, window >> 8, 0x01]).decode())
245245
eof = False
246246
idx = 0
247247
buff = bytearray(window)
248248
file = b""
249249
while not eof:
250250
for idx in range(window):
251-
b = await s.read(1)
251+
b = s_in.read(1)
252252
c = ord(b)
253253
if c == CHAR_CTRL_C or c == CHAR_CTRL_D:
254254
# end of file
255-
sys.stdout.write(chr(CHAR_CTRL_D))
255+
s_out.write(chr(CHAR_CTRL_D))
256256
if c == CHAR_CTRL_C:
257257
raise KeyboardInterrupt
258258
file += buff[:idx]
@@ -262,46 +262,51 @@ async def raw_paste(s, g, window=512):
262262

263263
if not eof:
264264
file += buff
265-
sys.stdout.write("\x01") # indicate window available to host
265+
s_out.write("\x01") # indicate window available to host
266266

267267
return file
268268

269269

270-
async def raw_repl(s: asyncio.StreamReader, g: dict):
270+
async def raw_repl(s_in: io.IOBase, s_out: io.IOBase, g: dict):
271+
"""
272+
This function is blocking to prevent other
273+
async tasks from writing to the stdio stream and
274+
breaking the raw repl session.
275+
"""
271276
heading = "raw REPL; CTRL-B to exit\n"
272277
line = ""
273-
sys.stdout.write(heading)
278+
s_out.write(heading)
274279

275280
while True:
276281
line = ""
277-
sys.stdout.write(">")
282+
s_out.write(">")
278283
while True:
279-
b = await s.read(1)
284+
b = s_in.read(1)
280285
c = ord(b)
281286
if c == CHAR_CTRL_A:
282287
rline = line
283288
line = ""
284289

285290
if len(rline) == 2 and ord(rline[0]) == CHAR_CTRL_E:
286291
if rline[1] == "A":
287-
line = await raw_paste(s, g)
292+
line = raw_paste(s_in, s_out)
288293
break
289294
else:
290295
# reset raw REPL
291-
sys.stdout.write(heading)
292-
sys.stdout.write(">")
296+
s_out.write(heading)
297+
s_out.write(">")
293298
continue
294299
elif c == CHAR_CTRL_B:
295300
# exit raw REPL
296-
sys.stdout.write("\n")
301+
s_out.write("\n")
297302
return 0
298303
elif c == CHAR_CTRL_C:
299304
# clear line
300305
line = ""
301306
elif c == CHAR_CTRL_D:
302307
# entry finished
303308
# indicate reception of command
304-
sys.stdout.write("OK")
309+
s_out.write("OK")
305310
break
306311
else:
307312
# let through any other raw 8-bit value
@@ -310,16 +315,16 @@ async def raw_repl(s: asyncio.StreamReader, g: dict):
310315
if len(line) == 0:
311316
# Normally used to trigger soft-reset but stay in raw mode.
312317
# Fake it for aiorepl / mpremote.
313-
sys.stdout.write("Ignored: soft reboot\n")
314-
sys.stdout.write(heading)
318+
s_out.write("Ignored: soft reboot\n")
319+
s_out.write(heading)
315320

316321
try:
317322
result = exec(line, g)
318323
if result is not None:
319-
sys.stdout.write(repr(result))
320-
sys.stdout.write(chr(CHAR_CTRL_D))
324+
s_out.write(repr(result))
325+
s_out.write(chr(CHAR_CTRL_D))
321326
except Exception as ex:
322327
print(line)
323-
sys.stdout.write(chr(CHAR_CTRL_D))
324-
sys.print_exception(ex, sys.stdout)
325-
sys.stdout.write(chr(CHAR_CTRL_D))
328+
s_out.write(chr(CHAR_CTRL_D))
329+
sys.print_exception(ex, s_out)
330+
s_out.write(chr(CHAR_CTRL_D))

0 commit comments

Comments
 (0)