typer.completion

  1import os
  2import sys
  3from typing import Any, MutableMapping, Tuple
  4
  5import click
  6
  7from ._completion_classes import completion_init
  8from ._completion_shared import Shells, get_completion_script, install
  9from .models import ParamMeta
 10from .params import Option
 11from .utils import get_params_from_function
 12
 13try:
 14    import shellingham
 15except ImportError:  # pragma: no cover
 16    shellingham = None
 17
 18
 19_click_patched = False
 20
 21
 22def get_completion_inspect_parameters() -> Tuple[ParamMeta, ParamMeta]:
 23    completion_init()
 24    test_disable_detection = os.getenv("_TYPER_COMPLETE_TEST_DISABLE_SHELL_DETECTION")
 25    if shellingham and not test_disable_detection:
 26        parameters = get_params_from_function(_install_completion_placeholder_function)
 27    else:
 28        parameters = get_params_from_function(
 29            _install_completion_no_auto_placeholder_function
 30        )
 31    install_param, show_param = parameters.values()
 32    return install_param, show_param
 33
 34
 35def install_callback(ctx: click.Context, param: click.Parameter, value: Any) -> Any:
 36    if not value or ctx.resilient_parsing:
 37        return value  # pragma: no cover
 38    if isinstance(value, str):
 39        shell, path = install(shell=value)
 40    else:
 41        shell, path = install()
 42    click.secho(f"{shell} completion installed in {path}", fg="green")
 43    click.echo("Completion will take effect once you restart the terminal")
 44    sys.exit(0)
 45
 46
 47def show_callback(ctx: click.Context, param: click.Parameter, value: Any) -> Any:
 48    if not value or ctx.resilient_parsing:
 49        return value  # pragma: no cover
 50    prog_name = ctx.find_root().info_name
 51    assert prog_name
 52    complete_var = "_{}_COMPLETE".format(prog_name.replace("-", "_").upper())
 53    shell = ""
 54    test_disable_detection = os.getenv("_TYPER_COMPLETE_TEST_DISABLE_SHELL_DETECTION")
 55    if isinstance(value, str):
 56        shell = value
 57    elif shellingham and not test_disable_detection:
 58        shell, _ = shellingham.detect_shell()
 59    script_content = get_completion_script(
 60        prog_name=prog_name, complete_var=complete_var, shell=shell
 61    )
 62    click.echo(script_content)
 63    sys.exit(0)
 64
 65
 66# Create a fake command function to extract the completion parameters
 67def _install_completion_placeholder_function(
 68    install_completion: bool = Option(
 69        None,
 70        "--install-completion",
 71        callback=install_callback,
 72        expose_value=False,
 73        help="Install completion for the current shell.",
 74    ),
 75    show_completion: bool = Option(
 76        None,
 77        "--show-completion",
 78        callback=show_callback,
 79        expose_value=False,
 80        help="Show completion for the current shell, to copy it or customize the installation.",
 81    ),
 82) -> Any:
 83    pass  # pragma: no cover
 84
 85
 86def _install_completion_no_auto_placeholder_function(
 87    install_completion: Shells = Option(
 88        None,
 89        callback=install_callback,
 90        expose_value=False,
 91        help="Install completion for the specified shell.",
 92    ),
 93    show_completion: Shells = Option(
 94        None,
 95        callback=show_callback,
 96        expose_value=False,
 97        help="Show completion for the specified shell, to copy it or customize the installation.",
 98    ),
 99) -> Any:
