Coverage for typer / completion.py: 100%

66 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-02-09 12:36 +0000

1import os 1abcdefgh

2import sys 1abcdefgh

3from collections.abc import MutableMapping 1abcdefgh

4from typing import Any 1abcdefgh

5 

6import click 1abcdefgh

7 

8from ._completion_classes import completion_init 1abcdefgh

9from ._completion_shared import Shells, _get_shell_name, get_completion_script, install 1abcdefgh

10from .core import HAS_SHELLINGHAM 1abcdefgh

11from .models import ParamMeta 1abcdefgh

12from .params import Option 1abcdefgh

13from .utils import get_params_from_function 1abcdefgh

14 

15_click_patched = False 1abcdefgh

16 

17 

18def get_completion_inspect_parameters() -> tuple[ParamMeta, ParamMeta]: 1abcdefgh

19 completion_init() 1abcdefgh

20 test_disable_detection = os.getenv("_TYPER_COMPLETE_TEST_DISABLE_SHELL_DETECTION") 1abcdefgh

21 if HAS_SHELLINGHAM and not test_disable_detection: 1abcdefgh

22 parameters = get_params_from_function(_install_completion_placeholder_function) 1abcdefgh

23 else: 

24 parameters = get_params_from_function( 1abcdefgh

25 _install_completion_no_auto_placeholder_function 

26 ) 

27 install_param, show_param = parameters.values() 1abcdefgh

28 return install_param, show_param 1abcdefgh

29 

30 

31def install_callback(ctx: click.Context, param: click.Parameter, value: Any) -> Any: 1abcdefgh

32 if not value or ctx.resilient_parsing: 1abcdefgh

33 return value # pragma: no cover 1abcdefgh

34 if isinstance(value, str): 1abcdefgh

35 shell, path = install(shell=value) 1abcdefgh

36 else: 

37 shell, path = install() 1abcdefgh

38 click.secho(f"{shell} completion installed in {path}", fg="green") 1abcdefgh

39 click.echo("Completion will take effect once you restart the terminal") 1abcdefgh

40 sys.exit(0) 1abcdefgh

41 

42 

43def show_callback(ctx: click.Context, param: click.Parameter, value: Any) -> Any: 1abcdefgh

44 if not value or ctx.resilient_parsing: 1abcdefgh

45 return value # pragma: no cover 1abcdefgh

46 prog_name = ctx.find_root().info_name 1abcdefgh

47 assert prog_name 1abcdefgh

48 complete_var = "_{}_COMPLETE".format(prog_name.replace("-", "_").upper()) 1abcdefgh

49 shell = "" 1abcdefgh

50 test_disable_detection = os.getenv("_TYPER_COMPLETE_TEST_DISABLE_SHELL_DETECTION") 1abcdefgh

51 if isinstance(value, str): 1abcdefgh

52 shell = value 1abcdefgh

53 elif not test_disable_detection: 1abcdefgh

54 detected_shell = _get_shell_name() 1abcdefgh

55 if detected_shell is not None: 1abcdefgh

56 shell = detected_shell 1abcdefgh

57 script_content = get_completion_script( 1abcdefgh

58 prog_name=prog_name, complete_var=complete_var, shell=shell 

59 ) 

60 click.echo(script_content) 1abcdefgh

61 sys.exit(0) 1abcdefgh

62 

63 

64# Create a fake command function to extract the completion parameters 

65def _install_completion_placeholder_function( 1abcdefgh

66 install_completion: bool = Option( 

67 None, 

68 "--install-completion", 

69 callback=install_callback, 

70 expose_value=False, 

71 help="Install completion for the current shell.", 

72 ), 

73 show_completion: bool = Option( 

74 None, 

75 "--show-completion", 

76 callback=show_callback, 

77 expose_value=False, 

78 help="Show completion for the current shell, to copy it or customize the installation.", 

79 ), 

80) -> Any: 

81 pass # pragma: no cover 

82 

83 

84def _install_completion_no_auto_placeholder_function( 1abcdefgh

85 install_completion: Shells = Option( 

86 None, 

87 callback=install_callback, 

88 expose_value=False, 

89 help="Install completion for the specified shell.", 

90 ), 

91 show_completion: Shells = Option( 

92 None, 

93 callback=show_callback, 

94 expose_value=False, 

95 help="Show completion for the specified shell, to copy it or customize the installation.", 

96 ), 

97) -> Any: 

98 pass # pragma: no cover 

99 

100 

101# Re-implement Click's shell_complete to add error message with: 

102# Invalid completion instruction 

103# To use 7.x instruction style for compatibility 

104# And to add extra error messages, for compatibility with Typer in previous versions 

105# This is only called in new Command method, only used by Click 8.x+ 

106def shell_complete( 1abcdefgh

107 cli: click.Command, 

108 ctx_args: MutableMapping[str, Any], 

109 prog_name: str, 

110 complete_var: str, 

111 instruction: str, 

112) -> int: 

113 import click 1abcdefgh

114 import click.shell_completion 1abcdefgh

115 

116 if "_" not in instruction: 1abcdefgh

117 click.echo("Invalid completion instruction.", err=True) 1abcdefgh

118 return 1 1abcdefgh

119 

120 # Click 8 changed the order/style of shell instructions from e.g. 

121 # source_bash to bash_source 

122 # Typer override to preserve the old style for compatibility 

123 # Original in Click 8.x commented: 

124 # shell, _, instruction = instruction.partition("_") 

125 instruction, _, shell = instruction.partition("_") 1abcdefgh

126 # Typer override end 

127 

128 comp_cls = click.shell_completion.get_completion_class(shell) 1abcdefgh

129 

130 if comp_cls is None: 1abcdefgh

131 click.echo(f"Shell {shell} not supported.", err=True) 1abcdefgh

132 return 1 1abcdefgh

133 

134 comp = comp_cls(cli, ctx_args, prog_name, complete_var) 1abcdefgh

135 

136 if instruction == "source": 1abcdefgh

137 click.echo(comp.source()) 1abcdefgh

138 return 0 1abcdefgh

139 

140 # Typer override to print the completion help msg with Rich 

141 if instruction == "complete": 1abcdefgh

142 click.echo(comp.complete()) 1abcdefgh

143 return 0 1abcdefgh

144 # Typer override end 

145 

146 click.echo(f'Completion instruction "{instruction}" not supported.', err=True) 1abcdefgh

147 return 1 1abcdefgh