Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

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

"Can't mount widget(s) before Vertical() is mounted" when reactive recompose #4691

Closed
comzyh opened this issue Jun 30, 2024 · 1 comment
Closed

Comments

@comzyh
Copy link

comzyh commented Jun 30, 2024

Similar to #4570.

Reproduce: using the following code and press the "t" key repeatedly.

import asyncio

from textual.app import App, ComposeResult
from textual.containers import Vertical
from textual.reactive import reactive
from textual.widget import Widget
from textual.widgets import Footer, Header, Label


class FooWidget(Widget):

    num_lines = reactive(0, recompose=True)

    def compose(self) -> ComposeResult:
        with Vertical():
            for i in range(self.num_lines):
                yield Label(f"Test line {i}")

    async def add_widgets(self) -> None:
        for i in range(32):
            self.num_lines += 1
            # v = self.query_one(Vertical)
            # v.mount(Label(f"Test line {i}"))
            await asyncio.sleep(0.01)


class TestApp(App):
    BINDINGS = [
        ("t", "trigger", "Test"),
        ("q", "quit", "Quit"),
    ]

    def compose(self) -> ComposeResult:
        yield Header()

        with Vertical(id="vertical_01"):
            yield FooWidget()
        yield Footer()

    async def action_trigger(self) -> None:

        v = self.query_one("#vertical_01", Vertical)
        f = v.query_one(FooWidget)
        f.workers.cancel_all()
        await f.remove()
        new_f = FooWidget(id="new_f")
        await_mount = v.mount(new_f)
        await await_mount
        new_f.run_worker(new_f.add_widgets())


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

exception:

