Fix pointer style crash during early theme changes - #6682
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Specter842
left a comment
There was a problem hiding this comment.
Fix looks right to me. obj.node.screen raises NoScreen for an unmounted widget and ScreenStackError for the App node when the stack is still empty (App.screen -> "No screens on stack"), so catching both here covers the early-theme-change case. There's already precedent for pairing them — app.py catches (ScreenStackError, NoScreen) together in a couple of places — so this reads as idiomatic.
The local from textual.app import ScreenStackError matches the existing local from textual.dom import NoScreen import right below it, so no circular-import concern introduced.
One small thing: the test asserts no crash, which is the bug, but it doesn't check that the pointer shape actually gets applied once a screen exists. A follow-up assertion (push a screen, then check screen._pointer_shape) would confirm the except is only deferring the update rather than dropping it. Non-blocking.
CHANGELOG entry is present. LGTM.
TianHengZhuang
left a comment
There was a problem hiding this comment.
Solid fix. Catching both NoScreen and ScreenStackError is the right approach since _refresh_parent can raise either depending on the exact state of the screen stack.
One note on the regression test: it asserts that the app starts without crashing (app.theme == "textual-light") but does not verify that the pointer shape itself is correct after the theme switch. That would be a stronger assertion, e.g. assert app.screen._pointer_shape == "default". Not blocking — the crash was the bug, and this test confirms it does not recur.
Good, focused fix. ✅
Fixes #6360
Reproduction
A minimal app that sets
pointer: defaultin CSS and switches totextual-lightin__init__crashes during startup because CSS is refreshed before any screen has been pushed.Root cause
Updating a pointer style calls
obj.node.screen.update_pointer_shape(). For theAppnode during early theme application,App.screenraisesScreenStackErrorbecause the screen stack is still empty; the pointer-style code only handledNoScreen.Fix
Treat
ScreenStackErrorlikeNoScreenfor pointer updates, deferring pointer-shape refresh until a screen exists. This preserves existing pointer updates once the app is mounted.Compatibility notes
This only suppresses an early pointer-shape refresh when no active screen exists. Existing validation and mounted pointer-style behavior are unchanged.
Validation
python -m pytest tests\test_app.py::test_app_pointer_style_with_theme_before_screen_stack tests\test_app.py::test_pointer_shape -q→ 2 passedtests\test_app.py::test_app_pointer_style_with_theme_before_screen_stackfails withScreenStackError: No screens on stack.python -m pytest tests --ignore=tests\snapshot_tests -q→ 3013 passed, 1 skipped, 4 xfailed