@@ -93,13 +93,13 @@ async def kbd_intr_task(exec_task, s):
93
93
94
94
95
95
# 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 ):
97
97
print ("Starting asyncio REPL..." )
98
98
if g is None :
99
99
g = __import__ ("__main__" ).__dict__
100
100
try :
101
101
micropython .kbd_intr (- 1 )
102
- s = asyncio .StreamReader (sys . stdin )
102
+ s = asyncio .StreamReader (s_in )
103
103
# clear = True
104
104
hist = [None ] * _HISTORY_LIMIT
105
105
hist_i = 0 # Index of most recent entry.
@@ -108,7 +108,7 @@ async def task(g=None, prompt="--> "):
108
108
t = 0 # timestamp of most recent character.
109
109
while True :
110
110
hist_b = 0 # How far back in the history are we currently.
111
- sys . stdout .write (prompt )
111
+ s_out .write (prompt )
112
112
cmd : str = ""
113
113
paste = False
114
114
curs = 0 # cursor offset from end of cmd buffer
@@ -122,7 +122,7 @@ async def task(g=None, prompt="--> "):
122
122
if c == 0x0A :
123
123
# LF
124
124
if paste :
125
- sys . stdout .write (b )
125
+ s_out .write (b )
126
126
cmd += b
127
127
continue
128
128
# If the previous character was also LF, and was less
@@ -132,9 +132,9 @@ async def task(g=None, prompt="--> "):
132
132
continue
133
133
if curs :
134
134
# move cursor to end of the line
135
- sys . stdout .write ("\x1b [{}C" .format (curs ))
135
+ s_out .write ("\x1b [{}C" .format (curs ))
136
136
curs = 0
137
- sys . stdout .write ("\n " )
137
+ s_out .write ("\n " )
138
138
if cmd :
139
139
# Push current command.
140
140
hist [hist_i ] = cmd
@@ -144,46 +144,46 @@ async def task(g=None, prompt="--> "):
144
144
145
145
result = await execute (cmd , g , s )
146
146
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 " )
149
149
break
150
150
elif c == 0x08 or c == 0x7F :
151
151
# Backspace.
152
152
if cmd :
153
153
if curs :
154
154
cmd = "" .join ((cmd [: - curs - 1 ], cmd [- curs :]))
155
- sys . stdout .write (
155
+ s_out .write (
156
156
"\x08 \x1b [K"
157
157
) # 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
160
160
else :
161
161
cmd = cmd [:- 1 ]
162
- sys . stdout .write ("\x08 \x08 " )
162
+ s_out .write ("\x08 \x08 " )
163
163
elif c == CHAR_CTRL_A :
164
- await raw_repl (s , g )
164
+ await raw_repl (s_in , s_out , g )
165
165
break
166
166
elif c == CHAR_CTRL_B :
167
167
continue
168
168
elif c == CHAR_CTRL_C :
169
169
if paste :
170
170
break
171
- sys . stdout .write ("\n " )
171
+ s_out .write ("\n " )
172
172
break
173
173
elif c == CHAR_CTRL_D :
174
174
if paste :
175
175
result = await execute (cmd , g , s )
176
176
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 " )
179
179
break
180
180
181
- sys . stdout .write ("\n " )
181
+ s_out .write ("\n " )
182
182
# Shutdown asyncio.
183
183
asyncio .new_event_loop ()
184
184
return
185
185
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 " )
187
187
paste = True
188
188
elif c == 0x1B :
189
189
# Start of escape sequence.
@@ -193,66 +193,66 @@ async def task(g=None, prompt="--> "):
193
193
hist [(hist_i - hist_b ) % _HISTORY_LIMIT ] = cmd
194
194
# Clear current command.
195
195
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 )
199
199
# Go backwards or forwards in the history.
200
200
if key == "[A" :
201
201
hist_b = min (hist_n , hist_b + 1 )
202
202
else :
203
203
hist_b = max (0 , hist_b - 1 )
204
204
# Update current command.
205
205
cmd = hist [(hist_i - hist_b ) % _HISTORY_LIMIT ]
206
- sys . stdout .write (cmd )
206
+ s_out .write (cmd )
207
207
elif key == "[D" : # left
208
208
if curs < len (cmd ) - 1 :
209
209
curs += 1
210
- sys . stdout .write ("\x1b " )
211
- sys . stdout .write (key )
210
+ s_out .write ("\x1b " )
211
+ s_out .write (key )
212
212
elif key == "[C" : # right
213
213
if curs :
214
214
curs -= 1
215
- sys . stdout .write ("\x1b " )
216
- sys . stdout .write (key )
215
+ s_out .write ("\x1b " )
216
+ s_out .write (key )
217
217
elif key == "[H" : # home
218
218
pcurs = curs
219
219
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
221
221
elif key == "[F" : # end
222
222
pcurs = curs
223
223
curs = 0
224
- sys . stdout .write ("\x1b [{}C" .format (pcurs )) # move cursor right
224
+ s_out .write ("\x1b [{}C" .format (pcurs )) # move cursor right
225
225
else :
226
- # sys.stdout .write("\\x")
227
- # sys.stdout .write(hex(c))
226
+ # s_out .write("\\x")
227
+ # s_out .write(hex(c))
228
228
pass
229
229
else :
230
230
if curs :
231
231
# inserting into middle of line
232
232
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
235
235
else :
236
- sys . stdout .write (b )
236
+ s_out .write (b )
237
237
cmd += b
238
238
finally :
239
239
micropython .kbd_intr (3 )
240
240
241
241
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 ())
245
245
eof = False
246
246
idx = 0
247
247
buff = bytearray (window )
248
248
file = b""
249
249
while not eof :
250
250
for idx in range (window ):
251
- b = await s .read (1 )
251
+ b = s_in .read (1 )
252
252
c = ord (b )
253
253
if c == CHAR_CTRL_C or c == CHAR_CTRL_D :
254
254
# end of file
255
- sys . stdout .write (chr (CHAR_CTRL_D ))
255
+ s_out .write (chr (CHAR_CTRL_D ))
256
256
if c == CHAR_CTRL_C :
257
257
raise KeyboardInterrupt
258
258
file += buff [:idx ]
@@ -262,46 +262,51 @@ async def raw_paste(s, g, window=512):
262
262
263
263
if not eof :
264
264
file += buff
265
- sys . stdout .write ("\x01 " ) # indicate window available to host
265
+ s_out .write ("\x01 " ) # indicate window available to host
266
266
267
267
return file
268
268
269
269
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
+ """
271
276
heading = "raw REPL; CTRL-B to exit\n "
272
277
line = ""
273
- sys . stdout .write (heading )
278
+ s_out .write (heading )
274
279
275
280
while True :
276
281
line = ""
277
- sys . stdout .write (">" )
282
+ s_out .write (">" )
278
283
while True :
279
- b = await s .read (1 )
284
+ b = s_in .read (1 )
280
285
c = ord (b )
281
286
if c == CHAR_CTRL_A :
282
287
rline = line
283
288
line = ""
284
289
285
290
if len (rline ) == 2 and ord (rline [0 ]) == CHAR_CTRL_E :
286
291
if rline [1 ] == "A" :
287
- line = await raw_paste (s , g )
292
+ line = raw_paste (s_in , s_out )
288
293
break
289
294
else :
290
295
# reset raw REPL
291
- sys . stdout .write (heading )
292
- sys . stdout .write (">" )
296
+ s_out .write (heading )
297
+ s_out .write (">" )
293
298
continue
294
299
elif c == CHAR_CTRL_B :
295
300
# exit raw REPL
296
- sys . stdout .write ("\n " )
301
+ s_out .write ("\n " )
297
302
return 0
298
303
elif c == CHAR_CTRL_C :
299
304
# clear line
300
305
line = ""
301
306
elif c == CHAR_CTRL_D :
302
307
# entry finished
303
308
# indicate reception of command
304
- sys . stdout .write ("OK" )
309
+ s_out .write ("OK" )
305
310
break
306
311
else :
307
312
# let through any other raw 8-bit value
@@ -310,16 +315,16 @@ async def raw_repl(s: asyncio.StreamReader, g: dict):
310
315
if len (line ) == 0 :
311
316
# Normally used to trigger soft-reset but stay in raw mode.
312
317
# 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 )
315
320
316
321
try :
317
322
result = exec (line , g )
318
323
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 ))
321
326
except Exception as ex :
322
327
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