From 1c5798f76d1d79e184442446744d5cefe542b301 Mon Sep 17 00:00:00 2001 From: Tristan Simas Date: Fri, 6 Nov 2026 14:09:17 -0500 Subject: [PATCH 1/2] Keymap discovery runs its fresh-interpreter case in a new interpreter toad#1592 made the app test template import every toad module before it forks, so the forked child this case ran in already held toad.screens.welcome and its first assertion failed before discovery ran. The case is about an interpreter that never imported the screen, so it now runs in one: a new interpreter with this case's roots, asserting the same discovery facts. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01XbFMSqJMarXn2ZE8iAL7Sd --- tests/textual/test_keymap_discovery.py | 32 ++++++++++++++++++-------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/tests/textual/test_keymap_discovery.py b/tests/textual/test_keymap_discovery.py index 654bc7bab..3d6df7f9a 100644 --- a/tests/textual/test_keymap_discovery.py +++ b/tests/textual/test_keymap_discovery.py @@ -1,18 +1,32 @@ """Discovery includes screens that the current application has not opened.""" +from pathlib import Path +import subprocess +import sys + +import toad + +DISCOVERY = """ import sys from toad.key_modes import KeyMode, Standard from toad.keymap import KeymapCatalog, KeymapRow +assert "toad.screens.welcome" not in sys.modules +catalog = KeymapCatalog.capture(tuple(KeyMode.members_with(KeyMode))) +assert "toad.screens.welcome" in sys.modules +from toad.screens.welcome import NewSessionAction + +row = KeymapRow.of(NewSessionAction(), Standard) +assert catalog.require(row.declaration).key_title() == NewSessionAction().key_title() +assert catalog.defaults(row) == NewSessionAction.keys[Standard.binding_mode()] +""" + def test_unopened_screen_invocations_are_discovered_in_a_fresh_process(workspace): - # `workspace` gives this case its own interpreter, independent of other imports. - assert "toad.screens.welcome" not in sys.modules - catalog = KeymapCatalog.capture(tuple(KeyMode.members_with(KeyMode))) - assert "toad.screens.welcome" in sys.modules - from toad.screens.welcome import NewSessionAction - - row = KeymapRow.of(NewSessionAction(), Standard) - assert catalog.require(row.declaration).key_title() == NewSessionAction().key_title() - assert catalog.defaults(row) == NewSessionAction.keys[Standard.binding_mode()] + # The app test template imports every toad module before it forks, so only a new + # interpreter starts without the welcome screen; `workspace` gives it this case's roots. + source = str(Path(toad.__file__).parents[1]) + run = subprocess.run([sys.executable, "-c", f"import sys; sys.path.insert(0, {source!r})\n{DISCOVERY}"], + capture_output=True, text=True, timeout=60) + assert run.returncode == 0, run.stderr From a72400324b58bbe895c938682be29e6756ce4c4f Mon Sep 17 00:00:00 2001 From: Tristan Simas Date: Fri, 6 Nov 2026 14:11:22 -0500 Subject: [PATCH 2/2] The fresh-interpreter discovery check is a module the test runs with -m The check was a python -c string, so a rename of KeymapCatalog.capture would miss it and no linter or type checker read it; it is now tests/support/keymap_discovery.py. The path insert restated the editable install the test environment already provides, and is deleted. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01XbFMSqJMarXn2ZE8iAL7Sd --- tests/support/keymap_discovery.py | 19 +++++++++++++++++++ tests/textual/test_keymap_discovery.py | 23 +++-------------------- 2 files changed, 22 insertions(+), 20 deletions(-) create mode 100644 tests/support/keymap_discovery.py diff --git a/tests/support/keymap_discovery.py b/tests/support/keymap_discovery.py new file mode 100644 index 000000000..0bf98ba2a --- /dev/null +++ b/tests/support/keymap_discovery.py @@ -0,0 +1,19 @@ +"""Run as its own interpreter: keymap discovery imports a screen nothing in it has opened. + +tests/textual/test_keymap_discovery.py starts it with `python -m`, because the app test +template imports every toad module before it forks a test. +""" + +import sys + +from toad.key_modes import KeyMode, Standard +from toad.keymap import KeymapCatalog, KeymapRow + +assert "toad.screens.welcome" not in sys.modules +catalog = KeymapCatalog.capture(tuple(KeyMode.members_with(KeyMode))) +assert "toad.screens.welcome" in sys.modules +from toad.screens.welcome import NewSessionAction # noqa: E402 - discovery imported it + +row = KeymapRow.of(NewSessionAction(), Standard) +assert catalog.require(row.declaration).key_title() == NewSessionAction().key_title() +assert catalog.defaults(row) == NewSessionAction.keys[Standard.binding_mode()] diff --git a/tests/textual/test_keymap_discovery.py b/tests/textual/test_keymap_discovery.py index 3d6df7f9a..61694b636 100644 --- a/tests/textual/test_keymap_discovery.py +++ b/tests/textual/test_keymap_discovery.py @@ -4,29 +4,12 @@ import subprocess import sys -import toad - -DISCOVERY = """ -import sys - -from toad.key_modes import KeyMode, Standard -from toad.keymap import KeymapCatalog, KeymapRow - -assert "toad.screens.welcome" not in sys.modules -catalog = KeymapCatalog.capture(tuple(KeyMode.members_with(KeyMode))) -assert "toad.screens.welcome" in sys.modules -from toad.screens.welcome import NewSessionAction - -row = KeymapRow.of(NewSessionAction(), Standard) -assert catalog.require(row.declaration).key_title() == NewSessionAction().key_title() -assert catalog.defaults(row) == NewSessionAction.keys[Standard.binding_mode()] -""" +TESTS = Path(__file__).resolve().parents[1] def test_unopened_screen_invocations_are_discovered_in_a_fresh_process(workspace): # The app test template imports every toad module before it forks, so only a new # interpreter starts without the welcome screen; `workspace` gives it this case's roots. - source = str(Path(toad.__file__).parents[1]) - run = subprocess.run([sys.executable, "-c", f"import sys; sys.path.insert(0, {source!r})\n{DISCOVERY}"], - capture_output=True, text=True, timeout=60) + run = subprocess.run([sys.executable, "-m", "support.keymap_discovery"], + cwd=TESTS, capture_output=True, text=True, timeout=60) assert run.returncode == 0, run.stderr