Coverage for tests/test_others.py: 100%

149 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2025-04-14 00:18 +0000

1import os 1iabcdefgh

2import subprocess 1iabcdefgh

3import sys 1iabcdefgh

4import typing 1iabcdefgh

5from pathlib import Path 1iabcdefgh

6from unittest import mock 1iabcdefgh

7 

8import click 1iabcdefgh

9import pytest 1iabcdefgh

10import shellingham 1iabcdefgh

11import typer 1iabcdefgh

12import typer.completion 1iabcdefgh

13from typer.core import _split_opt 1iabcdefgh

14from typer.main import solve_typer_info_defaults, solve_typer_info_help 1iabcdefgh

15from typer.models import ParameterInfo, TyperInfo 1iabcdefgh

16from typer.testing import CliRunner 1iabcdefgh

17 

18from .utils import requires_completion_permission 1iabcdefgh

19 

20runner = CliRunner() 1iabcdefgh

21 

22 

23def test_help_from_info(): 1iabcdefgh

24 # Mainly for coverage/completeness 

25 value = solve_typer_info_help(TyperInfo()) 1iabcdefgh

26 assert value is None 1iabcdefgh

27 

28 

29def test_defaults_from_info(): 1iabcdefgh

30 # Mainly for coverage/completeness 

31 value = solve_typer_info_defaults(TyperInfo()) 1iabcdefgh

32 assert value 1iabcdefgh

33 

34 

35def test_too_many_parsers(): 1iabcdefgh

36 def custom_parser(value: str) -> int: 1iabcdefgh

37 return int(value) # pragma: no cover 

38 

39 class CustomClickParser(click.ParamType): 1iabcdefgh

40 name = "custom_parser" 1iabcdefgh

41 

42 def convert( 1abcdefgh

43 self, 

44 value: str, 

45 param: typing.Optional[click.Parameter], 

46 ctx: typing.Optional[click.Context], 

47 ) -> typing.Any: 

48 return int(value) # pragma: no cover 

49 

50 expected_error = ( 1abcdefgh

51 "Multiple custom type parsers provided. " 

52 "`parser` and `click_type` may not both be provided." 

53 ) 

54 

55 with pytest.raises(ValueError, match=expected_error): 1iabcdefgh

56 ParameterInfo(parser=custom_parser, click_type=CustomClickParser()) 1iabcdefgh

57 

58 

59def test_valid_parser_permutations(): 1iabcdefgh

60 def custom_parser(value: str) -> int: 1iabcdefgh

61 return int(value) # pragma: no cover 

62 

63 class CustomClickParser(click.ParamType): 1iabcdefgh

64 name = "custom_parser" 1iabcdefgh

65 

66 def convert( 1abcdefgh

67 self, 

68 value: str, 

69 param: typing.Optional[click.Parameter], 

70 ctx: typing.Optional[click.Context], 

71 ) -> typing.Any: 

72 return int(value) # pragma: no cover 

73 

74 ParameterInfo() 1iabcdefgh

75 ParameterInfo(parser=custom_parser) 1iabcdefgh

76 ParameterInfo(click_type=CustomClickParser()) 1iabcdefgh

77 

78 

79@requires_completion_permission 1iabcdefgh

80def test_install_invalid_shell(): 1abcdefgh

81 app = typer.Typer() 1iabcdefgh

82 

83 @app.command() 1iabcdefgh

84 def main(): 1abcdefgh

85 print("Hello World") 1iabcdefgh

86 

87 with mock.patch.object( 1iabcdefgh

88 shellingham, "detect_shell", return_value=("xshell", "/usr/bin/xshell") 

89 ): 

90 result = runner.invoke(app, ["--install-completion"]) 1iabcdefgh

91 assert "Shell xshell is not supported." in result.stdout 1iabcdefgh

92 result = runner.invoke(app) 1iabcdefgh

93 assert "Hello World" in result.stdout 1iabcdefgh

94 

95 

96def test_callback_too_many_parameters(): 1iabcdefgh

97 app = typer.Typer() 1iabcdefgh

98 

99 def name_callback(ctx, param, val1, val2): 1iabcdefgh

100 pass # pragma: no cover 

101 

102 @app.command() 1iabcdefgh

103 def main(name: str = typer.Option(..., callback=name_callback)): 1iabcdefgh

104 pass # pragma: no cover 

105 

106 with pytest.raises(click.ClickException) as exc_info: 1iabcdefgh

107 runner.invoke(app, ["--name", "Camila"]) 1iabcdefgh

