From acd0f84bcb8e7be0f58fa3934fbee13b95895da3 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sun, 26 May 2024 13:43:28 +0100 Subject: [PATCH 1/5] fix freeze --- CHANGELOG.md | 7 ++++++- pyproject.toml | 2 +- src/textual/widget.py | 11 ++++++----- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 671f519b4d..0d2411d151 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,12 +5,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). -## Unreleased +## [0.63.4] ### Added - Added `immediate` switch to `Signal.publish` +### Fixed + +- Fixed freeze in recompose from bindings + ## [0.63.3] - 2024-05-24 ### Fixed @@ -2019,6 +2023,7 @@ https://textual.textualize.io/blog/2022/11/08/version-040/#version-040 - New handler system for messages that doesn't require inheritance - Improved traceback handling +[0.63.4]: https://github.com/Textualize/textual/compare/v0.63.3...v0.63.4 [0.63.3]: https://github.com/Textualize/textual/compare/v0.63.2...v0.63.3 [0.63.2]: https://github.com/Textualize/textual/compare/v0.63.1...v0.63.2 [0.63.1]: https://github.com/Textualize/textual/compare/v0.63.0...v0.63.1 diff --git a/pyproject.toml b/pyproject.toml index f4f7302d1d..9cd24b9b78 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "textual" -version = "0.63.3" +version = "0.63.4" homepage = "https://github.com/Textualize/textual" repository = "https://github.com/Textualize/textual" documentation = "https://textual.textualize.io/" diff --git a/src/textual/widget.py b/src/textual/widget.py index 029e411905..696468b30e 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -1123,11 +1123,12 @@ async def recompose(self) -> None: Recomposing will remove children and call `self.compose` again to remount. """ - if self._parent is not None: - async with self.batch(): - await self.query("*").exclude(".-textual-system").remove() - if self.is_attached: - await self.mount_all(compose(self)) + if not self.is_attached: + return + async with self.batch(): + await self.query("*").exclude(".-textual-system").remove() + if self.is_attached: + await self.mount_all(compose(self)) def _post_register(self, app: App) -> None: """Called when the instance is registered. From e490a94cdd4e4cbd68203638ab217ea0c98fe0f2 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sun, 26 May 2024 13:52:47 +0100 Subject: [PATCH 2/5] test --- src/textual/widgets/_footer.py | 2 +- tests/snapshot_tests/conftest.py | 1 + tests/test_freeze.py | 44 +++++++++++++++++++++++++++++--- 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/src/textual/widgets/_footer.py b/src/textual/widgets/_footer.py index 3dffdb33b6..811ef2b76f 100644 --- a/src/textual/widgets/_footer.py +++ b/src/textual/widgets/_footer.py @@ -162,7 +162,7 @@ def compose(self) -> ComposeResult: def on_mount(self) -> None: async def bindings_changed(screen: Screen) -> None: - if screen is self.screen: + if self.is_attached and screen is self.screen: await self.recompose() self.screen.bindings_updated_signal.subscribe(self, bindings_changed) diff --git a/tests/snapshot_tests/conftest.py b/tests/snapshot_tests/conftest.py index e69de29bb2..0da04c7866 100644 --- a/tests/snapshot_tests/conftest.py +++ b/tests/snapshot_tests/conftest.py @@ -0,0 +1 @@ +fg diff --git a/tests/test_freeze.py b/tests/test_freeze.py index 8d31d20306..37edd2e5d5 100644 --- a/tests/test_freeze.py +++ b/tests/test_freeze.py @@ -1,8 +1,10 @@ import pytest -from textual.app import App -from textual.screen import Screen -from textual.widgets import Footer, Header, Input +from textual import on +from textual.app import App, ComposeResult +from textual.containers import ScrollableContainer +from textual.screen import ModalScreen, Screen +from textual.widgets import Button, Footer, Header, Input, Label class MyScreen(Screen): @@ -24,3 +26,39 @@ async def test_freeze(): with pytest.raises(Exception): async with app.run_test(): raise Exception("never raised") + + +async def test_button_freeze(): + class MyModal(ModalScreen[str | None]): + BINDINGS = [("escape", "cancel", "Cancel")] + + def __init__(self, s: str): + self._s = s + super().__init__() + + def compose(self) -> ComposeResult: + with ScrollableContainer(): + yield Label("My Screen") + yield Button("Save", id="save", variant="success") + yield Footer() + + @on(Button.Pressed, "#save") + def handle_save(self) -> None: + self.dismiss("Yes" if (self._s == "Hello!") else None) + + def action_cancel(self) -> None: + self.dismiss(None) + + class MyApp(App): + BINDINGS = [("m", "modal", "Show modal")] + + def compose(self) -> ComposeResult: + yield Footer() + + def action_modal(self) -> None: + s = "Hello!" + self.push_screen(MyModal(s)) + + app = MyApp() + async with app.run_test() as pilot: + await pilot.press("tab", "return") From fbe1e1617c0e0b1450341dd9172ed204826805ee Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sun, 26 May 2024 13:57:31 +0100 Subject: [PATCH 3/5] fix --- tests/snapshot_tests/conftest.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/snapshot_tests/conftest.py b/tests/snapshot_tests/conftest.py index 0da04c7866..e69de29bb2 100644 --- a/tests/snapshot_tests/conftest.py +++ b/tests/snapshot_tests/conftest.py @@ -1 +0,0 @@ -fg From dee6b4ad0955368b544da5d40af6e9db377b4fdd Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sun, 26 May 2024 14:06:11 +0100 Subject: [PATCH 4/5] remove faulty test --- tests/test_freeze.py | 44 +++----------------------------------------- 1 file changed, 3 insertions(+), 41 deletions(-) diff --git a/tests/test_freeze.py b/tests/test_freeze.py index 37edd2e5d5..8d31d20306 100644 --- a/tests/test_freeze.py +++ b/tests/test_freeze.py @@ -1,10 +1,8 @@ import pytest -from textual import on -from textual.app import App, ComposeResult -from textual.containers import ScrollableContainer -from textual.screen import ModalScreen, Screen -from textual.widgets import Button, Footer, Header, Input, Label +from textual.app import App +from textual.screen import Screen +from textual.widgets import Footer, Header, Input class MyScreen(Screen): @@ -26,39 +24,3 @@ async def test_freeze(): with pytest.raises(Exception): async with app.run_test(): raise Exception("never raised") - - -async def test_button_freeze(): - class MyModal(ModalScreen[str | None]): - BINDINGS = [("escape", "cancel", "Cancel")] - - def __init__(self, s: str): - self._s = s - super().__init__() - - def compose(self) -> ComposeResult: - with ScrollableContainer(): - yield Label("My Screen") - yield Button("Save", id="save", variant="success") - yield Footer() - - @on(Button.Pressed, "#save") - def handle_save(self) -> None: - self.dismiss("Yes" if (self._s == "Hello!") else None) - - def action_cancel(self) -> None: - self.dismiss(None) - - class MyApp(App): - BINDINGS = [("m", "modal", "Show modal")] - - def compose(self) -> ComposeResult: - yield Footer() - - def action_modal(self) -> None: - s = "Hello!" - self.push_screen(MyModal(s)) - - app = MyApp() - async with app.run_test() as pilot: - await pilot.press("tab", "return") From b90717844f7bd8efd7d8f61cb6f0a3318aead7e0 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sun, 26 May 2024 14:15:54 +0100 Subject: [PATCH 5/5] change log [skipci] --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d2411d151..8d889e62ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Fixed -- Fixed freeze in recompose from bindings +- Fixed freeze in recompose from bindings https://github.com/Textualize/textual/pull/4558 ## [0.63.3] - 2024-05-24