100    pass  # pragma: no cover
101
102
103# Re-implement Click's shell_complete to add error message with:
104# Invalid completion instruction
105# To use 7.x instruction style for compatibility
106# And to add extra error messages, for compatibility with Typer in previous versions
107# This is only called in new Command method, only used by Click 8.x+
108def shell_complete(
109    cli: click.Command,
110    ctx_args: MutableMapping[str, Any],
111    prog_name: str,
112    complete_var: str,
113    instruction: str,
114) -> int:
115    import click
116    import click.shell_completion
117
118    if "_" not in instruction:
119        click.echo("Invalid completion instruction.", err=True)
120        return 1
121
122    # Click 8 changed the order/style of shell instructions from e.g.
123    # source_bash to bash_source
124    # Typer override to preserve the old style for compatibility
125    # Original in Click 8.x commented:
126    # shell, _, instruction = instruction.partition("_")
127    instruction, _, shell = instruction.partition("_")
128    # Typer override end
129
130    comp_cls = click.shell_completion.get_completion_class(shell)
131
132    if comp_cls is None:
133        click.echo(f"Shell {shell} not supported.", err=True)
134        return 1
135
136    comp = comp_cls(cli, ctx_args, prog_name, complete_var)
137
138    if instruction == "source":
139        click.echo(comp.source())
140        return 0
141
142    # Typer override to print the completion help msg with Rich
143    if instruction == "complete":
144        click.echo(comp.complete())
145        return 0
146    # Typer override end
147
148    click.echo(f'Completion instruction "{instruction}" not supported.', err=True)
149    return 1
def get_completion_inspect_parameters() -> Tuple[typer.models.ParamMeta, typer.models.ParamMeta]:
23def get_completion_inspect_parameters() -> Tuple[ParamMeta, ParamMeta]:
24    completion_init()
25    test_disable_detection = os.getenv("_TYPER_COMPLETE_TEST_DISABLE_SHELL_DETECTION")
26    if shellingham and not test_disable_detection:
27        parameters = get_params_from_function(_install_completion_placeholder_function)
28    else:
29        parameters = get_params_from_function(
30            _install_completion_no_auto_placeholder_function
31        )
32    install_param, show_param = parameters.values()
33    return install_param, show_param
def install_callback(ctx: click.core.Context, param: click.core.Parameter, value: Any) -> Any:
36def install_callback(ctx: click.Context, param: click.Parameter, value: Any) -> Any:
37    if not value or ctx.resilient_parsing:
38        return value  # pragma: no cover
39    if isinstance(value, str):
40        shell, path = install(shell=value)
41    else:
42        shell, path = install()
43    click.secho(f"{shell} completion installed in {path}", fg="green")
44    click.echo("Completion will take effect once you restart the terminal")
45    sys.exit(0)
def show_callback(ctx: click.core.Context, param: click.core.Parameter, value: Any) -> Any:
48def show_callback(ctx: click.Context, param: click.Parameter, value: Any) -> Any:
49    if not value or ctx.resilient_parsing:
50        return value  # pragma: no cover
51    prog_name = ctx.find_root().info_name
52    assert prog_name
53    complete_var = "_{}_COMPLETE".format(prog_name.replace("-", "_").upper())
54    shell = ""
55    test_disable_detection = os.getenv("_TYPER_COMPLETE_TEST_DISABLE_SHELL_DETECTION")
56    if isinstance(value, str):
57        shell = value
58    elif shellingham and not test_disable_detection:
59        shell, _ = shellingham.detect_shell()
60    script_content = get_completion_script(
61        prog_name=prog_name, complete_var=complete_var, shell=shell
62    )
63    click.echo(script_content)
64    sys.exit(0)
def shell_complete( cli: click.core.Command, ctx_args: MutableMapping[str, Any], prog_name: str, complete_var: str, instruction: str) -> int:
109def shell_complete(
110    cli: click.Command,
111    ctx_args: MutableMapping[str, Any],
112    prog_name: str,
113    complete_var: str,
114    instruction: str,
115) -> int:
116    import click
117    import click.shell_completion
118
119    if "_" not in instruction:
120        click.echo("Invalid completion instruction.", err=True)
121        return 1
122
123    # Click 8 changed the order/style of shell instructions from e.g.
124    # source_bash to bash_source
125    # Typer override to preserve the old style for compatibility
126    # Original in Click 8.x commented:
127    # shell, _, instruction = instruction.partition("_")
128    instruction, _, shell = instruction.partition("_")
129    # Typer override end
130
131    comp_cls = click.shell_completion.get_completion_class(shell)
132
133    if comp_cls is None:
134        click.echo(f"Shell {shell} not supported.", err=True)
135        return 1
136
137    comp = comp_cls(cli, ctx_args, prog_name, complete_var)
138
139    if instruction == "source":
140        click.echo(comp.source())
141        return 0
142
143    # Typer override to print the completion help msg with Rich
144    if instruction == "complete":
145        click.echo(comp.complete())
146        return 0
147    # Typer override end
148
149    click.echo(f'Completion instruction "{instruction}" not supported.', err=True)
150    return 1