From ece86401ab37ab49fcdbeb6228517de04e047623 Mon Sep 17 00:00:00 2001 From: Pearsteam <93873756+Birnendampf@users.noreply.github.com> Date: Wed, 15 Apr 2026 15:52:44 +0200 Subject: [PATCH 1/3] create regression test for issue --- tests/input/test_input_default_mode.py | 34 ++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 tests/input/test_input_default_mode.py diff --git a/tests/input/test_input_default_mode.py b/tests/input/test_input_default_mode.py new file mode 100644 index 0000000000..495585ce6d --- /dev/null +++ b/tests/input/test_input_default_mode.py @@ -0,0 +1,34 @@ +import asyncio + +import pytest + +from textual.app import App, ComposeResult +from textual.screen import Screen +from textual.widgets import Input + + +class DefaultScreen(Screen): + def compose(self) -> ComposeResult: + yield Input() + yield Input("Hello, world!") + + +class InputApp(App): + DEFAULT_MODE = "default" + MODES = {"default": DefaultScreen} + + +@pytest.mark.skipif( + not hasattr(asyncio, "eager_task_factory"), reason="only occurs with eager tasks" +) +async def test_input_default_mode(): + """Test that Input in a DEFAULT_MODE Screen doesn't crash. + + Regression test for https://github.com/textualize/textual/issues/6444. + + """ + # changing this will not affect other tests since pytest-asyncio runs every function + # in a new event loop by default + asyncio.get_running_loop().set_task_factory(asyncio.eager_task_factory) + async with InputApp().run_test(): + pass From a053edad415bbd8572ee94a91eb023cbba2d3601 Mon Sep 17 00:00:00 2001 From: Pearsteam <93873756+Birnendampf@users.noreply.github.com> Date: Wed, 15 Apr 2026 16:05:55 +0200 Subject: [PATCH 2/3] introduce fix --- src/textual/widgets/_input.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/textual/widgets/_input.py b/src/textual/widgets/_input.py index ef8be70c2d..a2a01d43b1 100644 --- a/src/textual/widgets/_input.py +++ b/src/textual/widgets/_input.py @@ -252,7 +252,7 @@ def cursor_position(self, position: int) -> None: """Set the current position of the cursor.""" self.selection = Selection.cursor(position) - selection: Reactive[Selection] = reactive(Selection.cursor(0)) + selection: Reactive[Selection] = reactive(Selection.cursor(0), init=False) """The currently selected range of text.""" placeholder = reactive("") @@ -505,6 +505,9 @@ def validate_selection(self, selection: Selection) -> Selection: return Selection(clamp(start, 0, value_length), clamp(end, 0, value_length)) def _watch_selection(self, selection: Selection) -> None: + if not self.is_mounted: + return + self.app.clear_selection() self.app.cursor_position = self.cursor_screen_offset if not self._initial_value: From d30ee84bb44d0ee42cd85332b62b1a47471d1f60 Mon Sep 17 00:00:00 2001 From: Pearsteam <93873756+Birnendampf@users.noreply.github.com> Date: Wed, 15 Apr 2026 16:42:28 +0200 Subject: [PATCH 3/3] update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef2e5b6086..de0ab579ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## Unreleased +### Fixed + +- Fix crash when using Input in a DEFAULT_MODE screen https://github.com/Textualize/textual/pull/6492 + ### Added - Added `DOM.update_classes` https://github.com/Textualize/textual/pull/6478