Skip to content

Crash when closing notification - KeyError: "No 'toast--title' key in COMPONENT_CLASSES" #5646

@mzebrak

Description

@mzebrak

I think it is related to: #5629

Best to have some stress on your machine. I've got a basic Ryzen 5600x
and
stress --cpu 4 --timeout 600s --vm 4 --io 4 --vm-bytes 1024M

is enough for me.

To reproduce, you just need to spam "LMB" on the notification to close it, it will crash. If there is no notification title, it will crash with same error as in #5629

MRE:

from __future__ import annotations

from textual.app import App, ComposeResult
from textual.widgets import Static


class ExampleApp(App):
    def compose(self) -> ComposeResult:
        yield Static("Close notifications as fast as you can :)")

    def on_mount(self) -> None:
        self.set_interval(0.2, lambda: self.notify(title="Press me to close!", message="not me"))


if __name__ == "__main__":
    ExampleApp().run()

Stacktrace:

╭───────────────────────────────────────────────────────── Traceback (most recent call last) ─────────────────────────────────────────────────────────╮
│ /home/mzebrak/.pyenv/versions/clive-3.10.13/lib/python3.10/site-packages/textual/app.py:3748 in on_event                                            │
│                                                                                                                                                     │
│   3745 │   │   │   │   │   │   # Shouldn't occur, since at the very least this will find the Sc                                                     │3746 │   │   │   │   │   │   self._mouse_down_widget = None                                                                                       │
│   3747 │   │   │   │                                                                                                                                │
│ ❱ 3748 │   │   │   │   self.screen._forward_event(event)                                                                                            │
│   3749 │   │   │   │                                                                                                                                │
│   3750 │   │   │   │   # If a MouseUp occurs at the same widget as a MouseDown, then we should                                                      │3751 │   │   │   │   # consider it a click, and produce a Click event.                                                                            │
│                                                                                                                                                     │
│ ╭───────────────────────────────────────────── locals ─────────────────────────────────────────────╮                                                │
│ │     _ = Region(x=89, y=35, width=60, height=4)                                                   │                                                │
│ │ event = MouseDown(None, x=103, y=36, pointer_x=103.0, pointer_y=36.0, button=1)                  │                                                │
│ │  self = ExampleApp(title='ExampleApp', classes={'-dark-mode'}, pseudo_classes={'focus', 'dark'}) │                                                │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯                                                │
│                                                                                                                                                     │
│ /home/mzebrak/.pyenv/versions/clive-3.10.13/lib/python3.10/site-packages/textual/screen.py:1533 in _forward_event                                   │
│                                                                                                                                                     │
│   1530 │   │   │   elif isinstance(event, events.MouseDown) and not self.app.mouse_captured:                                                        │
│   1531 │   │   │   │   self._box_select = event.shift                                                                                               │
│   1532 │   │   │   │   self._mouse_down_offset = event.screen_offset                                                                                │
│ ❱ 1533 │   │   │   │   select_widget, select_offset = self.get_widget_and_offset_at(                                                                │
│   1534 │   │   │   │   │   event.screen_x, event.screen_y                                                                                           │
│   1535 │   │   │   │   )                                                                                                                            │
│   1536 │   │   │   │   if (                                                                                                                         │
│                                                                                                                                                     │
│ ╭──────────────────────────────────── locals ─────────────────────────────────────╮                                                                 │
│ │ event = MouseDown(None, x=103, y=36, pointer_x=103.0, pointer_y=36.0, button=1) │                                                                 │
│ │  self = Screen(id='_default')                                                   │                                                                 │
│ ╰─────────────────────────────────────────────────────────────────────────────────╯                                                                 │
│                                                                                                                                                     │
│ /home/mzebrak/.pyenv/versions/clive-3.10.13/lib/python3.10/site-packages/textual/screen.py:634 in get_widget_and_offset_at                          │
│                                                                                                                                                     │
│    631 │   │   Returns:                                                                         ╭─────────── locals ───────────╮                    │
│    632 │   │   │   Tuple of Widget and Offset, both of which may be None.                       │ self = Screen(id='_default') │                    │
│    633 │   │   """                                                                              │    x = 103                   │                    │
│ ❱  634 │   │   return self._compositor.get_widget_and_offset_at(x, y)                           │    y = 36                    │                    │
│    635 │                                                                                        ╰──────────────────────────────╯                    │
│    636 │   def find_widget(self, widget: Widget) -> MapGeometry:                                                                                    │
│    637 │   │   """Get the screen region of a Widget.                                                                                                │
│                                                                                                                                                     │
│ /home/mzebrak/.pyenv/versions/clive-3.10.13/lib/python3.10/site-packages/textual/_compositor.py:904 in get_widget_and_offset_at                     │
│                                                                                                                                                     │
│    901 │   │   y -= region.y + gutter_right                                                                                                         │
│    902 │   │                                                                                                                                        │
│    903 │   │   visible_screen_stack.set(widget.app._background_screens)                                                                             │
│ ❱  904 │   │   line = widget.render_line(y)                                                                                                         │
│    905 │   │                                                                                                                                        │
│    906 │   │   end = 0                                                                                                                              │
│    907 │   │   start = 0                                                                                                                            │
│                                                                                                                                                     │
│ ╭──────────────────────────────────────────────────────── locals ─────────────────────────────────────────────────────────╮                         │
│ │  gutter_left = 2                                                                                                        │                         │
│ │ gutter_right = 1                                                                                                        │                         │
│ │       region = Region(x=89, y=35, width=60, height=4)                                                                   │                         │
│ │         self = <Compositor size=Size(width=151, height=40) widgets={Static(), Toast(), Screen(id='_default'), Toast()}> │                         │
│ │       widget = Toast()                                                                                                  │                         │
│ │            x = -90                                                                                                      │                         │
│ │            y = -36                                                                                                      │                         │
│ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯                         │
│                                                                                                                                                     │
│ /home/mzebrak/.pyenv/versions/clive-3.10.13/lib/python3.10/site-packages/textual/widget.py:3902 in render_line                                      │
│                                                                                                                                                     │
│   3899 │   │   │   A rendered line.                                                             ╭──── locals ────╮                                  │
│   3900 │   │   """                                                                              │ self = Toast() │                                  │
│   3901 │   │   if self._dirty_regions:                                                          │    y = -36     │                                  │
│ ❱ 3902 │   │   │   self._render_content()                                                       ╰────────────────╯                                  │
│   3903 │   │   try:                                                                                                                                 │
│   3904 │   │   │   line = self._render_cache.lines[y]                                                                                               │
│   3905 │   │   except IndexError:                                                                                                                   │
│                                                                                                                                                     │
│ /home/mzebrak/.pyenv/versions/clive-3.10.13/lib/python3.10/site-packages/textual/widget.py:3887 in _render_content                                  │
│                                                                                                                                                     │
│   3884 │   def _render_content(self) -> None:                                                   ╭───── locals ─────╮                                │
│   3885 │   │   """Render all lines."""                                                          │ height = 0       │                                │
│   3886 │   │   width, height = self.size                                                        │   self = Toast() │                                │
│ ❱ 3887 │   │   visual = self._render()                                                          │  width = 0       │                                │
│   3888 │   │   strips = Visual.to_strips(self, visual, width, height, self.visual_style)        ╰──────────────────╯                                │
│   3889 │   │   self._render_cache = _RenderCache(self.size, strips)                                                                                 │
│   3890 │   │   self._dirty_regions.clear()                                                                                                          │
│                                                                                                                                                     │
│ /home/mzebrak/.pyenv/versions/clive-3.10.13/lib/python3.10/site-packages/textual/widget.py:4110 in _render                                          │
│                                                                                                                                                     │
│   4107 │   │   if cached_visual is not None:                                                    ╭───────────── locals ─────────────╮                │
│   4108 │   │   │   assert isinstance(cached_visual, Visual)                                     │     cache_key = '_render.visual' │                │
│   4109 │   │   │   return cached_visual                                                         │ cached_visual = None             │                │
│ ❱ 4110 │   │   visual = visualize(self, self.render(), markup=self._render_markup)              │          self = Toast()          │                │
│   4111 │   │   self._layout_cache[cache_key] = visual                                           ╰──────────────────────────────────╯                │
│   4112 │   │   return visual                                                                                                                        │
│   4113                                                                                                                                              │
│                                                                                                                                                     │
│ /home/mzebrak/.pyenv/versions/clive-3.10.13/lib/python3.10/site-packages/textual/widgets/_toast.py:115 in render                                    │
│                                                                                                                                                     │
│   112 │   │   """                                                                                                                                   │
│   113 │   │   notification = self._notification                                                                                                     │
│   114 │   │   if notification.title:                                                                                                                │
│ ❱ 115 │   │   │   header_style = self.get_component_rich_style("toast--title")                                                                      │
│   116 │   │   │   notification_text = Text.assemble(                                                                                                │
│   117 │   │   │   │   (notification.title, header_style),                                                                                           │
│   118 │   │   │   │   "\n",                                                                                                                         │
│                                                                                                                                                     │
│ ╭────────────────────────────── locals ───────────────────────────────╮                                                                             │
│ │ notification = Notification(                                        │                                                                             │
│ │                │   message='not me',                                │                                                                             │
│ │                │   title='Press me to close!',                      │                                                                             │
│ │                │   severity='information',                          │                                                                             │
│ │                │   raised_it=1741871626.2424397,                    │                                                                             │
│ │                │   identity='62d4cbfe-cc95-4e80-946c-a15af6778914', │                                                                             │
│ │                │   time_left=4.593467473983765,                     │                                                                             │
│ │                │   has_expired=False                                │                                                                             │
│ │                )                                                    │                                                                             │
│ │         self = Toast()                                              │                                                                             │
│ ╰─────────────────────────────────────────────────────────────────────╯                                                                             │
│                                                                                                                                                     │
│ /home/mzebrak/.pyenv/versions/clive-3.10.13/lib/python3.10/site-packages/textual/widget.py:1032 in get_component_rich_style                         │
│                                                                                                                                                     │
│   1029 │   │   """                                                                              ╭────────── locals ───────────╮                     │
│   1030 │   │                                                                                    │   names = ('toast--title',) │                     │
│   1031 │   │   if names not in self._rich_style_cache:                                          │ partial = False             │                     │
│ ❱ 1032 │   │   │   component_styles = self.get_component_styles(*names)                         │    self = Toast()           │                     │
│   1033 │   │   │   style = component_styles.rich_style                                          ╰─────────────────────────────╯                     │
│   1034 │   │   │   text_opacity = component_styles.text_opacity                                                                                     │
│   1035 │   │   │   if text_opacity < 1 and style.bgcolor is not None:                                                                               │
│                                                                                                                                                     │
│ /home/mzebrak/.pyenv/versions/clive-3.10.13/lib/python3.10/site-packages/textual/dom.py:570 in get_component_styles                                 │
│                                                                                                                                                     │
│    567 │   │                                                                                    ╭──────────── locals ────────────╮                  │
│    568 │   │   for name in names:                                                               │   name = 'toast--title'        │                  │
│    569 │   │   │   if name not in self._component_styles:                                       │  names = ('toast--title',)     │                  │
│ ❱  570 │   │   │   │   raise KeyError(f"No {name!r} key in COMPONENT_CLASSES")                  │   self = Toast()               │                  │
│    571 │   │   │   component_styles = self._component_styles[name]                              │ styles = RenderStyles(Toast()) │                  │
│    572 │   │   │   styles.node = component_styles.node                                          ╰────────────────────────────────╯                  │
│    573 │   │   │   styles.base.merge(component_styles.base)                                                                                         │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
KeyError: "No 'toast--title' key in COMPONENT_CLASSES"

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions