summaryrefslogtreecommitdiffstatshomepage
path: root/tools/mpremote
diff options
context:
space:
mode:
Diffstat (limited to 'tools/mpremote')
-rw-r--r--tools/mpremote/mpremote/main.py28
-rw-r--r--tools/mpremote/mpremote/repl.py107
2 files changed, 90 insertions, 45 deletions
diff --git a/tools/mpremote/mpremote/main.py b/tools/mpremote/mpremote/main.py
index f8c913d26d..0441857fab 100644
--- a/tools/mpremote/mpremote/main.py
+++ b/tools/mpremote/mpremote/main.py
@@ -379,7 +379,27 @@ _BUILTIN_COMMAND_EXPANSIONS = {
# Disk used/free.
"df": [
"exec",
- "import os\nprint('mount \\tsize \\tused \\tavail \\tuse%')\nfor _m in [''] + os.listdir('/'):\n _s = os.stat('/' + _m)\n if not _s[0] & 1 << 14: continue\n _s = os.statvfs(_m)\n if _s[0]:\n _size = _s[0] * _s[2]; _free = _s[0] * _s[3]; print(_m, _size, _size - _free, _free, int(100 * (_size - _free) / _size), sep='\\t')",
+ """
+import os,vfs
+_f = "{:<10}{:>9}{:>9}{:>9}{:>5} {}"
+print(_f.format("filesystem", "size", "used", "avail", "use%", "mounted on"))
+try:
+ _ms = vfs.mount()
+except:
+ _ms = []
+ for _m in [""] + os.listdir("/"):
+ _m = "/" + _m
+ _s = os.stat(_m)
+ if _s[0] & 1 << 14:
+ _ms.append(("<unknown>",_m))
+for _v,_p in _ms:
+ _s = os.statvfs(_p)
+ _sz = _s[0]*_s[2]
+ if _sz:
+ _av = _s[0]*_s[3]
+ _us = 100*(_sz-_av)//_sz
+ print(_f.format(str(_v), _sz, _sz-_av, _av, _us, _p))
+""",
],
# Other shortcuts.
"reset": {
@@ -600,7 +620,11 @@ def main():
# If no commands were "actions" then implicitly finish with the REPL
# using default args.
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
except CommandError as e:
diff --git a/tools/mpremote/mpremote/repl.py b/tools/mpremote/mpremote/repl.py
index d24a7774ac..4fda04a2e2 100644
--- a/tools/mpremote/mpremote/repl.py
+++ b/tools/mpremote/mpremote/repl.py
@@ -7,51 +7,53 @@ def do_repl_main_loop(
state, console_in, console_out_write, *, escape_non_printable, code_to_inject, file_to_inject
):
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:
+ 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)
+
n = state.transport.serial.inWaiting()
- except OSError as er:
- if er.args[0] == 5: # IO error, device disappeared
- print("device disconnected")
- break
+ 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)
- 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):
@@ -86,7 +88,7 @@ def do_repl(state, args):
capture_file.flush()
try:
- do_repl_main_loop(
+ return do_repl_main_loop(
state,
console,
console_out_write,
@@ -98,3 +100,22 @@ def do_repl(state, args):
console.exit()
if capture_file is not None:
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