Textualize / textual

The lean application framework for Python. Build sophisticated user interfaces with a simple Python API. Run your apps in the terminal and a web browser.
https://textual.textualize.io/
MIT License
25.19k stars 772 forks source link

Clicking off the Select dropdown causes a crash in `update_focus` #4648

Closed arcivanov closed 3 months ago

arcivanov commented 3 months ago

This just appeared in 0.67.1 out of the blue when clicking off a Select.

<╭─────────────────────────────────────────────────────────────────────────────────────────────── Traceback (most recent call last) ───────────────────────────────────────────────────────────────────────────────────────────────╮
│ /home/arcivanov/.pyenv/versions/myapp/lib/python3.12/site-packages/textual/widgets/_select.py:537 in update_focus                                                                                                       │
│                                                                                                                                                                                                                                 │
│   534 │   │   async def update_focus() -> None:                                                ╭──────────── locals ─────────────╮                                                                                              │
│   535 │   │   │   """Update focus and reset overlay."""                                        │ self = Select(id='class_value') │                                                                                              │
│   536 │   │   │   self.focus()                                                                 ╰─────────────────────────────────╯                                                                                              │
│ ❱ 537 │   │   │   self.expanded = False                                                                                                                                                                                         │
│   538 │   │                                                                                                                                                                                                                     │
│   539 │   │   self.call_after_refresh(update_focus)  # Prevents a little flicker                                                                                                                                                │
│   540                                                                                                                                                                                                                           │
│                                                                                                                                                                                                                                 │
│ /home/arcivanov/.pyenv/versions/myapp/lib/python3.12/site-packages/textual/widgets/_select.py:496 in _watch_expanded                                                                                                    │
│                                                                                                                                                                                                                                 │
│   493 │                                                                                        ╭────────────── locals ───────────────╮                                                                                          │
│   494 │   def _watch_expanded(self, expanded: bool) -> None:                                   │ expanded = False                    │                                                                                          │
│   495 │   │   """Display or hide overlay."""                                                   │     self = Select(id='class_value') │                                                                                          │
│ ❱ 496 │   │   overlay = self.query_one(SelectOverlay)                                          ╰─────────────────────────────────────╯                                                                                          │
│   497 │   │   self.set_class(expanded, "-expanded")                                                                                                                                                                             │
│   498 │   │   if expanded:                                                                                                                                                                                                      │
│   499 │   │   │   overlay.focus()                                                                                                                                                                                               │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
NoMatches: No nodes match <DOMQuery query='SelectOverlay'> on Select(id='class_value')

Textual Diagnostics

Versions

Name Value
Textual 0.67.1
Rich 13.7.1

Python

Name Value
Version 3.12.3
Implementation CPython
Compiler GCC 13.2.1 20240316 (Red Hat 13.2.1-7)
Executable /home/arcivanov/.pyenv/versions/3.12.3/envs/boris-trading/bin/python

Operating System

Name Value
System Linux
Release 6.8.11-300.fc40.x86_64
Version #1 SMP PREEMPT_DYNAMIC Mon May 27 14:53:33 UTC 2024

Terminal

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

Rich Console options

Name Value
size width=252, height=70
legacy_windows False
min_width 1
max_width 252
is_terminal True
encoding utf-8
max_height 70
justify None
overflow None
no_wrap False
highlight None
markup None
height None
github-actions[bot] commented 3 months ago

Thank you for your issue. Give us a little time to review it.

PS. You might want to check the FAQ if you haven't done so already.

This is an automated reply, generated by FAQtory

arcivanov commented 3 months ago

Click on Select control, navigate with arrows to select an option, press Enter, crash.

willmcgugan commented 3 months ago

Could you provide a minimal app to reproduce this?

arcivanov commented 3 months ago

I have a hunch of why this is happening - the dialog goes through the recomposition via self.recompose() when the selection changes.

willmcgugan commented 3 months ago

That would make sense. If the widget is recomposed while it is being used, it would result in this error.

How are you doing the recompose? Are you calling it explicitly, or via a reactive?

arcivanov commented 3 months ago

I was able to workaround this issue by moving recomposing the Widget containing the Select into a worker from the on_select_changed . Unfortunately it's a large proprietary application and I can't seem to distill it down into a reproducible case.

arcivanov commented 3 months ago

That would make sense. If the widget is recomposed while it is being used, it would result in this error.

How are you doing the recompose? Are you calling it explicitly, or via a reactive?

Directly but now in the worker rather than in the on_select_changed.

arcivanov commented 3 months ago

Thing is, in the repro case I tried to craft it caused no issues, so my guess is the timings would be something that causes the problem.

arcivanov commented 3 months ago

I'm going to close this as this bug went away as I moved recompose into a worker.

github-actions[bot] commented 3 months ago

Don't forget to star the repository!

Follow @textualizeio for Textual updates.