mirror of
https://github.com/micropython/micropython.git
synced 2025-12-16 09:50:15 +01:00
tools/mpremote: Fix disconnect handling on Windows and Linux.
Changes in this commit: - Handle SerialException on Windows when device disconnects. - Print clean 'device disconnected' message instead of stack trace. - Fix terminal formatting issues on Linux after disconnect. - Return disconnected state after console cleanup to avoid terminal issues. This ensures proper disconnect messages on both platforms without showing confusing error traces to users. Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
This commit is contained in:
committed by
Damien George
parent
e33a0f4682
commit
3a97175f5f
@@ -620,7 +620,11 @@ def main():
|
|||||||
# If no commands were "actions" then implicitly finish with the REPL
|
# If no commands were "actions" then implicitly finish with the REPL
|
||||||
# using default args.
|
# using default args.
|
||||||
if state.run_repl_on_completion():
|
if state.run_repl_on_completion():
|
||||||
do_repl(state, argparse_repl().parse_args([]))
|
disconnected = do_repl(state, argparse_repl().parse_args([]))
|
||||||
|
|
||||||
|
# Handle disconnection message
|
||||||
|
if disconnected:
|
||||||
|
print("\ndevice disconnected")
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
except CommandError as e:
|
except CommandError as e:
|
||||||
|
|||||||
@@ -7,51 +7,53 @@ def do_repl_main_loop(
|
|||||||
state, console_in, console_out_write, *, escape_non_printable, code_to_inject, file_to_inject
|
state, console_in, console_out_write, *, escape_non_printable, code_to_inject, file_to_inject
|
||||||
):
|
):
|
||||||
while True:
|
while True:
|
||||||
console_in.waitchar(state.transport.serial)
|
|
||||||
c = console_in.readchar()
|
|
||||||
if c:
|
|
||||||
if c in (b"\x1d", b"\x18"): # ctrl-] or ctrl-x, quit
|
|
||||||
break
|
|
||||||
elif c == b"\x04": # ctrl-D
|
|
||||||
# special handling needed for ctrl-D if filesystem is mounted
|
|
||||||
state.transport.write_ctrl_d(console_out_write)
|
|
||||||
elif c == b"\x0a" and code_to_inject is not None: # ctrl-j, inject code
|
|
||||||
state.transport.serial.write(code_to_inject)
|
|
||||||
elif c == b"\x0b" and file_to_inject is not None: # ctrl-k, inject script
|
|
||||||
console_out_write(bytes("Injecting %s\r\n" % file_to_inject, "utf8"))
|
|
||||||
state.transport.enter_raw_repl(soft_reset=False)
|
|
||||||
with open(file_to_inject, "rb") as f:
|
|
||||||
pyfile = f.read()
|
|
||||||
try:
|
|
||||||
state.transport.exec_raw_no_follow(pyfile)
|
|
||||||
except TransportError as er:
|
|
||||||
console_out_write(b"Error:\r\n")
|
|
||||||
console_out_write(er)
|
|
||||||
state.transport.exit_raw_repl()
|
|
||||||
else:
|
|
||||||
state.transport.serial.write(c)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
n = state.transport.serial.inWaiting()
|
console_in.waitchar(state.transport.serial)
|
||||||
except OSError as er:
|
c = console_in.readchar()
|
||||||
if er.args[0] == 5: # IO error, device disappeared
|
if c:
|
||||||
print("device disconnected")
|
if c in (b"\x1d", b"\x18"): # ctrl-] or ctrl-x, quit
|
||||||
break
|
break
|
||||||
|
elif c == b"\x04": # ctrl-D
|
||||||
if n > 0:
|
# special handling needed for ctrl-D if filesystem is mounted
|
||||||
dev_data_in = state.transport.serial.read(n)
|
state.transport.write_ctrl_d(console_out_write)
|
||||||
if dev_data_in is not None:
|
elif c == b"\x0a" and code_to_inject is not None: # ctrl-j, inject code
|
||||||
if escape_non_printable:
|
state.transport.serial.write(code_to_inject)
|
||||||
# Pass data through to the console, with escaping of non-printables.
|
elif c == b"\x0b" and file_to_inject is not None: # ctrl-k, inject script
|
||||||
console_data_out = bytearray()
|
console_out_write(bytes("Injecting %s\r\n" % file_to_inject, "utf8"))
|
||||||
for c in dev_data_in:
|
state.transport.enter_raw_repl(soft_reset=False)
|
||||||
if c in (8, 9, 10, 13, 27) or 32 <= c <= 126:
|
with open(file_to_inject, "rb") as f:
|
||||||
console_data_out.append(c)
|
pyfile = f.read()
|
||||||
else:
|
try:
|
||||||
console_data_out.extend(b"[%02x]" % c)
|
state.transport.exec_raw_no_follow(pyfile)
|
||||||
|
except TransportError as er:
|
||||||
|
console_out_write(b"Error:\r\n")
|
||||||
|
console_out_write(er)
|
||||||
|
state.transport.exit_raw_repl()
|
||||||
else:
|
else:
|
||||||
console_data_out = dev_data_in
|
state.transport.serial.write(c)
|
||||||
console_out_write(console_data_out)
|
|
||||||
|
n = state.transport.serial.inWaiting()
|
||||||
|
if n > 0:
|
||||||
|
dev_data_in = state.transport.serial.read(n)
|
||||||
|
if dev_data_in is not None:
|
||||||
|
if escape_non_printable:
|
||||||
|
# Pass data through to the console, with escaping of non-printables.
|
||||||
|
console_data_out = bytearray()
|
||||||
|
for c in dev_data_in:
|
||||||
|
if c in (8, 9, 10, 13, 27) or 32 <= c <= 126:
|
||||||
|
console_data_out.append(c)
|
||||||
|
else:
|
||||||
|
console_data_out.extend(b"[%02x]" % c)
|
||||||
|
else:
|
||||||
|
console_data_out = dev_data_in
|
||||||
|
console_out_write(console_data_out)
|
||||||
|
|
||||||
|
except OSError as er:
|
||||||
|
if _is_disconnect_exception(er):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def do_repl(state, args):
|
def do_repl(state, args):
|
||||||
@@ -86,7 +88,7 @@ def do_repl(state, args):
|
|||||||
capture_file.flush()
|
capture_file.flush()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
do_repl_main_loop(
|
return do_repl_main_loop(
|
||||||
state,
|
state,
|
||||||
console,
|
console,
|
||||||
console_out_write,
|
console_out_write,
|
||||||
@@ -98,3 +100,22 @@ def do_repl(state, args):
|
|||||||
console.exit()
|
console.exit()
|
||||||
if capture_file is not None:
|
if capture_file is not None:
|
||||||
capture_file.close()
|
capture_file.close()
|
||||||
|
|
||||||
|
|
||||||
|
def _is_disconnect_exception(exception):
|
||||||
|
"""
|
||||||
|
Check if an exception indicates device disconnect.
|
||||||
|
|
||||||
|
Returns True if the exception indicates the device has disconnected,
|
||||||
|
False otherwise.
|
||||||
|
"""
|
||||||
|
if isinstance(exception, OSError):
|
||||||
|
if hasattr(exception, 'args') and len(exception.args) > 0:
|
||||||
|
# IO error, device disappeared
|
||||||
|
if exception.args[0] == 5:
|
||||||
|
return True
|
||||||
|
# Check for common disconnect messages in the exception string
|
||||||
|
exception_str = str(exception)
|
||||||
|
disconnect_indicators = ["Write timeout", "Device disconnected", "ClearCommError failed"]
|
||||||
|
return any(indicator in exception_str for indicator in disconnect_indicators)
|
||||||
|
return False
|
||||||
|
|||||||
Reference in New Issue
Block a user