Textualize / trogon

Easily turn your Click CLI into a powerful terminal application
MIT License
2.4k stars 54 forks source link

Search crashes for `click.option` without a `help`: `AttributeError: 'NoneType' object has no attribute 'casefold'` #35

Closed samueldg closed 1 year ago

samueldg commented 1 year ago

Description

When you try to use the search feature in a command that has a click.option without a help text, you'll get an exception as soon as you type a letter in the search bar:

AttributeError: 'NoneType' object has no attribute 'casefold'

Full traceback:

╭───────────────────────────────────────────────────────────────────── Traceback (most recent call last) ──────────────────────────────────────────────────────────────────────╮
│ /Users/samuel/workspace/tmp/trogon/.venv/lib/python3.11/site-packages/trogon/widgets/form.py:222 in apply_filter                                                             │
│                                                                                                                                                                              │
│   219 │   │   all_controls = self.query(ParameterControls)                                                                                                                   │
│   220 │   │   for control in all_controls:                                                                                                                                   │
│   221 │   │   │   filter_query = filter_query.casefold()                                                                                                                     │
│ ❱ 222 │   │   │   control.apply_filter(filter_query)                                                                                                                         │
│   223                                                                                                                                                                        │
│                                                                                                                                                                              │
│ ╭──────────────────────────────────── locals ────────────────────────────────────╮                                                                                           │
│ │ all_controls = <DOMQuery query='ParameterControls'>                            │                                                                                           │
│ │      control = ParameterControls(id='id_1aad92d0', pseudo_classes={'enabled'}) │                                                                                           │
│ │        event = Changed()                                                       │                                                                                           │
│ │ filter_query = 'f'                                                             │                                                                                           │
│ │         self = CommandForm(pseudo_classes={'focus-within', 'enabled'})         │                                                                                           │
│ ╰────────────────────────────────────────────────────────────────────────────────╯                                                                                           │
│                                                                                                                                                                              │
│ /Users/samuel/workspace/tmp/trogon/.venv/lib/python3.11/site-packages/trogon/widgets/parameter_controls.py:88 in apply_filter                                                │
│                                                                                                                                                                              │
│    85 │   │   │   │   │   filter_query in name.casefold() for name in self.schema.name                                                                                       │
│    86 │   │   │   │   )                                                                                                                                                      │
│    87 │   │   │   │   help_contains_query = (                                                                                                                                │
│ ❱  88 │   │   │   │   │   filter_query in getattr(self.schema, "help", "").casefold()                                                                                        │
│    89 │   │   │   │   )                                                                                                                                                      │
│    90 │   │   │   │   should_be_visible = name_contains_query or help_contains_query                                                                                         │
│    91                                                                                                                                                                        │
│                                                                                                                                                                              │
│ ╭─────────────────────────────────────── locals ────────────────────────────────────────╮                                                                                    │
│ │        filter_query = 'f'                                                             │                                                                                    │
│ │           help_text = None                                                            │                                                                                    │
│ │                name = ['--force', '-f']                                               │                                                                                    │
│ │ name_contains_query = True                                                            │                                                                                    │
│ │                self = ParameterControls(id='id_1aad92d0', pseudo_classes={'enabled'}) │                                                                                    │
│ ╰───────────────────────────────────────────────────────────────────────────────────────╯                                                                                    │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

To reproduce

Here's a minimal reproducible example:

requirements.txt to install:

click==8.1.3
trogon==0.4.0

cli.py

import click
from trogon import tui

@tui()
@click.command()
@click.option("--force", "-f", is_flag=True)
def something(force):
    click.echo(f"{force=:}")

something()

Steps:

  1. Run python cli.py tui
  2. Hit <ctrl + s> or click in the search bar
  3. Type in any letter

Adding a help text solves the issue:

- @click.option("--force", "-f", is_flag=True)
+ @click.option("--force", "-f", is_flag=True, help="yay")

Demo

asciicast

darrenburns commented 1 year ago

Thanks for the report, this is fixed in main, so it should work again in the next release (likely this week). Feel free to re-open if you're still having issues after the release :)