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        is_flag=True,
 72        callback=install_callback,
 73        expose_value=False,
 74        help="Install completion for the current shell.",
 75    ),
 76    show_completion: bool = Option(
 77        None,
 78        "--show-completion",
 79        is_flag=True,
 80        callback=show_callback,
 81        expose_value=False,
 82        help="Show completion for the current shell, to copy it or customize the installation.",
 83    ),
 84) -> Any:
 85    pass  # pragma: no cover
 86
 87
 88def _install_completion_no_auto_placeholder_function(
 89    install_completion: Shells = Option(
 90        None,
 91        callback=install_callback,
 92        expose_value=False,
 93        help="Install completion for the specified shell.",
 94    ),
 95    show_completion: Shells = Option(
 96        None,
 97        callback=show_callback,
 98        expose_value=False,
 99        help="Show completion for the specified shell, to copy it or customize the installation.",
100    ),
101) -> Any:
102    pass  # pragma: no cover
103
104
105# Re-implement Click's shell_complete to add error message with:
106# Invalid completion instruction
107# To use 7.x instruction style for compatibility
108# And to add extra error messages, for compatibility with Typer in previous versions
109# This is only called in new Command method, only used by Click 8.x+
110def shell_complete(
111    cli: click.Command,
112    ctx_args: MutableMapping[str, Any],
113    prog_name: str,
114    complete_var: str,
115    instruction: str,
116) -> int:
117    import click
118    import click.shell_completion
119
120    if "_" not in instruction:
121        click.echo("Invalid completion instruction.", err=True)
122        return 1
123
124    # Click 8 changed the order/style of shell instructions from e.g.
125    # source_bash to bash_source
126    # Typer override to preserve the old style for compatibility
127    # Original in Click 8.x commented:
128    # shell, _, instruction = instruction.partition("_")
129    instruction, _, shell = instruction.partition("_")
130    # Typer override end
131
132    comp_cls = click.shell_completion.get_completion_class(shell)
133
134    if comp_cls is None:
135        click.echo(f"Shell {shell} not supported.", err=True)
136        return 1
137
138    comp = comp_cls(cli, ctx_args, prog_name, complete_var)
139
140    if instruction == "source":
141        click.echo(comp.source())
142        return 0
143
144    if instruction == "complete":
145        click.echo(comp.complete())
146        return 0
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:
111def shell_complete(
112    cli: click.Command,
113    ctx_args: MutableMapping[str, Any],
114    prog_name: str,
115    complete_var: str,
116    instruction: str,
117) -> int:
118    import click
119    import click.shell_completion
120
121    if "_" not in instruction:
122        click.echo("Invalid completion instruction.", err=True)
123        return 1
124
125    # Click 8 changed the order/style of shell instructions from e.g.
126    # source_bash to bash_source
127    # Typer override to preserve the old style for compatibility
128    # Original in Click 8.x commented:
129    # shell, _, instruction = instruction.partition("_")
130    instruction, _, shell = instruction.partition("_")
131    # Typer override end
132
133    comp_cls = click.shell_completion.get_completion_class(shell)
134
135    if comp_cls is None:
136        click.echo(f"Shell {shell} not supported.", err=True)
137        return 1
138
139    comp = comp_cls(cli, ctx_args, prog_name, complete_var)
140
141    if instruction == "source":
142        click.echo(comp.source())
143        return 0
144
145    if instruction == "complete":
146        click.echo(comp.complete())
147        return 0
148
149    click.echo(f'Completion instruction "{instruction}" not supported.', err=True)
150    return 1