diff --git a/docs/tutorial/commands/help.md b/docs/tutorial/commands/help.md index d5ec229d68..855a5f1458 100644 --- a/docs/tutorial/commands/help.md +++ b/docs/tutorial/commands/help.md @@ -446,6 +446,20 @@ $ python main.py --help You can see the custom panel for the commands for "`Utils and Configs`". +## Expand or Fit + +By default, the help panels all expand to match the width of your terminal window. + +For a CLI with few parameters, especially on wide terminal windows, you might prefer a more narrow layout. +You can do this by initializing your Typer with `rich_expand=False`, like this: + +```python +app = typer.Typer(rich_expand=False) +``` + +Your help panels will all fit to their contents, which also means they will probably have different widths. +It's a different look, and sometimes you might prefer it as an option. + ## Epilog If you need, you can also add an epilog section to the help of your commands: diff --git a/typer/core.py b/typer/core.py index bb5aa48dd0..d7500d4005 100644 --- a/typer/core.py +++ b/typer/core.py @@ -169,6 +169,7 @@ def _main( standalone_mode: bool = True, windows_expand_args: bool = True, rich_markup_mode: MarkupMode = DEFAULT_MARKUP_MODE, + rich_expand: bool = True, **extra: Any, ) -> Any: # Typer override, duplicated from click.main() to handle custom rich exceptions @@ -213,7 +214,7 @@ def _main( raise # Typer override if rich and rich_markup_mode is not None: - rich_utils.rich_format_error(e) + rich_utils.rich_format_error(e, expand=rich_expand) else: e.show() # Typer override end @@ -632,6 +633,7 @@ def __init__( # Rich settings rich_markup_mode: MarkupMode = DEFAULT_MARKUP_MODE, rich_help_panel: Union[str, None] = None, + rich_expand: bool = True, ) -> None: super().__init__( name=name, @@ -649,6 +651,7 @@ def __init__( ) self.rich_markup_mode: MarkupMode = rich_markup_mode self.rich_help_panel = rich_help_panel + self.rich_expand = rich_expand def format_options( self, ctx: click.Context, formatter: click.HelpFormatter @@ -682,6 +685,7 @@ def main( standalone_mode=standalone_mode, windows_expand_args=windows_expand_args, rich_markup_mode=self.rich_markup_mode, + rich_expand=self.rich_expand, **extra, ) @@ -692,6 +696,7 @@ def format_help(self, ctx: click.Context, formatter: click.HelpFormatter) -> Non obj=self, ctx=ctx, markup_mode=self.rich_markup_mode, + expand=self.rich_expand, ) @@ -706,11 +711,13 @@ def __init__( # Rich settings rich_markup_mode: MarkupMode = DEFAULT_MARKUP_MODE, rich_help_panel: Union[str, None] = None, + rich_expand: bool = True, **attrs: Any, ) -> None: super().__init__(name=name, commands=commands, **attrs) self.rich_markup_mode: MarkupMode = rich_markup_mode self.rich_help_panel = rich_help_panel + self.rich_expand = rich_expand def format_options( self, ctx: click.Context, formatter: click.HelpFormatter @@ -745,6 +752,7 @@ def main( standalone_mode=standalone_mode, windows_expand_args=windows_expand_args, rich_markup_mode=self.rich_markup_mode, + rich_expand=self.rich_expand, **extra, ) @@ -755,6 +763,7 @@ def format_help(self, ctx: click.Context, formatter: click.HelpFormatter) -> Non obj=self, ctx=ctx, markup_mode=self.rich_markup_mode, + expand=self.rich_expand, ) def list_commands(self, ctx: click.Context) -> List[str]: diff --git a/typer/main.py b/typer/main.py index 508d96617e..050e67bbaa 100644 --- a/typer/main.py +++ b/typer/main.py @@ -149,6 +149,7 @@ def __init__( add_completion: bool = True, # Rich settings rich_markup_mode: MarkupMode = Default(DEFAULT_MARKUP_MODE), + rich_expand: bool = True, rich_help_panel: Union[str, None] = Default(None), pretty_exceptions_enable: bool = True, pretty_exceptions_show_locals: bool = True, @@ -156,6 +157,7 @@ def __init__( ): self._add_completion = add_completion self.rich_markup_mode: MarkupMode = rich_markup_mode + self.rich_expand = rich_expand self.rich_help_panel = rich_help_panel self.pretty_exceptions_enable = pretty_exceptions_enable self.pretty_exceptions_show_locals = pretty_exceptions_show_locals @@ -345,6 +347,7 @@ def get_group(typer_instance: Typer) -> TyperGroup: TyperInfo(typer_instance), pretty_exceptions_short=typer_instance.pretty_exceptions_short, rich_markup_mode=typer_instance.rich_markup_mode, + rich_expand=typer_instance.rich_expand, ) return group @@ -377,6 +380,7 @@ def get_command(typer_instance: Typer) -> click.Command: single_command, pretty_exceptions_short=typer_instance.pretty_exceptions_short, rich_markup_mode=typer_instance.rich_markup_mode, + rich_expand=typer_instance.rich_expand, ) if typer_instance._add_completion: click_command.params.append(click_install_param) @@ -472,6 +476,7 @@ def get_group_from_info( *, pretty_exceptions_short: bool, rich_markup_mode: MarkupMode, + rich_expand: bool, ) -> TyperGroup: assert group_info.typer_instance, ( "A Typer instance is needed to generate a Click Group" @@ -482,6 +487,7 @@ def get_group_from_info( command_info=command_info, pretty_exceptions_short=pretty_exceptions_short, rich_markup_mode=rich_markup_mode, + rich_expand=rich_expand, ) if command.name: commands[command.name] = command @@ -490,6 +496,7 @@ def get_group_from_info( sub_group_info, pretty_exceptions_short=pretty_exceptions_short, rich_markup_mode=rich_markup_mode, + rich_expand=rich_expand, ) if sub_group.name: commands[sub_group.name] = sub_group @@ -536,6 +543,7 @@ def get_group_from_info( hidden=solved_info.hidden, deprecated=solved_info.deprecated, rich_markup_mode=rich_markup_mode, + rich_expand=rich_expand, # Rich settings rich_help_panel=solved_info.rich_help_panel, ) @@ -570,6 +578,7 @@ def get_command_from_info( *, pretty_exceptions_short: bool, rich_markup_mode: MarkupMode, + rich_expand: bool, ) -> click.Command: assert command_info.callback, "A command must have a callback function" name = command_info.name or get_command_name(command_info.callback.__name__) @@ -604,6 +613,7 @@ def get_command_from_info( hidden=command_info.hidden, deprecated=command_info.deprecated, rich_markup_mode=rich_markup_mode, + rich_expand=rich_expand, # Rich settings rich_help_panel=command_info.rich_help_panel, ) diff --git a/typer/rich_utils.py b/typer/rich_utils.py index 4a8ffbeeb0..839e97121e 100644 --- a/typer/rich_utils.py +++ b/typer/rich_utils.py @@ -11,7 +11,6 @@ import click from rich import box from rich.align import Align -from rich.columns import Columns from rich.console import Console, RenderableType, group from rich.emoji import Emoji from rich.highlighter import RegexHighlighter @@ -232,7 +231,7 @@ def _get_parameter_help( param: Union[click.Option, click.Argument, click.Parameter], ctx: click.Context, markup_mode: MarkupMode, -) -> Columns: +) -> Table: """Build primary help text for a click option or argument. Returns the prose help text for an option or argument, rendered either @@ -309,9 +308,11 @@ def _get_parameter_help( if param.required: items.append(Text(REQUIRED_LONG_STRING, style=STYLE_REQUIRED_LONG)) - # Use Columns - this allows us to group different renderable types + # Use Table - this allows us to group different renderable types # (Text, Markdown) onto a single line. - return Columns(items) + help_table = Table.grid(padding=(0, 1), expand=True) + help_table.add_row(*items) + return help_table def _make_command_help( @@ -347,6 +348,7 @@ def _print_options_panel( params: Union[List[click.Option], List[click.Argument]], ctx: click.Context, markup_mode: MarkupMode, + expand: bool, console: Console, ) -> None: options_rows: List[List[RenderableType]] = [] @@ -447,7 +449,7 @@ class MetavarHighlighter(RegexHighlighter): options_table = Table( highlight=True, show_header=False, - expand=True, + expand=False, box=box_style, **t_styles, ) @@ -458,6 +460,7 @@ class MetavarHighlighter(RegexHighlighter): options_table, border_style=STYLE_OPTIONS_PANEL_BORDER, title=name, + expand=expand, title_align=ALIGN_OPTIONS_PANEL, ) ) @@ -468,6 +471,7 @@ def _print_commands_panel( name: str, commands: List[click.Command], markup_mode: MarkupMode, + expand: bool, console: Console, cmd_len: int, ) -> None: @@ -534,6 +538,7 @@ def _print_commands_panel( commands_table, border_style=STYLE_COMMANDS_PANEL_BORDER, title=name, + expand=expand, title_align=ALIGN_COMMANDS_PANEL, ) ) @@ -544,6 +549,7 @@ def rich_format_help( obj: Union[click.Command, click.Group], ctx: click.Context, markup_mode: MarkupMode, + expand: bool, ) -> None: """Print nicely formatted help text using rich. @@ -597,6 +603,7 @@ def rich_format_help( params=default_arguments, ctx=ctx, markup_mode=markup_mode, + expand=expand, console=console, ) for panel_name, arguments in panel_to_arguments.items(): @@ -608,6 +615,7 @@ def rich_format_help( params=arguments, ctx=ctx, markup_mode=markup_mode, + expand=expand, console=console, ) default_options = panel_to_options.get(OPTIONS_PANEL_TITLE, []) @@ -616,6 +624,7 @@ def rich_format_help( params=default_options, ctx=ctx, markup_mode=markup_mode, + expand=expand, console=console, ) for panel_name, options in panel_to_options.items(): @@ -627,6 +636,7 @@ def rich_format_help( params=options, ctx=ctx, markup_mode=markup_mode, + expand=expand, console=console, ) @@ -657,6 +667,7 @@ def rich_format_help( name=COMMANDS_PANEL_TITLE, commands=default_commands, markup_mode=markup_mode, + expand=expand, console=console, cmd_len=max_cmd_len, ) @@ -668,6 +679,7 @@ def rich_format_help( name=panel_name, commands=commands, markup_mode=markup_mode, + expand=expand, console=console, cmd_len=max_cmd_len, ) @@ -681,7 +693,7 @@ def rich_format_help( console.print(Padding(Align(epilogue_text, pad=False), 1)) -def rich_format_error(self: click.ClickException) -> None: +def rich_format_error(self: click.ClickException, expand: bool = True) -> None: """Print richly formatted click errors. Called by custom exception handler to print richly formatted click errors. @@ -704,6 +716,7 @@ def rich_format_error(self: click.ClickException) -> None: Panel( highlighter(self.format_message()), border_style=STYLE_ERRORS_PANEL_BORDER, + expand=expand, title=ERRORS_PANEL_TITLE, title_align=ALIGN_ERRORS_PANEL, )