From 7a8d6920e81a819e44ee3a8d7bba83ba35140e02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Gir=C3=A3o=20Serr=C3=A3o?= <5621605+rodrigogiraoserrao@users.noreply.github.com> Date: Tue, 16 May 2023 11:14:54 +0100 Subject: [PATCH 1/2] Error when dismissing non-active screen. Related issues: #2575. --- CHANGELOG.md | 1 + src/textual/app.py | 2 +- src/textual/screen.py | 9 +++++++++ tests/test_screens.py | 14 ++++++++++++++ 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5991f818d..bce29efda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - App `title` and `sub_title` attributes can be set to any type https://github.com/Textualize/textual/issues/2521 - Using `Widget.move_child` where the target and the child being moved are the same is now a no-op https://github.com/Textualize/textual/issues/1743 +- Calling `dismiss` on a screen that is not at the top of the stack now raises an exception https://github.com/Textualize/textual/issues/2575 ### Fixed diff --git a/src/textual/app.py b/src/textual/app.py index 12f66cf04..c1376cd22 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -156,7 +156,7 @@ class ScreenError(Exception): class ScreenStackError(ScreenError): - """Raised when attempting to pop the last screen from the stack.""" + """Raised when trying to manipulate the screen stack incorrectly.""" class CssPathError(Exception): diff --git a/src/textual/screen.py b/src/textual/screen.py index af0b006be..a16273121 100644 --- a/src/textual/screen.py +++ b/src/textual/screen.py @@ -28,6 +28,7 @@ from ._callback import invoke from ._compositor import Compositor, MapGeometry from ._context import visible_screen_stack from ._types import CallbackType +from .app import ScreenStackError from .binding import Binding from .css.match import match from .css.parse import parse_selectors @@ -771,6 +772,10 @@ class Screen(Generic[ScreenResultType], Widget): Args: result: The optional result to be passed to the result callback. + Raises: + ScreenStackError: If trying to dismiss a screen that is not at the top of + the stack. + Note: If the screen was pushed with a callback, the callback will be called with the given result and then a call to @@ -778,6 +783,10 @@ class Screen(Generic[ScreenResultType], Widget): no callback was provided calling this method is the same as simply calling [`App.pop_screen`][textual.app.App.pop_screen]. """ + if self is not self.app.screen: + raise ScreenStackError( + f"Can't dismiss screen {self} that's not at the top of the stack." + ) if result is not self._NoResult and self._result_callbacks: self._result_callbacks[-1](cast(ScreenResultType, result)) self.app.pop_screen() diff --git a/tests/test_screens.py b/tests/test_screens.py index 2e3dbfcbe..5b29b1dd5 100644 --- a/tests/test_screens.py +++ b/tests/test_screens.py @@ -192,3 +192,17 @@ async def test_auto_focus(): assert app.focused is None app.pop_screen() assert app.focused.id == "two" + + +async def test_dismiss_non_top_screen(): + class MyApp(App[None]): + async def key_p(self) -> None: + self.bottom, top = Screen(), Screen() + await self.push_screen(self.bottom) + await self.push_screen(top) + + app = MyApp() + async with app.run_test() as pilot: + await pilot.press("p") + with pytest.raises(ScreenStackError): + app.bottom.dismiss() From 93f4de918ce5dfa9f29ea693a2f815282def6625 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Gir=C3=A3o=20Serr=C3=A3o?= <5621605+rodrigogiraoserrao@users.noreply.github.com> Date: Tue, 16 May 2023 11:30:14 +0100 Subject: [PATCH 2/2] Fix circular import. --- src/textual/screen.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/textual/screen.py b/src/textual/screen.py index a16273121..09505cc53 100644 --- a/src/textual/screen.py +++ b/src/textual/screen.py @@ -28,7 +28,6 @@ from ._callback import invoke from ._compositor import Compositor, MapGeometry from ._context import visible_screen_stack from ._types import CallbackType -from .app import ScreenStackError from .binding import Binding from .css.match import match from .css.parse import parse_selectors @@ -784,6 +783,8 @@ class Screen(Generic[ScreenResultType], Widget): simply calling [`App.pop_screen`][textual.app.App.pop_screen]. """ if self is not self.app.screen: + from .app import ScreenStackError + raise ScreenStackError( f"Can't dismiss screen {self} that's not at the top of the stack." )