Load local sessions in the background for the Sessions popup

command_choose ran `herdr session list --json` (up to its 5 s timeout)
before the popup drew anything, contradicting the documented immediate
display. Run the listing on a worker thread and hand the selector a
Future: the popup appears at once with a "… 本机 正在查询" placeholder,
then fills in the local rows and any discovery warning when the result
arrives, mirroring how remote hosts already load.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-10-03 03:03:09 +00:00
co-authored by Claude Fable 5.1
parent 907813867a
commit 435ba91d1e
3 changed files with 98 additions and 3 deletions
+47
View File
@@ -1,5 +1,6 @@
from __future__ import annotations
from concurrent.futures import Future
import curses
from dataclasses import replace
import os
@@ -1045,5 +1046,51 @@ class SelectorRobustnessTests(unittest.TestCase):
self.assertIn("未登记", selector.message)
class SelectorBackgroundLoadingTests(unittest.TestCase):
def test_local_sessions_arrive_after_the_popup_is_drawn(self) -> None:
with tempfile.TemporaryDirectory() as directory:
pending: Future[tuple[list[str], list[str]]] = Future()
selector = SessionSelector(
[],
RemoteRegistry(Path(directory) / "remotes.json"),
"/missing/ssh",
pending_local_sessions=pending,
)
self.addCleanup(selector.pool.close)
selector.message = "startup"
# Before the listing finishes the popup shows a placeholder row.
selector._poll_local_sessions() # type: ignore[attr-defined]
kinds = {row.key: row for row in selector._rows()} # type: ignore[attr-defined]
self.assertIn("loading:local", kinds)
self.assertFalse(kinds["loading:local"].selectable)
self.assertNotIn("local:work", kinds)
pending.set_result((["work", "default"], ["local Herdr session discovery was slow"]))
selector._poll_local_sessions() # type: ignore[attr-defined]
self.assertIsNone(selector.pending_local_sessions)
keys = {row.key for row in selector._rows()} # type: ignore[attr-defined]
self.assertIn("local:work", keys)
self.assertIn("local:default", keys)
self.assertNotIn("loading:local", keys)
self.assertEqual(selector.message, "startup; local Herdr session discovery was slow")
def test_failed_local_listing_becomes_a_message(self) -> None:
with tempfile.TemporaryDirectory() as directory:
pending: Future[tuple[list[str], list[str]]] = Future()
pending.set_exception(OSError("herdr is not executable"))
selector = SessionSelector(
[],
RemoteRegistry(Path(directory) / "remotes.json"),
"/missing/ssh",
pending_local_sessions=pending,
)
self.addCleanup(selector.pool.close)
selector._poll_local_sessions() # type: ignore[attr-defined]
self.assertIsNone(selector.pending_local_sessions)
self.assertIn("herdr is not executable", selector.message)
self.assertEqual(selector.local_sessions, [])
if __name__ == "__main__":
unittest.main()