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