Coverage for tests/test_others.py: 100%

140 statements  

« prev     ^ index     » next       coverage.py v7.5.4, created at 2024-06-24 00:17 +0000

1import os 1habcdefg

2import subprocess 1habcdefg

3import sys 1habcdefg

4import typing 1habcdefg

5from pathlib import Path 1habcdefg

6from unittest import mock 1habcdefg

7 

8import click 1habcdefg

9import pytest 1habcdefg

10import shellingham 1habcdefg

11import typer 1habcdefg

12import typer.completion 1habcdefg

13from typer.core import _split_opt 1habcdefg

14from typer.main import solve_typer_info_defaults, solve_typer_info_help 1habcdefg

15from typer.models import ParameterInfo, TyperInfo 1habcdefg

16from typer.testing import CliRunner 1habcdefg

17 

18runner = CliRunner() 1habcdefg

19 

20 

21def test_help_from_info(): 1habcdefg

22 # Mainly for coverage/completeness 

23 value = solve_typer_info_help(TyperInfo()) 1habcdefg

24 assert value is None 1habcdefg

25 

26 

27def test_defaults_from_info(): 1habcdefg

28 # Mainly for coverage/completeness 

29 value = solve_typer_info_defaults(TyperInfo()) 1habcdefg

30 assert value 1habcdefg

31 

32 

33def test_too_many_parsers(): 1habcdefg

34 def custom_parser(value: str) -> int: 1habcdefg

35 return int(value) # pragma: no cover 

36 

37 class CustomClickParser(click.ParamType): 1habcdefg

38 name = "custom_parser" 1habcdefg

39 

40 def convert( 1abcdefg

41 self, 

42 value: str, 

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

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

45 ) -> typing.Any: 

46 return int(value) # pragma: no cover 

47 

48 expected_error = ( 1abcdefg

49 "Multiple custom type parsers provided. " 

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

51 ) 

52 

53 with pytest.raises(ValueError, match=expected_error): 1habcdefg

54 ParameterInfo(parser=custom_parser, click_type=CustomClickParser()) 1habcdefg

55 

56 

57def test_valid_parser_permutations(): 1habcdefg

58 def custom_parser(value: str) -> int: 1habcdefg

59 return int(value) # pragma: no cover 

60 

61 class CustomClickParser(click.ParamType): 1habcdefg

62 name = "custom_parser" 1habcdefg

63 

64 def convert( 1abcdefg

65 self, 

66 value: str, 

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

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

69 ) -> typing.Any: 

70 return int(value) # pragma: no cover 

71 

72 ParameterInfo() 1habcdefg

73 ParameterInfo(parser=custom_parser) 1habcdefg

74 ParameterInfo(click_type=CustomClickParser()) 1habcdefg

75 

76 

77def test_install_invalid_shell(): 1habcdefg

78 app = typer.Typer() 1habcdefg

79 

80 @app.command() 1habcdefg

81 def main(): 1abcdefg

82 print("Hello World") 1habcdefg

83 

84 with mock.patch.object( 1habcdefg

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

86 ): 

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

88 assert "Shell xshell is not supported." in result.stdout 1habcdefg

89 result = runner.invoke(app) 1habcdefg

90 assert "Hello World" in result.stdout 1habcdefg

91 

92 

93def test_callback_too_many_parameters(): 1habcdefg

94 app = typer.Typer() 1habcdefg

95 

96 def name_callback(ctx, param, val1, val2): 1habcdefg

97 pass # pragma: no cover 

98 

99 @app.command() 1habcdefg

100 def main(name: str = typer.Option(..., callback=name_callback)): 1habcdefg

101 pass # pragma: no cover 

102 

103 with pytest.raises(click.ClickException) as exc_info: 1habcdefg

104 runner.invoke(app, ["--name", "Camila"]) 1habcdefg

105 assert ( 1habcdefg

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

107 ) 

108 

109 

110def test_callback_2_untyped_parameters(): 1habcdefg

111 app = typer.Typer() 1habcdefg

112 

113 def name_callback(ctx, value): 1habcdefg

114 print(f"info name is: {ctx.info_name}") 1habcdefg

115 print(f"value is: {value}") 1habcdefg

116 

117 @app.command() 1habcdefg

118 def main(name: str = typer.Option(..., callback=name_callback)): 1habcdefg

119 print("Hello World") 1habcdefg

120 

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

122 assert "info name is: main" in result.stdout 1habcdefg

123 assert "value is: Camila" in result.stdout 1habcdefg

124 

125 

126def test_callback_3_untyped_parameters(): 1habcdefg

127 app = typer.Typer() 1habcdefg

128 

129 def name_callback(ctx, param, value): 1habcdefg

130 print(f"info name is: {ctx.info_name}") 1habcdefg

131 print(f"param name is: {param.name}") 1habcdefg

132 print(f"value is: {value}") 1habcdefg

133 