108 assert ( 1iabcdefgh

109 exc_info.value.message == "Too many CLI parameter callback function parameters" 

110 ) 

111 

112 

113def test_callback_2_untyped_parameters(): 1iabcdefgh

114 app = typer.Typer() 1iabcdefgh

115 

116 def name_callback(ctx, value): 1iabcdefgh

117 print(f"info name is: {ctx.info_name}") 1iabcdefgh

118 print(f"value is: {value}") 1iabcdefgh

119 

120 @app.command() 1iabcdefgh

121 def main(name: str = typer.Option(..., callback=name_callback)): 1iabcdefgh

122 print("Hello World") 1iabcdefgh

123 

124 result = runner.invoke(app, ["--name", "Camila"]) 1iabcdefgh

125 assert "info name is: main" in result.stdout 1iabcdefgh

126 assert "value is: Camila" in result.stdout 1iabcdefgh

127 

128 

129def test_callback_3_untyped_parameters(): 1iabcdefgh

130 app = typer.Typer() 1iabcdefgh

131 

132 def name_callback(ctx, param, value): 1iabcdefgh

133 print(f"info name is: {ctx.info_name}") 1iabcdefgh

134 print(f"param name is: {param.name}") 1iabcdefgh

135 print(f"value is: {value}") 1iabcdefgh

136 

137 @app.command() 1iabcdefgh

138 def main(name: str = typer.Option(..., callback=name_callback)): 1iabcdefgh

139 print("Hello World") 1iabcdefgh

140 

141 result = runner.invoke(app, ["--name", "Camila"]) 1iabcdefgh

142 assert "info name is: main" in result.stdout 1iabcdefgh

143 assert "param name is: name" in result.stdout 1iabcdefgh

144 assert "value is: Camila" in result.stdout 1iabcdefgh

145 

146 

147def test_completion_argument(): 1iabcdefgh

148 file_path = Path(__file__).parent / "assets/completion_argument.py" 1iabcdefgh

149 result = subprocess.run( 1iabcdefgh

150 [sys.executable, "-m", "coverage", "run", str(file_path), "E"], 

151 capture_output=True, 

152 encoding="utf-8", 

153 env={ 

154 **os.environ, 

155 "_COMPLETION_ARGUMENT.PY_COMPLETE": "complete_zsh", 

156 "_TYPER_COMPLETE_ARGS": "completion_argument.py E", 

157 "_TYPER_COMPLETE_TESTING": "True", 

158 }, 

159 ) 

160 assert "Emma" in result.stdout or "_files" in result.stdout 1iabcdefgh

161 assert "ctx: completion_argument" in result.stderr 1iabcdefgh

162 assert "arg is: name" in result.stderr 1iabcdefgh

163 assert "incomplete is: E" in result.stderr 1iabcdefgh

164 

165 

166def test_completion_untyped_parameters(): 1iabcdefgh

167 file_path = Path(__file__).parent / "assets/completion_no_types.py" 1iabcdefgh

168 result = subprocess.run( 1iabcdefgh

169 [sys.executable, "-m", "coverage", "run", str(file_path)], 

170 capture_output=True, 

171 encoding="utf-8", 

172 env={ 

173 **os.environ, 

174 "_COMPLETION_NO_TYPES.PY_COMPLETE": "complete_zsh", 

175 "_TYPER_COMPLETE_ARGS": "completion_no_types.py --name Sebastian --name Ca", 

176 }, 

177 ) 

178 assert "info name is: completion_no_types.py" in result.stderr 1iabcdefgh

179 assert "args is: []" in result.stderr 1iabcdefgh

180 assert "incomplete is: Ca" in result.stderr 1iabcdefgh

181 assert '"Camila":"The reader of books."' in result.stdout 1iabcdefgh

182 assert '"Carlos":"The writer of scripts."' in result.stdout 1iabcdefgh

183 

184 result = subprocess.run( 1iabcdefgh

185 [sys.executable, "-m", "coverage", "run", str(file_path)], 

186 capture_output=True, 

187 encoding="utf-8", 

188 ) 

189 assert "Hello World" in result.stdout 1iabcdefgh

190 

191 

192def test_completion_untyped_parameters_different_order_correct_names(): 1iabcdefgh

193 file_path = Path(__file__).parent / "assets/completion_no_types_order.py" 1iabcdefgh

