diff --git a/CHANGELOG.md b/CHANGELOG.md index ab6ce20e8a..10ed793804 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Added `Content.blank` https://github.com/Textualize/textual/pull/6264 +### Fixed + +- Fixed exception when setting `loading` attribute before mount https://github.com/Textualize/textual/pull/6268 + ## [6.7.1] - 2025-12-1 ### Fixed diff --git a/src/textual/widget.py b/src/textual/widget.py index c7af2a6d2a..9f6d28b056 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -1035,7 +1035,10 @@ def set_loading(self, loading: bool) -> None: def _watch_loading(self, loading: bool) -> None: """Called when the 'loading' reactive is changed.""" - self.set_loading(loading) + if not self.is_mounted: + self.call_later(self.set_loading, loading) + else: + self.set_loading(loading) ExpectType = TypeVar("ExpectType", bound="Widget") diff --git a/tests/test_loading.py b/tests/test_loading.py index 262feac102..c3125c663a 100644 --- a/tests/test_loading.py +++ b/tests/test_loading.py @@ -1,6 +1,6 @@ from textual.app import App, ComposeResult from textual.containers import VerticalScroll -from textual.widgets import Label +from textual.widgets import Label, Static class LoadingApp(App[None]): @@ -31,3 +31,25 @@ async def test_loading_disables_and_remove_scrollbars(): await pilot.pause() assert vs._check_disabled() + + +async def test_premature_loading(): + """Test a widget can set the `loading` attribute before mounting.""" + + # No assert, we're just expecting it to not crash + + class LoadingWidget(Static): + """A widget that shows a loading indicator.""" + + class LoadingApp(App[None]): + """Simple app with a single loading widget.""" + + def compose(self) -> ComposeResult: + """Compose the app with the loading widget.""" + widget = LoadingWidget("Loading content...") + widget.loading = True # Should not crash + yield widget + + app = LoadingApp() + async with app.run_test() as pilot: + await pilot.pause()