Skip to content

Commit

Permalink
Make Typer compatible with Click 8.2
Browse files Browse the repository at this point in the history
Click 8.2 now expects the context to be passed to the make_metavar method. To enable Typer to be used both with new and old versions of Click, this passes the parameter conditionally. For the make_metavar override to stay compatible, pass the parameter through via catchall for
    positional arguments.
  • Loading branch information
theMarix committed Feb 6, 2025
1 parent 629963d commit ce64168
Show file tree
Hide file tree
Showing 12 changed files with 35 additions and 18 deletions.
2 changes: 1 addition & 1 deletion tests/test_completion/test_completion_show.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,4 +146,4 @@ def test_completion_show_invalid_shell():
shellingham, "detect_shell", return_value=("xshell", "/usr/bin/xshell")
):
result = runner.invoke(app, ["--show-completion"])
assert "Shell xshell not supported" in result.stdout
assert "Shell xshell not supported" in result.output
2 changes: 1 addition & 1 deletion tests/test_others.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ def main(arg1, arg2: int, arg3: "int", arg4: bool = False, arg5: "bool" = False)
result = runner.invoke(app, ["Hello", "2", "3", "--arg4", "--arg5"])
assert (
"arg1: <class 'str'> Hello\narg2: <class 'int'> 2\narg3: <class 'int'> 3\narg4: <class 'bool'> True\narg5: <class 'bool'> True\n"
in result.stdout
in result.output
)


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def test_call_no_arg_no_rich():
typer.core.rich = None
result = runner.invoke(app)
assert result.exit_code != 0
assert "Error: Missing argument 'NAME'" in result.stdout
assert "Error: Missing argument 'NAME'" in result.output
typer.core.rich = rich


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def test_call_no_arg_no_rich():
typer.core.rich = None
result = runner.invoke(app)
assert result.exit_code != 0
assert "Error: Missing argument 'NAME'" in result.stdout
assert "Error: Missing argument 'NAME'" in result.output
typer.core.rich = rich


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def test_call_no_arg_no_rich():
typer.core.rich = None
result = runner.invoke(app)
assert result.exit_code != 0
assert "Error: Missing argument 'NAME'" in result.stdout
assert "Error: Missing argument 'NAME'" in result.output
typer.core.rich = rich


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def test_defaults():
def test_invalid_args():
result = runner.invoke(app, ["Draco", "Hagrid"])
assert result.exit_code != 0
assert "Argument 'names' takes 3 values" in result.stdout
assert "Argument 'names' takes 3 values" in result.output


def test_valid_args():
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def test_defaults():
def test_invalid_args():
result = runner.invoke(app, ["Draco", "Hagrid"])
assert result.exit_code != 0
assert "Argument 'names' takes 3 values" in result.stdout
assert "Argument 'names' takes 3 values" in result.output


def test_valid_args():
Expand Down
4 changes: 2 additions & 2 deletions tests/test_tutorial/test_terminating/test_tutorial003.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ def test_root_no_rich():
typer.core.rich = None
result = runner.invoke(app, ["root"])
assert result.exit_code == 1
assert "The root user is reserved" in result.stdout
assert "Aborted!" in result.stdout
assert "The root user is reserved" in result.output
assert "Aborted!" in result.output
typer.core.rich = rich


Expand Down
2 changes: 1 addition & 1 deletion tests/test_tutorial/test_using_click/test_tutorial003.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

def test_cli():
result = runner.invoke(mod.typer_click_object, [])
assert "Missing command" in result.stdout
assert "Missing command" in result.output


def test_help():
Expand Down
6 changes: 3 additions & 3 deletions tests/test_tutorial/test_using_click/test_tutorial004.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@

def test_cli():
result = runner.invoke(mod.cli, [])
assert "Usage" in result.stdout
assert "dropdb" in result.stdout
assert "sub" in result.stdout
assert "Usage" in result.output
assert "dropdb" in result.output
assert "sub" in result.output


def test_typer():
Expand Down
18 changes: 14 additions & 4 deletions typer/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,12 @@ def get_help_record(self, ctx: click.Context) -> Optional[Tuple[str, str]]:
# to support Arguments
if self.hidden:
return None
name = self.make_metavar()
# Starting with Click 8.2 we need to pass the context.
# We need to check on Clicks function, as our override uses varargs
if inspect.signature(click.Parameter.make_metavar).parameters.get("ctx"):
name = self.make_metavar(ctx)
else:
name = self.make_metavar()
help = self.help or ""
extra = []
if self.show_envvar:
Expand Down Expand Up @@ -378,15 +383,15 @@ def get_help_record(self, ctx: click.Context) -> Optional[Tuple[str, str]]:
help = f"{help} {extra_str}" if help else f"{extra_str}"
return name, help

def make_metavar(self) -> str:
def make_metavar(self, *args) -> str:
# Modified version of click.core.Argument.make_metavar()
# to include Argument name
if self.metavar is not None:
return self.metavar
var = (self.name or "").upper()
if not self.required:
var = f"[{var}]"
type_var = self.type.get_metavar(self)
type_var = self.type.get_metavar(self, *args)
if type_var:
var += f":{type_var}"
if self.nargs != 1:
Expand Down Expand Up @@ -501,7 +506,12 @@ def _write_opts(opts: Sequence[str]) -> str:
any_prefix_is_slash = True

if not self.is_flag and not self.count:
rv += f" {self.make_metavar()}"
if inspect.signature(click.Parameter.make_metavar).parameters.get(
"ctx"
):
rv += f" {self.make_metavar(ctx)}"
else:
rv += f" {self.make_metavar()}"

return rv

Expand Down
9 changes: 8 additions & 1 deletion typer/rich_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from typing import Any, DefaultDict, Dict, Iterable, List, Optional, Union

import click
from click import Parameter
from rich import box
from rich.align import Align
from rich.columns import Columns
Expand Down Expand Up @@ -366,7 +367,13 @@ def _print_options_panel(

# Column for a metavar, if we have one
metavar = Text(style=STYLE_METAVAR, overflow="fold")
metavar_str = param.make_metavar()

# Starting with Click 8.2 we need to pass the context.
# We need to check on Clicks function, as our override uses varargs
if inspect.signature(Parameter.make_metavar).parameters.get("ctx"):
metavar_str = param.make_metavar(ctx)
else:
metavar_str = param.make_metavar()

# Do it ourselves if this is a positional argument
if (
Expand Down

0 comments on commit ce64168

Please sign in to comment.