pull/1017/merge
Andrew Leech 2025-06-03 03:41:40 +00:00 zatwierdzone przez GitHub
commit a181cfdf65
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
1 zmienionych plików z 60 dodań i 55 usunięć

Wyświetl plik

@ -93,13 +93,13 @@ __exec_task = asyncio.create_task(__code())
# REPL task. Invoke this with an optional mutable globals dict. # REPL task. Invoke this with an optional mutable globals dict.
async def task(g=None, prompt="--> "): async def task(g=None, prompt="--> ", s_in=sys.stdin, s_out=sys.stdout):
print("Starting asyncio REPL...") print("Starting asyncio REPL...")
if g is None: if g is None:
g = __import__("__main__").__dict__ g = __import__("__main__").__dict__
try: try:
micropython.kbd_intr(-1) micropython.kbd_intr(-1)
s = asyncio.StreamReader(sys.stdin) s = asyncio.StreamReader(s_in)
# clear = True # clear = True
hist = [None] * _HISTORY_LIMIT hist = [None] * _HISTORY_LIMIT
hist_i = 0 # Index of most recent entry. hist_i = 0 # Index of most recent entry.
@ -108,7 +108,7 @@ async def task(g=None, prompt="--> "):
t = 0 # timestamp of most recent character. t = 0 # timestamp of most recent character.
while True: while True:
hist_b = 0 # How far back in the history are we currently. hist_b = 0 # How far back in the history are we currently.
sys.stdout.write(prompt) s_out.write(prompt)
cmd: str = "" cmd: str = ""
paste = False paste = False
curs = 0 # cursor offset from end of cmd buffer curs = 0 # cursor offset from end of cmd buffer
@ -122,7 +122,7 @@ async def task(g=None, prompt="--> "):
if c == 0x0A: if c == 0x0A:
# LF # LF
if paste: if paste:
sys.stdout.write(b) s_out.write(b)
cmd += b cmd += b
continue continue
# If the previous character was also LF, and was less # If the previous character was also LF, and was less
@ -132,9 +132,9 @@ async def task(g=None, prompt="--> "):
continue continue
if curs: if curs:
# move cursor to end of the line # move cursor to end of the line
sys.stdout.write("\x1b[{}C".format(curs)) s_out.write("\x1b[{}C".format(curs))
curs = 0 curs = 0
sys.stdout.write("\n") s_out.write("\n")
if cmd: if cmd:
# Push current command. # Push current command.
hist[hist_i] = cmd hist[hist_i] = cmd
@ -144,46 +144,46 @@ async def task(g=None, prompt="--> "):
result = await execute(cmd, g, s) result = await execute(cmd, g, s)
if result is not None: if result is not None:
sys.stdout.write(repr(result)) s_out.write(repr(result))
sys.stdout.write("\n") s_out.write("\n")
break break
elif c == 0x08 or c == 0x7F: elif c == 0x08 or c == 0x7F:
# Backspace. # Backspace.
if cmd: if cmd:
if curs: if curs:
cmd = "".join((cmd[: -curs - 1], cmd[-curs:])) cmd = "".join((cmd[: -curs - 1], cmd[-curs:]))
sys.stdout.write( s_out.write(
"\x08\x1b[K" "\x08\x1b[K"
) # move cursor back, erase to end of line ) # move cursor back, erase to end of line
sys.stdout.write(cmd[-curs:]) # redraw line s_out.write(cmd[-curs:]) # redraw line
sys.stdout.write("\x1b[{}D".format(curs)) # reset cursor location s_out.write("\x1b[{}D".format(curs)) # reset cursor location
else: else:
cmd = cmd[:-1] cmd = cmd[:-1]
sys.stdout.write("\x08 \x08") s_out.write("\x08 \x08")
elif c == CHAR_CTRL_A: elif c == CHAR_CTRL_A:
await raw_repl(s, g) await raw_repl(s_in, s_out, g)
break break
elif c == CHAR_CTRL_B: elif c == CHAR_CTRL_B:
continue continue
elif c == CHAR_CTRL_C: elif c == CHAR_CTRL_C:
if paste: if paste:
break break
sys.stdout.write("\n") s_out.write("\n")
break break
elif c == CHAR_CTRL_D: elif c == CHAR_CTRL_D:
if paste: if paste:
result = await execute(cmd, g, s) result = await execute(cmd, g, s)
if result is not None: if result is not None:
sys.stdout.write(repr(result)) s_out.write(repr(result))
sys.stdout.write("\n") s_out.write("\n")
break break
sys.stdout.write("\n") s_out.write("\n")
# Shutdown asyncio. # Shutdown asyncio.
asyncio.new_event_loop() asyncio.new_event_loop()
return return
elif c == CHAR_CTRL_E: elif c == CHAR_CTRL_E:
sys.stdout.write("paste mode; Ctrl-C to cancel, Ctrl-D to finish\n===\n") s_out.write("paste mode; Ctrl-C to cancel, Ctrl-D to finish\n===\n")
paste = True paste = True
elif c == 0x1B: elif c == 0x1B:
# Start of escape sequence. # Start of escape sequence.
@ -193,9 +193,9 @@ async def task(g=None, prompt="--> "):
hist[(hist_i - hist_b) % _HISTORY_LIMIT] = cmd hist[(hist_i - hist_b) % _HISTORY_LIMIT] = cmd
# Clear current command. # Clear current command.
b = "\x08" * len(cmd) b = "\x08" * len(cmd)
sys.stdout.write(b) s_out.write(b)
sys.stdout.write(" " * len(cmd)) s_out.write(" " * len(cmd))
sys.stdout.write(b) s_out.write(b)
# Go backwards or forwards in the history. # Go backwards or forwards in the history.
if key == "[A": if key == "[A":
hist_b = min(hist_n, hist_b + 1) hist_b = min(hist_n, hist_b + 1)
@ -203,56 +203,56 @@ async def task(g=None, prompt="--> "):
hist_b = max(0, hist_b - 1) hist_b = max(0, hist_b - 1)
# Update current command. # Update current command.
cmd = hist[(hist_i - hist_b) % _HISTORY_LIMIT] cmd = hist[(hist_i - hist_b) % _HISTORY_LIMIT]
sys.stdout.write(cmd) s_out.write(cmd)
elif key == "[D": # left elif key == "[D": # left
if curs < len(cmd) - 1: if curs < len(cmd) - 1:
curs += 1 curs += 1
sys.stdout.write("\x1b") s_out.write("\x1b")
sys.stdout.write(key) s_out.write(key)
elif key == "[C": # right elif key == "[C": # right
if curs: if curs:
curs -= 1 curs -= 1
sys.stdout.write("\x1b") s_out.write("\x1b")
sys.stdout.write(key) s_out.write(key)
elif key == "[H": # home elif key == "[H": # home
pcurs = curs pcurs = curs
curs = len(cmd) curs = len(cmd)
sys.stdout.write("\x1b[{}D".format(curs - pcurs)) # move cursor left s_out.write("\x1b[{}D".format(curs - pcurs)) # move cursor left
elif key == "[F": # end elif key == "[F": # end
pcurs = curs pcurs = curs
curs = 0 curs = 0
sys.stdout.write("\x1b[{}C".format(pcurs)) # move cursor right s_out.write("\x1b[{}C".format(pcurs)) # move cursor right
else: else:
# sys.stdout.write("\\x") # s_out.write("\\x")
# sys.stdout.write(hex(c)) # s_out.write(hex(c))
pass pass
else: else:
if curs: if curs:
# inserting into middle of line # inserting into middle of line
cmd = "".join((cmd[:-curs], b, cmd[-curs:])) cmd = "".join((cmd[:-curs], b, cmd[-curs:]))
sys.stdout.write(cmd[-curs - 1 :]) # redraw line to end s_out.write(cmd[-curs - 1 :]) # redraw line to end
sys.stdout.write("\x1b[{}D".format(curs)) # reset cursor location s_out.write("\x1b[{}D".format(curs)) # reset cursor location
else: else:
sys.stdout.write(b) s_out.write(b)
cmd += b cmd += b
finally: finally:
micropython.kbd_intr(3) micropython.kbd_intr(3)
async def raw_paste(s, g, window=512): def raw_paste(s_in, s_out, window=512):
sys.stdout.write("R\x01") # supported s_out.write("R\x01") # supported
sys.stdout.write(bytearray([window & 0xFF, window >> 8, 0x01]).decode()) s_out.write(bytearray([window & 0xFF, window >> 8, 0x01]).decode())
eof = False eof = False
idx = 0 idx = 0
buff = bytearray(window) buff = bytearray(window)
file = b"" file = b""
while not eof: while not eof:
for idx in range(window): for idx in range(window):
b = await s.read(1) b = s_in.read(1)
c = ord(b) c = ord(b)
if c == CHAR_CTRL_C or c == CHAR_CTRL_D: if c == CHAR_CTRL_C or c == CHAR_CTRL_D:
# end of file # end of file
sys.stdout.write(chr(CHAR_CTRL_D)) s_out.write(chr(CHAR_CTRL_D))
if c == CHAR_CTRL_C: if c == CHAR_CTRL_C:
raise KeyboardInterrupt raise KeyboardInterrupt
file += buff[:idx] file += buff[:idx]
@ -262,21 +262,26 @@ async def raw_paste(s, g, window=512):
if not eof: if not eof:
file += buff file += buff
sys.stdout.write("\x01") # indicate window available to host s_out.write("\x01") # indicate window available to host
return file return file
async def raw_repl(s: asyncio.StreamReader, g: dict): async def raw_repl(s_in: io.IOBase, s_out: io.IOBase, g: dict):
"""
This function is blocking to prevent other
async tasks from writing to the stdio stream and
breaking the raw repl session.
"""
heading = "raw REPL; CTRL-B to exit\n" heading = "raw REPL; CTRL-B to exit\n"
line = "" line = ""
sys.stdout.write(heading) s_out.write(heading)
while True: while True:
line = "" line = ""
sys.stdout.write(">") s_out.write(">")
while True: while True:
b = await s.read(1) b = s_in.read(1)
c = ord(b) c = ord(b)
if c == CHAR_CTRL_A: if c == CHAR_CTRL_A:
rline = line rline = line
@ -284,16 +289,16 @@ async def raw_repl(s: asyncio.StreamReader, g: dict):
if len(rline) == 2 and ord(rline[0]) == CHAR_CTRL_E: if len(rline) == 2 and ord(rline[0]) == CHAR_CTRL_E:
if rline[1] == "A": if rline[1] == "A":
line = await raw_paste(s, g) line = raw_paste(s_in, s_out)
break break
else: else:
# reset raw REPL # reset raw REPL
sys.stdout.write(heading) s_out.write(heading)
sys.stdout.write(">") s_out.write(">")
continue continue
elif c == CHAR_CTRL_B: elif c == CHAR_CTRL_B:
# exit raw REPL # exit raw REPL
sys.stdout.write("\n") s_out.write("\n")
return 0 return 0
elif c == CHAR_CTRL_C: elif c == CHAR_CTRL_C:
# clear line # clear line
@ -301,7 +306,7 @@ async def raw_repl(s: asyncio.StreamReader, g: dict):
elif c == CHAR_CTRL_D: elif c == CHAR_CTRL_D:
# entry finished # entry finished
# indicate reception of command # indicate reception of command
sys.stdout.write("OK") s_out.write("OK")
break break
else: else:
# let through any other raw 8-bit value # let through any other raw 8-bit value
@ -310,16 +315,16 @@ async def raw_repl(s: asyncio.StreamReader, g: dict):
if len(line) == 0: if len(line) == 0:
# Normally used to trigger soft-reset but stay in raw mode. # Normally used to trigger soft-reset but stay in raw mode.
# Fake it for aiorepl / mpremote. # Fake it for aiorepl / mpremote.
sys.stdout.write("Ignored: soft reboot\n") s_out.write("Ignored: soft reboot\n")
sys.stdout.write(heading) s_out.write(heading)
try: try:
result = exec(line, g) result = exec(line, g)
if result is not None: if result is not None:
sys.stdout.write(repr(result)) s_out.write(repr(result))
sys.stdout.write(chr(CHAR_CTRL_D)) s_out.write(chr(CHAR_CTRL_D))
except Exception as ex: except Exception as ex:
print(line) print(line)
sys.stdout.write(chr(CHAR_CTRL_D)) s_out.write(chr(CHAR_CTRL_D))
sys.print_exception(ex, sys.stdout) sys.print_exception(ex, s_out)
sys.stdout.write(chr(CHAR_CTRL_D)) s_out.write(chr(CHAR_CTRL_D))