134 @app.command() 1habcdefg

135 def main(name: str = typer.Option(..., callback=name_callback)): 1habcdefg

136 print("Hello World") 1habcdefg

137 

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

139 assert "info name is: main" in result.stdout 1habcdefg

140 assert "param name is: name" in result.stdout 1habcdefg

141 assert "value is: Camila" in result.stdout 1habcdefg

142 

143 

144def test_completion_untyped_parameters(): 1habcdefg

145 file_path = Path(__file__).parent / "assets/completion_no_types.py" 1habcdefg

146 result = subprocess.run( 1habcdefg

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

148 capture_output=True, 

149 encoding="utf-8", 

150 env={ 

151 **os.environ, 

152 "_COMPLETION_NO_TYPES.PY_COMPLETE": "complete_zsh", 

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

154 }, 

155 ) 

156 assert "info name is: completion_no_types.py" in result.stderr 1habcdefg

157 assert "args is: []" in result.stderr 1habcdefg

158 assert "incomplete is: Ca" in result.stderr 1habcdefg

159 assert '"Camila":"The reader of books."' in result.stdout 1habcdefg

160 assert '"Carlos":"The writer of scripts."' in result.stdout 1habcdefg

161 

162 result = subprocess.run( 1habcdefg

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

164 capture_output=True, 

165 encoding="utf-8", 

166 ) 

167 assert "Hello World" in result.stdout 1habcdefg

168 

169 

170def test_completion_untyped_parameters_different_order_correct_names(): 1habcdefg

171 file_path = Path(__file__).parent / "assets/completion_no_types_order.py" 1habcdefg

172 result = subprocess.run( 1habcdefg

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

174 capture_output=True, 

175 encoding="utf-8", 

176 env={ 

177 **os.environ, 

178 "_COMPLETION_NO_TYPES_ORDER.PY_COMPLETE": "complete_zsh", 

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

180 }, 

181 ) 

182 assert "info name is: completion_no_types_order.py" in result.stderr 1habcdefg

183 assert "args is: []" in result.stderr 1habcdefg

184 assert "incomplete is: Ca" in result.stderr 1habcdefg

185 assert '"Camila":"The reader of books."' in result.stdout 1habcdefg

186 assert '"Carlos":"The writer of scripts."' in result.stdout 1habcdefg

187 

188 result = subprocess.run( 1habcdefg

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

190 capture_output=True, 

191 encoding="utf-8", 

192 ) 

193 assert "Hello World" in result.stdout 1habcdefg

194 

195 

196def test_autocompletion_too_many_parameters(): 1habcdefg

197 app = typer.Typer() 1habcdefg

198 

199 def name_callback(ctx, args, incomplete, val2): 1habcdefg

200 pass # pragma: no cover 

201 

202 @app.command() 1habcdefg

203 def main(name: str = typer.Option(..., autocompletion=name_callback)): 1habcdefg

204 pass # pragma: no cover 

205 

206 with pytest.raises(click.ClickException) as exc_info: 1habcdefg

207 runner.invoke(app, ["--name", "Camila"]) 1habcdefg

208 assert exc_info.value.message == "Invalid autocompletion callback parameters: val2" 1habcdefg

209 

210 

211def test_forward_references(): 1habcdefg

212 app = typer.Typer() 1habcdefg

213 

214 @app.command() 1habcdefg

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

216 print(f"arg1: {type(arg1)} {arg1}") 1habcdefg

217 print(f"arg2: {type(arg2)} {arg2}") 1habcdefg

218 print(f"arg3: {type(arg3)} {arg3}") 1habcdefg

219 print(f"arg4: {type(arg4)} {arg4}") 1habcdefg

220 print(f"arg5: {type(arg5)} {arg5}") 1habcdefg

221 

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

223 

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

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

226 assert ( 1habcdefg

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

228 in result.stdout 

229 ) 

230 

231 

232def test_context_settings_inheritance_single_command(): 1habcdefg

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

234 

235 @app.command() 1habcdefg

236 def main(name: str): 1habcdefg

237 pass # pragma: no cover 

238 

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

240 assert "Show this message and exit." in result.stdout 1habcdefg

241 

242 

243def test_split_opt(): 1habcdefg

244 prefix, opt = _split_opt("--verbose") 1habcdefg

245 assert prefix == "--" 1habcdefg

246 assert opt == "verbose" 1habcdefg

247 

248 prefix, opt = _split_opt("//verbose") 1habcdefg

249 assert prefix == "//" 1habcdefg

250 assert opt == "verbose" 1habcdefg

251 

252 prefix, opt = _split_opt("-verbose") 1habcdefg

253 assert prefix == "-" 1habcdefg

254 assert opt == "verbose" 1habcdefg

255 

256 prefix, opt = _split_opt("verbose") 1habcdefg

257 assert prefix == "" 1habcdefg

258 assert opt == "verbose" 1habcdefg