From 172a4894695e4e2ca191900d469aa436f3882bbe Mon Sep 17 00:00:00 2001 From: svlandeg Date: Wed, 11 Dec 2024 16:24:45 +0100 Subject: [PATCH 1/9] extend test to cover issue reported in #1058 --- tests/test_rich_markup_mode.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_rich_markup_mode.py b/tests/test_rich_markup_mode.py index d9fe5bae4b..e8d1925a96 100644 --- a/tests/test_rich_markup_mode.py +++ b/tests/test_rich_markup_mode.py @@ -20,6 +20,7 @@ def main(arg: str): assert "Hello World" in result.stdout result = runner.invoke(app, ["--help"]) + assert "ARG [required]" in result.stdout assert all(c not in result.stdout for c in rounded) From 91641165db1272c051a1257637734b21c3492eab Mon Sep 17 00:00:00 2001 From: svlandeg Date: Thu, 12 Dec 2024 12:03:24 +0100 Subject: [PATCH 2/9] set rich_markup_mode as a setting on ctx.obj to only perform escaping when necessary --- typer/cli.py | 5 +++++ typer/core.py | 15 +++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/typer/cli.py b/typer/cli.py index 3fe3d3ee7f..aa6bc100d9 100644 --- a/typer/cli.py +++ b/typer/cli.py @@ -301,6 +301,11 @@ def docs( if not typer_obj: typer.echo("No Typer app found", err=True) raise typer.Abort() + if hasattr(typer_obj, "rich_markup_mode"): + if not hasattr(ctx, "obj") or ctx.obj is None: + ctx.ensure_object(dict) + if isinstance(ctx.obj, dict): + ctx.obj['TYPER_RICH_MARKUP_MODE'] = typer_obj.rich_markup_mode click_obj = typer.main.get_command(typer_obj) docs = get_docs_for_click(obj=click_obj, ctx=ctx, name=name, title=title) clean_docs = f"{docs.strip()}\n" diff --git a/typer/core.py b/typer/core.py index 4dc24ada70..227e882940 100644 --- a/typer/core.py +++ b/typer/core.py @@ -371,7 +371,10 @@ def get_help_record(self, ctx: click.Context) -> Optional[Tuple[str, str]]: if extra: extra_str = "; ".join(extra) extra_str = f"[{extra_str}]" - if rich is not None: + rich_markup_mode = None + if hasattr(ctx, "obj") and isinstance(ctx.obj, dict): + rich_markup_mode = ctx.obj.get('TYPER_RICH_MARKUP_MODE', None) + if rich is not None and rich_markup_mode == "rich": # This is needed for when we want to export to HTML extra_str = rich.markup.escape(extra_str).strip() @@ -565,7 +568,11 @@ def _write_opts(opts: Sequence[str]) -> str: if extra: extra_str = "; ".join(extra) extra_str = f"[{extra_str}]" - if rich is not None: + + rich_markup_mode = None + if hasattr(ctx, "obj") and isinstance(ctx.obj, dict): + rich_markup_mode = ctx.obj.get('TYPER_RICH_MARKUP_MODE', None) + if rich is not None and rich_markup_mode == "rich": # This is needed for when we want to export to HTML extra_str = rich.markup.escape(extra_str).strip() @@ -690,6 +697,10 @@ def main( def format_help(self, ctx: click.Context, formatter: click.HelpFormatter) -> None: if not rich or self.rich_markup_mode is None: + if not hasattr(ctx, "obj") or ctx.obj is None: + ctx.ensure_object(dict) + if isinstance(ctx.obj, dict): + ctx.obj['TYPER_RICH_MARKUP_MODE'] = self.rich_markup_mode return super().format_help(ctx, formatter) return rich_utils.rich_format_help( obj=self, From c6a7347e73456265ab51abd776d992c3a7e12524 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 12 Dec 2024 11:28:37 +0000 Subject: [PATCH 3/9] =?UTF-8?q?=F0=9F=8E=A8=20[pre-commit.ci]=20Auto=20for?= =?UTF-8?q?mat=20from=20pre-commit.com=20hooks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- typer/cli.py | 2 +- typer/core.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/typer/cli.py b/typer/cli.py index aa6bc100d9..087efef625 100644 --- a/typer/cli.py +++ b/typer/cli.py @@ -305,7 +305,7 @@ def docs( if not hasattr(ctx, "obj") or ctx.obj is None: ctx.ensure_object(dict) if isinstance(ctx.obj, dict): - ctx.obj['TYPER_RICH_MARKUP_MODE'] = typer_obj.rich_markup_mode + ctx.obj["TYPER_RICH_MARKUP_MODE"] = typer_obj.rich_markup_mode click_obj = typer.main.get_command(typer_obj) docs = get_docs_for_click(obj=click_obj, ctx=ctx, name=name, title=title) clean_docs = f"{docs.strip()}\n" diff --git a/typer/core.py b/typer/core.py index 227e882940..39b55d2684 100644 --- a/typer/core.py +++ b/typer/core.py @@ -373,7 +373,7 @@ def get_help_record(self, ctx: click.Context) -> Optional[Tuple[str, str]]: extra_str = f"[{extra_str}]" rich_markup_mode = None if hasattr(ctx, "obj") and isinstance(ctx.obj, dict): - rich_markup_mode = ctx.obj.get('TYPER_RICH_MARKUP_MODE', None) + rich_markup_mode = ctx.obj.get("TYPER_RICH_MARKUP_MODE", None) if rich is not None and rich_markup_mode == "rich": # This is needed for when we want to export to HTML extra_str = rich.markup.escape(extra_str).strip() @@ -571,7 +571,7 @@ def _write_opts(opts: Sequence[str]) -> str: rich_markup_mode = None if hasattr(ctx, "obj") and isinstance(ctx.obj, dict): - rich_markup_mode = ctx.obj.get('TYPER_RICH_MARKUP_MODE', None) + rich_markup_mode = ctx.obj.get("TYPER_RICH_MARKUP_MODE", None) if rich is not None and rich_markup_mode == "rich": # This is needed for when we want to export to HTML extra_str = rich.markup.escape(extra_str).strip() @@ -700,7 +700,7 @@ def format_help(self, ctx: click.Context, formatter: click.HelpFormatter) -> Non if not hasattr(ctx, "obj") or ctx.obj is None: ctx.ensure_object(dict) if isinstance(ctx.obj, dict): - ctx.obj['TYPER_RICH_MARKUP_MODE'] = self.rich_markup_mode + ctx.obj["TYPER_RICH_MARKUP_MODE"] = self.rich_markup_mode return super().format_help(ctx, formatter) return rich_utils.rich_format_help( obj=self, From 542d6bdd74f27de85037572a06068a9f1148fb9a Mon Sep 17 00:00:00 2001 From: svlandeg Date: Thu, 12 Dec 2024 14:22:22 +0100 Subject: [PATCH 4/9] deal with DefaultPlaceholder value --- typer/cli.py | 6 +++++- typer/core.py | 7 ++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/typer/cli.py b/typer/cli.py index 087efef625..2f12af059b 100644 --- a/typer/cli.py +++ b/typer/cli.py @@ -10,6 +10,7 @@ from click import Command, Group, Option from . import __version__ +from .models import DefaultPlaceholder try: import rich @@ -305,7 +306,10 @@ def docs( if not hasattr(ctx, "obj") or ctx.obj is None: ctx.ensure_object(dict) if isinstance(ctx.obj, dict): - ctx.obj["TYPER_RICH_MARKUP_MODE"] = typer_obj.rich_markup_mode + if isinstance(typer_obj.rich_markup_mode, DefaultPlaceholder): + ctx.obj["TYPER_RICH_MARKUP_MODE"] = typer_obj.rich_markup_mode.value + else: + ctx.obj["TYPER_RICH_MARKUP_MODE"] = typer_obj.rich_markup_mode click_obj = typer.main.get_command(typer_obj) docs = get_docs_for_click(obj=click_obj, ctx=ctx, name=name, title=title) clean_docs = f"{docs.strip()}\n" diff --git a/typer/core.py b/typer/core.py index 39b55d2684..22c610e319 100644 --- a/typer/core.py +++ b/typer/core.py @@ -26,6 +26,8 @@ import click.types import click.utils +from .models import DefaultPlaceholder + if sys.version_info >= (3, 8): from typing import Literal else: @@ -700,7 +702,10 @@ def format_help(self, ctx: click.Context, formatter: click.HelpFormatter) -> Non if not hasattr(ctx, "obj") or ctx.obj is None: ctx.ensure_object(dict) if isinstance(ctx.obj, dict): - ctx.obj["TYPER_RICH_MARKUP_MODE"] = self.rich_markup_mode + if isinstance(self.rich_markup_mode, DefaultPlaceholder): + ctx.obj["TYPER_RICH_MARKUP_MODE"] = self.rich_markup_mode.value + else: + ctx.obj["TYPER_RICH_MARKUP_MODE"] = self.rich_markup_mode return super().format_help(ctx, formatter) return rich_utils.rich_format_help( obj=self, From 72eb74bf4789b7405fa2ee2e2e8476cccffc5754 Mon Sep 17 00:00:00 2001 From: svlandeg Date: Thu, 12 Dec 2024 14:43:45 +0100 Subject: [PATCH 5/9] add another failing test --- tests/assets/cli/multi_app_norich.py | 40 +++++++++ tests/assets/cli/multiapp-docs-norich.md | 102 +++++++++++++++++++++++ tests/test_cli/test_doc.py | 24 ++++++ 3 files changed, 166 insertions(+) create mode 100644 tests/assets/cli/multi_app_norich.py create mode 100644 tests/assets/cli/multiapp-docs-norich.md diff --git a/tests/assets/cli/multi_app_norich.py b/tests/assets/cli/multi_app_norich.py new file mode 100644 index 0000000000..d61a963b69 --- /dev/null +++ b/tests/assets/cli/multi_app_norich.py @@ -0,0 +1,40 @@ +import typer + +sub_app = typer.Typer() + +variable = "Some text" + + +@sub_app.command() +def hello(name: str = "World", age: int = typer.Option(0, help="The age of the user")): + """ + Say Hello + """ + typer.echo(f"Hello {name}") + + +@sub_app.command() +def hi(user: str = typer.Argument("World", help="The name of the user to greet")): + """ + Say Hi + """ + + +@sub_app.command() +def bye(): + """ + Say bye + """ + typer.echo("sub bye") + + +app = typer.Typer(help="Demo App", epilog="The end", rich_markup_mode=None) +app.add_typer(sub_app, name="sub") + + +@app.command() +def top(): + """ + Top command + """ + typer.echo("top") diff --git a/tests/assets/cli/multiapp-docs-norich.md b/tests/assets/cli/multiapp-docs-norich.md new file mode 100644 index 0000000000..67d02568db --- /dev/null +++ b/tests/assets/cli/multiapp-docs-norich.md @@ -0,0 +1,102 @@ +# `multiapp` + +Demo App + +**Usage**: + +```console +$ multiapp [OPTIONS] COMMAND [ARGS]... +``` + +**Options**: + +* `--install-completion`: Install completion for the current shell. +* `--show-completion`: Show completion for the current shell, to copy it or customize the installation. +* `--help`: Show this message and exit. + +The end + +**Commands**: + +* `top`: Top command +* `sub` + +## `multiapp top` + +Top command + +**Usage**: + +```console +$ multiapp top [OPTIONS] +``` + +**Options**: + +* `--help`: Show this message and exit. + +## `multiapp sub` + +**Usage**: + +```console +$ multiapp sub [OPTIONS] COMMAND [ARGS]... +``` + +**Options**: + +* `--help`: Show this message and exit. + +**Commands**: + +* `hello`: Say Hello +* `hi`: Say Hi +* `bye`: Say bye + +### `multiapp sub hello` + +Say Hello + +**Usage**: + +```console +$ multiapp sub hello [OPTIONS] +``` + +**Options**: + +* `--name TEXT`: [default: World] +* `--age INTEGER`: The age of the user [default: 0] +* `--help`: Show this message and exit. + +### `multiapp sub hi` + +Say Hi + +**Usage**: + +```console +$ multiapp sub hi [OPTIONS] [USER] +``` + +**Arguments**: + +* `[USER]`: The name of the user to greet [default: World] + +**Options**: + +* `--help`: Show this message and exit. + +### `multiapp sub bye` + +Say bye + +**Usage**: + +```console +$ multiapp sub bye [OPTIONS] +``` + +**Options**: + +* `--help`: Show this message and exit. diff --git a/tests/test_cli/test_doc.py b/tests/test_cli/test_doc.py index f0d1d9b850..24b0a2da8a 100644 --- a/tests/test_cli/test_doc.py +++ b/tests/test_cli/test_doc.py @@ -28,6 +28,30 @@ def test_doc(): assert "**Arguments**" in result.stdout +def test_doc_no_rich(): + result = subprocess.run( + [ + sys.executable, + "-m", + "coverage", + "run", + "-m", + "typer", + "tests.assets.cli.multi_app_norich", + "utils", + "docs", + "--name", + "multiapp", + ], + capture_output=True, + encoding="utf-8", + ) + docs_path: Path = Path(__file__).parent.parent / "assets/cli/multiapp-docs.md" + docs = docs_path.read_text() + assert docs in result.stdout + assert "**Arguments**" in result.stdout + + def test_doc_output(tmp_path: Path): out_file: Path = tmp_path / "out.md" result = subprocess.run( From c02f0fd55c773cebd3416a27797a9c37e4185061 Mon Sep 17 00:00:00 2001 From: svlandeg Date: Thu, 12 Dec 2024 14:51:31 +0100 Subject: [PATCH 6/9] Ensure that rich_to_html is only called when escaping has happened correctly --- typer/cli.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/typer/cli.py b/typer/cli.py index 2f12af059b..c5a047019b 100644 --- a/typer/cli.py +++ b/typer/cli.py @@ -203,6 +203,9 @@ def get_docs_for_click( title: Optional[str] = None, ) -> str: docs = "#" * (1 + indent) + rich_markup_mode = None + if hasattr(ctx, "obj") and isinstance(ctx.obj, dict): + rich_markup_mode = ctx.obj.get("TYPER_RICH_MARKUP_MODE", None) command_name = name or obj.name if call_prefix: command_name = f"{call_prefix} {command_name}" @@ -210,7 +213,7 @@ def get_docs_for_click( title = f"`{command_name}`" if command_name else "CLI" docs += f" {title}\n\n" if obj.help: - docs += f"{_parse_html(obj.help)}\n\n" + docs += f"{_parse_html(obj.help, rich_markup_mode)}\n\n" usage_pieces = obj.collect_usage_pieces(ctx) if usage_pieces: docs += "**Usage**:\n\n" @@ -234,7 +237,7 @@ def get_docs_for_click( for arg_name, arg_help in args: docs += f"* `{arg_name}`" if arg_help: - docs += f": {_parse_html(arg_help)}" + docs += f": {_parse_html(arg_help, rich_markup_mode)}" docs += "\n" docs += "\n" if opts: @@ -242,7 +245,7 @@ def get_docs_for_click( for opt_name, opt_help in opts: docs += f"* `{opt_name}`" if opt_help: - docs += f": {_parse_html(opt_help)}" + docs += f": {_parse_html(opt_help, rich_markup_mode)}" docs += "\n" docs += "\n" if obj.epilog: @@ -258,7 +261,7 @@ def get_docs_for_click( docs += f"* `{command_obj.name}`" command_help = command_obj.get_short_help_str() if command_help: - docs += f": {_parse_html(command_help)}" + docs += f": {_parse_html(command_help, rich_markup_mode)}" docs += "\n" docs += "\n" for command in commands: @@ -273,10 +276,10 @@ def get_docs_for_click( return docs -def _parse_html(input_text: str) -> str: - if not has_rich: # pragma: no cover - return input_text - return rich_utils.rich_to_html(input_text) +def _parse_html(input_text: str, rich_markup_mode: None | str) -> str: + if has_rich and rich_markup_mode and rich_markup_mode == "rich": # pragma: no cover + return rich_utils.rich_to_html(input_text) + return input_text @utils_app.command() From dd7edfd8e24cd599167c57d8f16210ef581ba85a Mon Sep 17 00:00:00 2001 From: svlandeg Date: Thu, 12 Dec 2024 14:58:51 +0100 Subject: [PATCH 7/9] Fix type annotation --- typer/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typer/cli.py b/typer/cli.py index c5a047019b..2d8507c030 100644 --- a/typer/cli.py +++ b/typer/cli.py @@ -276,7 +276,7 @@ def get_docs_for_click( return docs -def _parse_html(input_text: str, rich_markup_mode: None | str) -> str: +def _parse_html(input_text: str, rich_markup_mode: Optional[str]) -> str: if has_rich and rich_markup_mode and rich_markup_mode == "rich": # pragma: no cover return rich_utils.rich_to_html(input_text) return input_text From c687a85655ef9d159484b8cf7efbd480464da861 Mon Sep 17 00:00:00 2001 From: svlandeg Date: Thu, 12 Dec 2024 15:49:44 +0100 Subject: [PATCH 8/9] cleanup --- tests/assets/cli/multi_app_norich.py | 3 - tests/assets/cli/multiapp-docs-norich.md | 102 ----------------------- tests/test_cli/test_doc.py | 34 ++++---- 3 files changed, 17 insertions(+), 122 deletions(-) delete mode 100644 tests/assets/cli/multiapp-docs-norich.md diff --git a/tests/assets/cli/multi_app_norich.py b/tests/assets/cli/multi_app_norich.py index d61a963b69..32917f666d 100644 --- a/tests/assets/cli/multi_app_norich.py +++ b/tests/assets/cli/multi_app_norich.py @@ -10,7 +10,6 @@ def hello(name: str = "World", age: int = typer.Option(0, help="The age of the u """ Say Hello """ - typer.echo(f"Hello {name}") @sub_app.command() @@ -25,7 +24,6 @@ def bye(): """ Say bye """ - typer.echo("sub bye") app = typer.Typer(help="Demo App", epilog="The end", rich_markup_mode=None) @@ -37,4 +35,3 @@ def top(): """ Top command """ - typer.echo("top") diff --git a/tests/assets/cli/multiapp-docs-norich.md b/tests/assets/cli/multiapp-docs-norich.md deleted file mode 100644 index 67d02568db..0000000000 --- a/tests/assets/cli/multiapp-docs-norich.md +++ /dev/null @@ -1,102 +0,0 @@ -# `multiapp` - -Demo App - -**Usage**: - -```console -$ multiapp [OPTIONS] COMMAND [ARGS]... -``` - -**Options**: - -* `--install-completion`: Install completion for the current shell. -* `--show-completion`: Show completion for the current shell, to copy it or customize the installation. -* `--help`: Show this message and exit. - -The end - -**Commands**: - -* `top`: Top command -* `sub` - -## `multiapp top` - -Top command - -**Usage**: - -```console -$ multiapp top [OPTIONS] -``` - -**Options**: - -* `--help`: Show this message and exit. - -## `multiapp sub` - -**Usage**: - -```console -$ multiapp sub [OPTIONS] COMMAND [ARGS]... -``` - -**Options**: - -* `--help`: Show this message and exit. - -**Commands**: - -* `hello`: Say Hello -* `hi`: Say Hi -* `bye`: Say bye - -### `multiapp sub hello` - -Say Hello - -**Usage**: - -```console -$ multiapp sub hello [OPTIONS] -``` - -**Options**: - -* `--name TEXT`: [default: World] -* `--age INTEGER`: The age of the user [default: 0] -* `--help`: Show this message and exit. - -### `multiapp sub hi` - -Say Hi - -**Usage**: - -```console -$ multiapp sub hi [OPTIONS] [USER] -``` - -**Arguments**: - -* `[USER]`: The name of the user to greet [default: World] - -**Options**: - -* `--help`: Show this message and exit. - -### `multiapp sub bye` - -Say bye - -**Usage**: - -```console -$ multiapp sub bye [OPTIONS] -``` - -**Options**: - -* `--help`: Show this message and exit. diff --git a/tests/test_cli/test_doc.py b/tests/test_cli/test_doc.py index 24b0a2da8a..1da8bb73ef 100644 --- a/tests/test_cli/test_doc.py +++ b/tests/test_cli/test_doc.py @@ -28,7 +28,8 @@ def test_doc(): assert "**Arguments**" in result.stdout -def test_doc_no_rich(): +def test_doc_output(tmp_path: Path): + out_file: Path = tmp_path / "out.md" result = subprocess.run( [ sys.executable, @@ -37,22 +38,25 @@ def test_doc_no_rich(): "run", "-m", "typer", - "tests.assets.cli.multi_app_norich", + "tests.assets.cli.multi_app", "utils", "docs", "--name", "multiapp", + "--output", + str(out_file), ], capture_output=True, encoding="utf-8", ) docs_path: Path = Path(__file__).parent.parent / "assets/cli/multiapp-docs.md" docs = docs_path.read_text() - assert docs in result.stdout - assert "**Arguments**" in result.stdout + written_docs = out_file.read_text() + assert docs in written_docs + assert "Docs saved to:" in result.stdout -def test_doc_output(tmp_path: Path): +def test_doc_title_output(tmp_path: Path): out_file: Path = tmp_path / "out.md" result = subprocess.run( [ @@ -67,21 +71,22 @@ def test_doc_output(tmp_path: Path): "docs", "--name", "multiapp", + "--title", + "Awesome CLI", "--output", str(out_file), ], capture_output=True, encoding="utf-8", ) - docs_path: Path = Path(__file__).parent.parent / "assets/cli/multiapp-docs.md" + docs_path: Path = Path(__file__).parent.parent / "assets/cli/multiapp-docs-title.md" docs = docs_path.read_text() written_docs = out_file.read_text() assert docs in written_docs assert "Docs saved to:" in result.stdout -def test_doc_title_output(tmp_path: Path): - out_file: Path = tmp_path / "out.md" +def test_doc_no_rich(): result = subprocess.run( [ sys.executable, @@ -90,24 +95,19 @@ def test_doc_title_output(tmp_path: Path): "run", "-m", "typer", - "tests.assets.cli.multi_app", + "tests.assets.cli.multi_app_norich", "utils", "docs", "--name", "multiapp", - "--title", - "Awesome CLI", - "--output", - str(out_file), ], capture_output=True, encoding="utf-8", ) - docs_path: Path = Path(__file__).parent.parent / "assets/cli/multiapp-docs-title.md" + docs_path: Path = Path(__file__).parent.parent / "assets/cli/multiapp-docs.md" docs = docs_path.read_text() - written_docs = out_file.read_text() - assert docs in written_docs - assert "Docs saved to:" in result.stdout + assert docs in result.stdout + assert "**Arguments**" in result.stdout def test_doc_not_existing(): From 0a9c25373209ec27e4a2e5ff6262f020cbfce893 Mon Sep 17 00:00:00 2001 From: svlandeg Date: Thu, 12 Dec 2024 15:55:57 +0100 Subject: [PATCH 9/9] small refactor --- typer/cli.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/typer/cli.py b/typer/cli.py index 2d8507c030..d31cc474dc 100644 --- a/typer/cli.py +++ b/typer/cli.py @@ -203,9 +203,6 @@ def get_docs_for_click( title: Optional[str] = None, ) -> str: docs = "#" * (1 + indent) - rich_markup_mode = None - if hasattr(ctx, "obj") and isinstance(ctx.obj, dict): - rich_markup_mode = ctx.obj.get("TYPER_RICH_MARKUP_MODE", None) command_name = name or obj.name if call_prefix: command_name = f"{call_prefix} {command_name}" @@ -213,7 +210,7 @@ def get_docs_for_click( title = f"`{command_name}`" if command_name else "CLI" docs += f" {title}\n\n" if obj.help: - docs += f"{_parse_html(obj.help, rich_markup_mode)}\n\n" + docs += f"{_parse_html(ctx, obj.help)}\n\n" usage_pieces = obj.collect_usage_pieces(ctx) if usage_pieces: docs += "**Usage**:\n\n" @@ -237,7 +234,7 @@ def get_docs_for_click( for arg_name, arg_help in args: docs += f"* `{arg_name}`" if arg_help: - docs += f": {_parse_html(arg_help, rich_markup_mode)}" + docs += f": {_parse_html(ctx, arg_help)}" docs += "\n" docs += "\n" if opts: @@ -245,7 +242,7 @@ def get_docs_for_click( for opt_name, opt_help in opts: docs += f"* `{opt_name}`" if opt_help: - docs += f": {_parse_html(opt_help, rich_markup_mode)}" + docs += f": {_parse_html(ctx, opt_help)}" docs += "\n" docs += "\n" if obj.epilog: @@ -261,7 +258,7 @@ def get_docs_for_click( docs += f"* `{command_obj.name}`" command_help = command_obj.get_short_help_str() if command_help: - docs += f": {_parse_html(command_help, rich_markup_mode)}" + docs += f": {_parse_html(ctx, command_help)}" docs += "\n" docs += "\n" for command in commands: @@ -276,7 +273,10 @@ def get_docs_for_click( return docs -def _parse_html(input_text: str, rich_markup_mode: Optional[str]) -> str: +def _parse_html(ctx: click.Context, input_text: str) -> str: + rich_markup_mode = None + if hasattr(ctx, "obj") and isinstance(ctx.obj, dict): + rich_markup_mode = ctx.obj.get("TYPER_RICH_MARKUP_MODE", None) if has_rich and rich_markup_mode and rich_markup_mode == "rich": # pragma: no cover return rich_utils.rich_to_html(input_text) return input_text