From 7a532dc2f3a167784dc31e248d802b6ff9348e43 Mon Sep 17 00:00:00 2001 From: the8thbit Date: Thu, 22 Oct 2026 22:01:18 -0500 Subject: [PATCH 1/2] Report get completion after connection cleanup --- meshtastic/__main__.py | 6 +++++- meshtastic/tests/test_main.py | 23 +++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/meshtastic/__main__.py b/meshtastic/__main__.py index ca1680a58..ef0feb771 100644 --- a/meshtastic/__main__.py +++ b/meshtastic/__main__.py @@ -1093,6 +1093,7 @@ def setSimpleConfig(modem_preset): "Use the '--get' command for a specific configuration (e.g. 'lora') instead." ) + completed_get = False if args.get: closeNow = True node = interface.getNode(args.dest, False, **getNode_kwargs) @@ -1100,7 +1101,7 @@ def setSimpleConfig(modem_preset): found = getPref(node, pref[0]) if found: - print("Completed getting preferences") + completed_get = True if args.nodes: closeNow = True @@ -1201,6 +1202,9 @@ def setSimpleConfig(modem_preset): if log_set: log_set.close() + if completed_get: + print("Completed getting preferences") + except Exception as ex: print(f"Aborting due to: {ex}") interface.close() # close the connection now, so that our app exits diff --git a/meshtastic/tests/test_main.py b/meshtastic/tests/test_main.py index e0d65f60f..42e97bb9d 100644 --- a/meshtastic/tests/test_main.py +++ b/meshtastic/tests/test_main.py @@ -1747,6 +1747,29 @@ def test_main_ch_longfast_on_non_primary_channel(capsys): # assert err == "" +@pytest.mark.unit +@pytest.mark.usefixtures("reset_mt_config") +def test_main_get_reports_completion_after_closing(): + """A one-shot get must finish shutting down before reporting success.""" + sys.argv = ["", "--get", "lora"] + mt_config.args = sys.argv + + events = [] + iface = MagicMock(autospec=SerialInterface) + iface.close.side_effect = lambda: events.append("close") + + def record_print(*values, **_kwargs): + events.append(" ".join(str(value) for value in values)) + + with patch("meshtastic.serial_interface.SerialInterface", return_value=iface): + with patch("meshtastic.__main__.getPref", return_value=True): + with patch("builtins.print", side_effect=record_print): + main() + + assert events.count("Completed getting preferences") == 1 + assert events.index("close") < events.index("Completed getting preferences") + + @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_get_with_invalid(capsys): From 430c5b2ec6c9d9231d2626cc74efdebfa2838912 Mon Sep 17 00:00:00 2001 From: the8thbit Date: Thu, 22 Oct 2026 22:21:07 -0500 Subject: [PATCH 2/2] Limit get completion to closed connections --- meshtastic/__main__.py | 4 +++- meshtastic/tests/test_main.py | 22 ++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/meshtastic/__main__.py b/meshtastic/__main__.py index ef0feb771..f60c87f83 100644 --- a/meshtastic/__main__.py +++ b/meshtastic/__main__.py @@ -1195,14 +1195,16 @@ def setSimpleConfig(modem_preset): time.sleep(int(args.wait_to_disconnect)) # if the user didn't ask for serial debugging output, we might want to exit after we've done our operation + did_close_interface = False if (not args.seriallog) and closeNow: interface.close() # after running command then exit + did_close_interface = True # Close any structured logs after we've done all of our API operations if log_set: log_set.close() - if completed_get: + if completed_get and did_close_interface: print("Completed getting preferences") except Exception as ex: diff --git a/meshtastic/tests/test_main.py b/meshtastic/tests/test_main.py index 42e97bb9d..1f31dc5a6 100644 --- a/meshtastic/tests/test_main.py +++ b/meshtastic/tests/test_main.py @@ -1770,6 +1770,28 @@ def record_print(*values, **_kwargs): assert events.index("close") < events.index("Completed getting preferences") +@pytest.mark.unit +@pytest.mark.usefixtures("reset_mt_config") +@pytest.mark.parametrize("keep_open_args", [["--listen"], ["--seriallog", "stdout"]]) +def test_main_get_does_not_report_completion_while_connection_stays_open( + capsys, keep_open_args +): + """A get must not report post-cleanup success when cleanup is skipped.""" + sys.argv = ["", "--get", "lora", *keep_open_args] + mt_config.args = sys.argv + + iface = MagicMock(autospec=SerialInterface) + + with patch("meshtastic.serial_interface.SerialInterface", return_value=iface): + with patch("meshtastic.__main__.getPref", return_value=True): + with patch("meshtastic.__main__.time.sleep", side_effect=KeyboardInterrupt): + main() + + out, _err = capsys.readouterr() + iface.close.assert_not_called() + assert "Completed getting preferences" not in out + + @pytest.mark.unit @pytest.mark.usefixtures("reset_mt_config") def test_main_get_with_invalid(capsys):