Details
```
$ textual run  --dev mount_bug_reproduce.py
╭────────────────────────────────────────────────── Traceback (most recent call last) ───────────────────────────────────────────────────╮
│ /home/comzyh/.virtualenvs/textual/lib/python3.11/site-packages/textual/widget.py:3735 in _compose                                      │
│                                                                                                                                        │
│   3732 │   │   │   self.app._handle_exception(error)                                                                                   │
│   3733 │   │   else:                                                                                                                   │
│   3734 │   │   │   self._extend_compose(widgets)                                                                                       │
│ ❱ 3735 │   │   │   await self.mount_composed_widgets(widgets)                                                                          │
│   3736 │                                                                                                                               │
│   3737 │   async def mount_composed_widgets(self, widgets: list[Widget]) -> None:                                                      │
│   3738 │   │   """Called by Textual to mount widgets after compose.                                                                    │
│                                                                                                                                        │
│ ╭────────────────────────────────────── locals ──────────────────────────────────────╮                                                 │
│ │    self = Vertical()                                                               │                                                 │
│ │ widgets = [Label(), Label(), Label(), Label(), Label(), Label(), Label(), Label()] │                                                 │
│ ╰────────────────────────────────────────────────────────────────────────────────────╯                                                 │
│                                                                                                                                        │
│ /home/comzyh/.virtualenvs/textual/lib/python3.11/site-packages/textual/widget.py:3748 in mount_composed_widgets                        │
│                                                                                                                                        │
│   3745 │   │   │   widgets: A list of child widgets.                                                                                   │
│   3746 │   │   """                                                                                                                     │
│   3747 │   │   if widgets:                                                                                                             │
│ ❱ 3748 │   │   │   await self.mount_all(widgets)                                                                                       │
│   3749 │                                                                                                                               │
│   3750 │   def _extend_compose(self, widgets: list[Widget]) -> None:                                                                   │
│   3751 │   │   """Hook to extend composed widgets.                                                                                     │
│                                                                                                                                        │
│ ╭────────────────────────────────────── locals ──────────────────────────────────────╮                                                 │
│ │    self = Vertical()                                                               │                                                 │
│ │ widgets = [Label(), Label(), Label(), Label(), Label(), Label(), Label(), Label()] │                                                 │
│ ╰────────────────────────────────────────────────────────────────────────────────────╯                                                 │
│                                                                                                                                        │
│ /home/comzyh/.virtualenvs/textual/lib/python3.11/site-packages/textual/widget.py:1016 in mount_all                                     │
│                                                                                                                                        │
│   1013 │   │   """                                                                                                                     │
│   1014 │   │   if self.app._exit:                                                                                                      │
│   1015 │   │   │   return AwaitMount(self, [])                                                                                         │
│ ❱ 1016 │   │   await_mount = self.mount(*widgets, before=before, after=after)                                                          │
│   1017 │   │   return await_mount                                                                                                      │
│   1018 │                                                                                                                               │
│   1019 │   if TYPE_CHECKING:                                                                                                           │
│                                                                                                                                        │
│ ╭────────────────────────────────────── locals ──────────────────────────────────────╮                                                 │
│ │   after = None                                                                     │                                                 │
│ │  before = None                                                                     │                                                 │
│ │    self = Vertical()                                                               │                                                 │
│ │ widgets = [Label(), Label(), Label(), Label(), Label(), Label(), Label(), Label()] │                                                 │
│ ╰────────────────────────────────────────────────────────────────────────────────────╯                                                 │
│                                                                                                                                        │
│ /home/comzyh/.virtualenvs/textual/lib/python3.11/site-packages/textual/widget.py:947 in mount                                          │
│                                                                                                                                        │
│    944 │   │   if self._closing:                                                                                                       │
│    945 │   │   │   return AwaitMount(self, [])                                                                                         │
│    946 │   │   if not self.is_attached:                                                                                                │
│ ❱  947 │   │   │   raise MountError(f"Can't mount widget(s) before {self!r} is mounted")                                               │
│    948 │   │   # Check for duplicate IDs in the incoming widgets                                                                       │
│    949 │   │   ids_to_mount = [                                                                                                        │
│    950 │   │   │   widget_id for widget in widgets if (widget_id := widget.id) is not None                                             │
│                                                                                                                                        │
│ ╭────────────────────────────────────── locals ──────────────────────────────────────╮                                                 │
│ │   after = None                                                                     │                                                 │
│ │  before = None                                                                     │                                                 │
│ │    self = Vertical()                                                               │                                                 │
│ │ widgets = (Label(), Label(), Label(), Label(), Label(), Label(), Label(), Label()) │                                                 │
│ ╰────────────────────────────────────────────────────────────────────────────────────╯                                                 │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
MountError: Can't mount widget(s) before Vertical() is mounted

In my environment, it only reproduces if I use reactive recompose.

If I remove the reactive add and uncomment 2 lines in add_widgets,

    async def add_widgets(self) -> None:
        for i in range(32):
            v = self.query_one(Vertical)
            v.mount(Label(f"Test line {i}"))
            await asyncio.sleep(0.01)

it won't happen.

Textual diagnose

$ textual diagnose

Textual Diagnostics

Versions

Name Value
Textual 0.71.0
Rich 13.6.0

Python

Name Value
Version 3.11.0rc1
Implementation CPython
Compiler GCC 11.2.0
Executable /home/comzyh/.virtualenvs/textual/bin/python3.11

Operating System

Name Value
System Linux
Release 5.15.133.1-microsoft-standard-WSL2
Version #1 SMP Thu Oct 5 21:02:42 UTC 2023

Terminal

Name Value
Terminal Application Windows Terminal
TERM xterm-256color
COLORTERM Not set
FORCE_COLOR Not set
NO_COLOR Not set

Rich Console options

Name Value
size width=138, height=78
legacy_windows False
min_width 1
max_width 138
is_terminal True
encoding utf-8
max_height 78
justify None
overflow None
no_wrap False
highlight None
markup None
height None
Copy link

We found the following entries in the FAQ which you may find helpful:

Feel free to close this issue if you found an answer in the FAQ. Otherwise, please give us a little time to review.

This is an automated reply, generated by FAQtory

@Textualize Textualize locked and limited conversation to collaborators Jun 30, 2024
@willmcgugan willmcgugan converted this issue into discussion #4692 Jun 30, 2024

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant