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:
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user