Skip to content

Fix pointer style crash during early theme changes - #6682

Open
deepakganesh78 wants to merge 1 commit into
Textualize:mainfrom
deepakganesh78:fix/issue6360-pointer-theme-crash
Open

Fix pointer style crash during early theme changes#6682
deepakganesh78 wants to merge 1 commit into
Textualize:mainfrom
deepakganesh78:fix/issue6360-pointer-theme-crash

Conversation

@deepakganesh78

Copy link
Copy Markdown

Fixes #6360

Reproduction

A minimal app that sets pointer: default in CSS and switches to textual-light in __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 the App node during early theme application, App.screen raises ScreenStackError because the screen stack is still empty; the pointer-style code only handled NoScreen.

Fix

Treat ScreenStackError like NoScreen for 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 passed
  • Verified regression: with the source fix reverted, tests\test_app.py::test_app_pointer_style_with_theme_before_screen_stack fails with ScreenStackError: No screens on stack.
  • python -m pytest tests --ignore=tests\snapshot_tests -q → 3013 passed, 1 skipped, 4 xfailed

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

@Specter842 Specter842 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 TianHengZhuang left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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. ✅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Use of pointer style can cause a startup crash if setting a theme

3 participants