Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Crash on pushing screen when a toast is active #4607

Closed
darrenburns opened this issue Jun 5, 2024 · 7 comments · Fixed by #4615
Closed

Crash on pushing screen when a toast is active #4607

darrenburns opened this issue Jun 5, 2024 · 7 comments · Fixed by #4615

Comments

@darrenburns
Copy link
Member

darrenburns commented Jun 5, 2024

I haven't been able to isolate an MRE for this. It works if I clear the notifications before I push my modal screen.

MountError: Can't mount widget(s) before
ToastHolder(id='--textual-toast-8456776f-9c7c-49ab-a3b8-cdfcfdcc6d85') is mounted

More details below:

Details
╭─────────────────────── Traceback (most recent call last) ────────────────────────╮
│ /Users/darrenburns/Code/posting/.venv/lib/python3.11/site-packages/textual/widge │
│ t.py:3720 in _compose                                                            │
│                                                                                  │
│   3717 │   │   │   self.app._handle_exception(error)                             │
│   3718 │   │   else:                                                             │
│   3719 │   │   │   self._extend_compose(widgets)                                 │
│ ❱ 3720 │   │   │   await self.mount_composed_widgets(widgets)                    │
│   3721 │                                                                         │
│   3722 │   async def mount_composed_widgets(self, widgets: list[Widget]) -> None │
│   3723 │   │   """Called by Textual to mount widgets after compose.              │
│                                                                                  │
│ ╭─────────────────────────────────── locals ───────────────────────────────────╮ │
│ │    self = ToastHolder(                                                       │ │
│ │           │   id='--textual-toast-8456776f-9c7c-49ab-a3b8-cdfcfdcc6d85'      │ │
│ │           )                                                                  │ │
│ │ widgets = [Toast()]                                                          │ │
│ ╰──────────────────────────────────────────────────────────────────────────────╯ │
│                                                                                  │
│ /Users/darrenburns/Code/posting/.venv/lib/python3.11/site-packages/textual/widge │
│ t.py:3733 in mount_composed_widgets                                              │
│                                                                                  │
│   3730 │   │   │   widgets: A list of child widgets.                             │
│   3731 │   │   """                                                               │
│   3732 │   │   if widgets:                                                       │
│ ❱ 3733 │   │   │   await self.mount_all(widgets)                                 │
│   3734 │                                                                         │
│   3735 │   def _extend_compose(self, widgets: list[Widget]) -> None:             │
│   3736 │   │   """Hook to extend composed widgets.                               │
│                                                                                  │
│ ╭─────────────────────────────────── locals ───────────────────────────────────╮ │
│ │    self = ToastHolder(                                                       │ │
│ │           │   id='--textual-toast-8456776f-9c7c-49ab-a3b8-cdfcfdcc6d85'      │ │
│ │           )                                                                  │ │
│ │ widgets = [Toast()]                                                          │ │
│ ╰──────────────────────────────────────────────────────────────────────────────╯ │
│                                                                                  │
│ /Users/darrenburns/Code/posting/.venv/lib/python3.11/site-packages/textual/widge │
│ t.py:1007 in mount_all                                                           │
│                                                                                  │
│   1004 │   │   │   Only one of ``before`` or ``after`` can be provided. If both  │
│   1005 │   │   │   provided a ``MountError`` will be raised.                     │
│   1006 │   │   """                                                               │
│ ❱ 1007 │   │   await_mount = self.mount(*widgets, before=before, after=after)    │
│   1008 │   │   return await_mount                                                │
│   1009 │                                                                         │
│   1010 │   @overload                                                             │
│                                                                                  │
│ ╭─────────────────────────────────── locals ───────────────────────────────────╮ │
│ │   after = None                                                               │ │
│ │  before = None                                                               │ │
│ │    self = ToastHolder(                                                       │ │
│ │           │   id='--textual-toast-8456776f-9c7c-49ab-a3b8-cdfcfdcc6d85'      │ │
│ │           )                                                                  │ │
│ │ widgets = [Toast()]                                                          │ │
│ ╰──────────────────────────────────────────────────────────────────────────────╯ │
│                                                                                  │
│ /Users/darrenburns/Code/posting/.venv/lib/python3.11/site-packages/textual/widge │
│ t.py:940 in mount                                                                │
│                                                                                  │
│    937 │   │   if self._closing:                                                 │
│    938 │   │   │   return AwaitMount(self, [])                                   │
│    939 │   │   if not self.is_attached:                                          │
│ ❱  940 │   │   │   raise MountError(f"Can't mount widget(s) before {self!r} is m │
│    941 │   │   # Check for duplicate IDs in the incoming widgets                 │
│    942 │   │   ids_to_mount = [                                                  │
│    943 │   │   │   widget_id for widget in widgets if (widget_id := widget.id) i │
│                                                                                  │
│ ╭─────────────────────────────────── locals ───────────────────────────────────╮ │
│ │   after = None                                                               │ │
│ │  before = None                                                               │ │
│ │    self = ToastHolder(                                                       │ │
│ │           │   id='--textual-toast-8456776f-9c7c-49ab-a3b8-cdfcfdcc6d85'      │ │
│ │           )                                                                  │ │
│ │ widgets = (Toast(),)                                                         │ │
│ ╰──────────────────────────────────────────────────────────────────────────────╯ │
╰──────────────────────────────────────────────────────────────────────────────────╯
MountError: Can't mount widget(s) before
ToastHolder(id='--textual-toast-8456776f-9c7c-49ab-a3b8-cdfcfdcc6d85') is mounted
@darrenburns
Copy link
Member Author

I was calling self.recompose inside on_resize inside the Screen I was pushing. This fails if there's a notification on screen as the first resize event arrives too early. I just put a block in to prevent it running on the first resize event and it works as expect.

@willmcgugan
Copy link
Collaborator

I suspect that is down to the call to self.app._refresh_notifications() in on_screen_resume. If that was awaited, it could fix it.

Could you try for an MRE again? I suspect it will be a matter of just repeating things very fast.

@darrenburns
Copy link
Member Author

Press n and then p:

from textual.app import App, ComposeResult
from textual.binding import Binding
from textual.screen import ModalScreen
from textual.widgets import Footer, Label


class MyScreen(ModalScreen[None]):
    def compose(self) -> ComposeResult:
        yield Label("MyScreen")

    async def on_resize(self) -> None:
        await self.recompose()


class MountCrash(App[None]):
    BINDINGS = [
        Binding("p", "push", "Push a new screen"),
        Binding("n", "notification", "Notification"),
    ]

    def compose(self) -> ComposeResult:
        yield Label("App")
        yield Footer()

    def action_notification(self) -> None:
        self.notify("Hello, world!", title="Notification message", timeout=10)

    async def action_push(self) -> None:
        await self.push_screen(MyScreen(), callback=lambda _: None)


app = MountCrash()
if __name__ == "__main__":
    app.run()

@mzebrak
Copy link

mzebrak commented Jun 6, 2024

I think that's related to: #4570 (see the last MRE of my last response - it also happens on screen change, but pop_screen in my case)

Copy link

github-actions bot commented Jun 6, 2024

Don't forget to star the repository!

Follow @textualizeio for Textual updates.

@mzebrak
Copy link

mzebrak commented Jun 7, 2024

Unfortunately, the latest change in locks seems does not fix any of my issues mentioned in the linked (closed) issue #4570.

Could I ask you @darrenburns to take a look at this, please? Maybe you'll notice something right away, and I have some problems shown there (in MREs), like this mounting error and even the application freeze.

@willmcgugan
Copy link
Collaborator

Please do not comment on closed issues. We need an open issue to track tasks.

There's no need to mention any particular dev.

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 a pull request may close this issue.

3 participants