Mounting of `Markdown` isn't reliably awaitable
Closed this issue · 3 comments
I imagine this would be an issue for any container-like widget that itself returns awaitables of some description, but I first encountered this with Markdown
and the attempt to populate a VerticalScroll
with them, awaiting a mount_all
of them into the VerticalScroll
, and then attempting to scroll_end
.
The effect of the problem can be seen with this code:
from textual.app import App, ComposeResult
from textual.containers import VerticalScroll
from textual.widgets import Markdown
MARKDOWN = """
## Have some code:
```lisp
(defconstant +byte+ '(unsigned-byte 8)
"Type of a byte array element.")
(defconstant +2bit-version+ 0
"The only valid version number of 2bit data.")
(defconstant +signature+ #x1a412743
"2bit file signature.")
(defconstant +bases+ #("T" "C" "A" "G")
"Vector of the bases.
Note that the positions of each base in the vector map to the 2bit decoding
for them.")
```
## Now a list:
- This
- Is
- A
- List
- Really
"""
class ScrollEndWithMarkdownApp(App[None]):
def compose(self) -> ComposeResult:
yield VerticalScroll()
async def on_mount(self) -> None:
await self.query_one(VerticalScroll).mount_all([
Markdown(f"# This is Markdown {n}{MARKDOWN}") for n in range(50)
])
self.query_one(VerticalScroll).scroll_end(animate=False)
if __name__ == "__main__":
ScrollEndWithMarkdownApp().run()
When run it should await
the mount_all
of a number of Markdown
into the VerticalScroll
and then perform a scroll_end
. The result is that it scrolls almost, but not quite, to the end:
Delaying the scroll_end
using call_next
, call_later
or call_after_refresh
makes no difference. So far the only approach I've found to make this happen is to use a set_timer
to delay the scroll_end
, and the time required seems to be proportional to the number of Markdown
within the VerticalScroll
(on my Mac Mini, M2Pro, under normal load, anything less than a 0.9 second delay results in it not scrolling to the end; in the reasonable use case in the application I ran into this in I'm having to use a 0.05 second delay).
textual diagnose output
Textual Diagnostics
Versions
Name | Value |
---|---|
Textual | 0.58.0 |
Rich | 13.7.1 |
Python
Name | Value |
---|---|
Version | 3.10.13 |
Implementation | CPython |
Compiler | Clang 15.0.0 (clang-1500.0.40.1) |
Executable | /Users/davep/develop/python/textual-sandbox/.venv/bin/python |
Operating System
Name | Value |
---|---|
System | Darwin |
Release | 23.4.0 |
Version | Darwin Kernel Version 23.4.0: Fri Mar 15 00:12:49 PDT 2024; root:xnu-10063.101.17~1/RELEASE_ARM64_T6020 |
Terminal
Name | Value |
---|---|
Terminal Application | Kitty |
TERM | xterm-256color |
COLORTERM | truecolor |
FORCE_COLOR | Not set |
NO_COLOR | Not set |
Rich Console options
Name | Value |
---|---|
size | width=103, height=50 |
legacy_windows | False |
min_width | 1 |
max_width | 103 |
is_terminal | False |
encoding | utf-8 |
max_height | 50 |
justify | None |
overflow | None |
no_wrap | False |
highlight | None |
markup | None |
height | None |
I might be wrong but is the problem that the content of Markdown
is updated after it is mounted? I seem to remember the update was awaited once upon a time, as the parsing seems an expensive operation.
textual/src/textual/widgets/_markdown.py
Lines 768 to 770 in da56de9
@TomJGooding is correct in where the issue lies. Changing that _on_mount
to async
and awaiting the update
fixes this.
Don't forget to star the repository!
Follow @textualizeio for Textual updates.