194 result = subprocess.run( 1iabcdefgh

195 [sys.executable, "-m", "coverage", "run", str(file_path)], 

196 capture_output=True, 

197 encoding="utf-8", 

198 env={ 

199 **os.environ, 

200 "_COMPLETION_NO_TYPES_ORDER.PY_COMPLETE": "complete_zsh", 

201 "_TYPER_COMPLETE_ARGS": "completion_no_types_order.py --name Sebastian --name Ca", 

202 }, 

203 ) 

204 assert "info name is: completion_no_types_order.py" in result.stderr 1iabcdefgh

205 assert "args is: []" in result.stderr 1iabcdefgh

206 assert "incomplete is: Ca" in result.stderr 1iabcdefgh

207 assert '"Camila":"The reader of books."' in result.stdout 1iabcdefgh

208 assert '"Carlos":"The writer of scripts."' in result.stdout 1iabcdefgh

209 

210 result = subprocess.run( 1iabcdefgh

211 [sys.executable, "-m", "coverage", "run", str(file_path)], 

212 capture_output=True, 

213 encoding="utf-8", 

214 ) 

215 assert "Hello World" in result.stdout 1iabcdefgh

216 

217 

218def test_autocompletion_too_many_parameters(): 1iabcdefgh

219 app = typer.Typer() 1iabcdefgh

220 

221 def name_callback(ctx, args, incomplete, val2): 1iabcdefgh

222 pass # pragma: no cover 

223 

224 @app.command() 1iabcdefgh

225 def main(name: str = typer.Option(..., autocompletion=name_callback)): 1iabcdefgh

226 pass # pragma: no cover 

227 

228 with pytest.raises(click.ClickException) as exc_info: 1iabcdefgh

229 runner.invoke(app, ["--name", "Camila"]) 1iabcdefgh

230 assert exc_info.value.message == "Invalid autocompletion callback parameters: val2" 1iabcdefgh

231 

232 

233def test_forward_references(): 1iabcdefgh

234 app = typer.Typer() 1iabcdefgh

235 

236 @app.command() 1iabcdefgh

237 def main(arg1, arg2: int, arg3: "int", arg4: bool = False, arg5: "bool" = False): 1iabcdefgh

238 print(f"arg1: {type(arg1)} {arg1}") 1iabcdefgh

239 print(f"arg2: {type(arg2)} {arg2}") 1iabcdefgh

240 print(f"arg3: {type(arg3)} {arg3}") 1iabcdefgh

241 print(f"arg4: {type(arg4)} {arg4}") 1iabcdefgh

242 print(f"arg5: {type(arg5)} {arg5}") 1iabcdefgh

243 

244 result = runner.invoke(app, ["Hello", "2", "invalid"]) 1iabcdefgh

245 

246 assert "Invalid value for 'ARG3': 'invalid' is not a valid integer" in result.stdout 1iabcdefgh

247 result = runner.invoke(app, ["Hello", "2", "3", "--arg4", "--arg5"]) 1iabcdefgh

248 assert ( 1iabcdefgh

249 "arg1: <class 'str'> Hello\narg2: <class 'int'> 2\narg3: <class 'int'> 3\narg4: <class 'bool'> True\narg5: <class 'bool'> True\n" 

250 in result.stdout 

251 ) 

252 

253 

254def test_context_settings_inheritance_single_command(): 1iabcdefgh

255 app = typer.Typer(context_settings={"help_option_names": ["-h", "--help"]}) 1iabcdefgh

256 

257 @app.command() 1iabcdefgh

258 def main(name: str): 1iabcdefgh

259 pass # pragma: no cover 

260 

261 result = runner.invoke(app, ["main", "-h"]) 1iabcdefgh

262 assert "Show this message and exit." in result.stdout 1iabcdefgh

263 

264 

265def test_split_opt(): 1iabcdefgh

266 prefix, opt = _split_opt("--verbose") 1iabcdefgh

267 assert prefix == "--" 1iabcdefgh

268 assert opt == "verbose" 1iabcdefgh

269 

270 prefix, opt = _split_opt("//verbose") 1iabcdefgh

271 assert prefix == "//" 1iabcdefgh

272 assert opt == "verbose" 1iabcdefgh

273 

274 prefix, opt = _split_opt("-verbose") 1iabcdefgh

275 assert prefix == "-" 1iabcdefgh

276 assert opt == "verbose" 1iabcdefgh

277 

278 prefix, opt = _split_opt("verbose") 1iabcdefgh

279 assert prefix == "" 1iabcdefgh

280 assert opt == "verbose" 1iabcdefgh