Coverage for tests/test_ambiguous_params.py: 100%

81 statements  

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

1import pytest 1habcdefg

2import typer 1habcdefg

3from typer.testing import CliRunner 1habcdefg

4from typer.utils import ( 1habcdefg

5 AnnotatedParamWithDefaultValueError, 

6 DefaultFactoryAndDefaultValueError, 

7 MixedAnnotatedAndDefaultStyleError, 

8 MultipleTyperAnnotationsError, 

9 _split_annotation_from_typer_annotations, 

10) 

11from typing_extensions import Annotated 1habcdefg

12 

13runner = CliRunner() 1habcdefg

14 

15 

16def test_split_annotations_from_typer_annotations_simple(): 1habcdefg

17 # Simple sanity check that this utility works. If this isn't working on a given 

18 # python version, then no other tests for Annotated will work. 

19 given = Annotated[str, typer.Argument()] 1habcdefg

20 base, typer_annotations = _split_annotation_from_typer_annotations(given) 1habcdefg

21 assert base is str 1habcdefg

22 # No equality check on the param types. Checking the length is sufficient. 

23 assert len(typer_annotations) == 1 1habcdefg

24 

25 

26def test_forbid_default_value_in_annotated_argument(): 1habcdefg

27 app = typer.Typer() 1habcdefg

28 

29 # This test case only works with `typer.Argument`. `typer.Option` uses positionals 

30 # for param_decls too. 

31 @app.command() 1habcdefg

32 def cmd(my_param: Annotated[str, typer.Argument("foo")]): 1habcdefg

33 ... # pragma: no cover 

34 

35 with pytest.raises(AnnotatedParamWithDefaultValueError) as excinfo: 1habcdefg

36 runner.invoke(app) 1habcdefg

37 

38 assert vars(excinfo.value) == { 1habcdefg

39 "param_type": typer.models.ArgumentInfo, 

40 "argument_name": "my_param", 

41 } 

42 

43 

44def test_allow_options_to_have_names(): 1habcdefg

45 app = typer.Typer() 1habcdefg

46 

47 @app.command() 1habcdefg

48 def cmd(my_param: Annotated[str, typer.Option("--some-opt")]): 1habcdefg

49 print(my_param) 1habcdefg

50 

51 result = runner.invoke(app, ["--some-opt", "hello"]) 1habcdefg

52 assert result.exit_code == 0, result.output 1habcdefg

53 assert "hello" in result.output 1habcdefg

54 

55 

56@pytest.mark.parametrize( 1habcdefg

57 ["param", "param_info_type"], 

58 [ 

59 (typer.Argument, typer.models.ArgumentInfo), 

60 (typer.Option, typer.models.OptionInfo), 

61 ], 

62) 

63def test_forbid_annotated_param_and_default_param(param, param_info_type): 1abcdefg

64 app = typer.Typer() 1habcdefg

65 

66 @app.command() 1habcdefg

67 def cmd(my_param: Annotated[str, param()] = param("foo")): 1habcdefg

68 ... # pragma: no cover 

69 

70 with pytest.raises(MixedAnnotatedAndDefaultStyleError) as excinfo: 1habcdefg

71 runner.invoke(app) 1habcdefg

72 

73 assert vars(excinfo.value) == { 1habcdefg

74 "argument_name": "my_param", 

75 "annotated_param_type": param_info_type, 

76 "default_param_type": param_info_type, 

77 } 

78 

79 

80def test_forbid_multiple_typer_params_in_annotated(): 1habcdefg

81 app = typer.Typer() 1habcdefg

82 

83 @app.command() 1habcdefg

84 def cmd( 1abcdefg

85 my_param: Annotated[str, typer.Argument(), typer.Argument()], 

86 ): 

87 ... # pragma: no cover 

88 

89 with pytest.raises(MultipleTyperAnnotationsError) as excinfo: 1habcdefg

90 runner.invoke(app) 1habcdefg

91 

92 assert vars(excinfo.value) == {"argument_name": "my_param"} 1habcdefg

93 

94 

95def test_allow_multiple_non_typer_params_in_annotated(): 1habcdefg

96 app = typer.Typer() 1habcdefg

97 

98 @app.command() 1habcdefg

99 def cmd(my_param: Annotated[str, "someval", typer.Argument(), 4] = "hello"): 1habcdefg

100 print(my_param) 1habcdefg

101 

102 result = runner.invoke(app) 1habcdefg

103 # Should behave like normal 

104 assert result.exit_code == 0, result.output 1habcdefg

105 assert "hello" in result.output 1habcdefg

106 

107 

108@pytest.mark.parametrize( 1habcdefg

109 ["param", "param_info_type"], 

110 [ 

111 (typer.Argument, typer.models.ArgumentInfo), 

112 (typer.Option, typer.models.OptionInfo), 

113 ], 

114) 

115def test_forbid_default_factory_and_default_value_in_annotated(param, param_info_type): 1abcdefg

116 def make_string(): 1habcdefg

117 return "foo" # pragma: no cover 

118 

119 app = typer.Typer() 1habcdefg

120 

121 @app.command() 1habcdefg

122 def cmd( 1abcdefg

123 my_param: Annotated[str, param(default_factory=make_string)] = "hello", 

124 ): 

125 ... # pragma: no cover 

126 

127 with pytest.raises(DefaultFactoryAndDefaultValueError) as excinfo: 1habcdefg

128 runner.invoke(app) 1habcdefg

129 

130 assert vars(excinfo.value) == { 1habcdefg

131 "argument_name": "my_param", 

132 "param_type": param_info_type, 

133 } 

134 

135 

136@pytest.mark.parametrize( 1habcdefg

137 "param", 

138 [ 

139 typer.Argument, 

140 typer.Option, 

141 ], 

142) 

143def test_allow_default_factory_with_default_param(param): 1abcdefg

144 def make_string(): 1habcdefg

145 return "foo" 1habcdefg

146 

147 app = typer.Typer() 1habcdefg

148 

149 @app.command() 1habcdefg

150 def cmd(my_param: str = param(default_factory=make_string)): 1habcdefg

151 print(my_param) 1habcdefg

152 

153 result = runner.invoke(app) 1habcdefg

154 assert result.exit_code == 0, result.output 1habcdefg

155 assert "foo" in result.output 1habcdefg

156 

157 

158@pytest.mark.parametrize( 1habcdefg

159 ["param", "param_info_type"], 

160 [ 

161 (typer.Argument, typer.models.ArgumentInfo), 

162 (typer.Option, typer.models.OptionInfo), 

163 ], 

164) 

165def test_forbid_default_and_default_factory_with_default_param(param, param_info_type): 1abcdefg

166 def make_string(): 1habcdefg

167 return "foo" # pragma: no cover 

168 

169 app = typer.Typer() 1habcdefg

170 

171 @app.command() 1habcdefg

172 def cmd( 1abcdefg

173 my_param: str = param("hi", default_factory=make_string), 

174 ): 

175 ... # pragma: no cover 

176 

177 with pytest.raises(DefaultFactoryAndDefaultValueError) as excinfo: 1habcdefg

178 runner.invoke(app) 1habcdefg

179 

180 assert vars(excinfo.value) == { 1habcdefg

181 "argument_name": "my_param", 

182 "param_type": param_info_type, 

183 } 

184 

185 

186@pytest.mark.parametrize( 1habcdefg

187 ["error", "message"], 

188 [ 

189 ( 

190 AnnotatedParamWithDefaultValueError( 

191 argument_name="my_argument", 

192 param_type=typer.models.ArgumentInfo, 

193 ), 

194 "`Argument` default value cannot be set in `Annotated` for 'my_argument'. Set the default value with `=` instead.", 

195 ), 

196 ( 

197 MixedAnnotatedAndDefaultStyleError( 

198 argument_name="my_argument", 

199 annotated_param_type=typer.models.OptionInfo, 

200 default_param_type=typer.models.ArgumentInfo, 

201 ), 

202 "Cannot specify `Option` in `Annotated` and `Argument` as a default value together for 'my_argument'", 

203 ), 

204 ( 

205 MixedAnnotatedAndDefaultStyleError( 

206 argument_name="my_argument", 

207 annotated_param_type=typer.models.OptionInfo, 

208 default_param_type=typer.models.OptionInfo, 

209 ), 

210 "Cannot specify `Option` in `Annotated` and default value together for 'my_argument'", 

211 ), 

212 ( 

213 MixedAnnotatedAndDefaultStyleError( 

214 argument_name="my_argument", 

215 annotated_param_type=typer.models.ArgumentInfo, 

216 default_param_type=typer.models.ArgumentInfo, 

217 ), 

218 "Cannot specify `Argument` in `Annotated` and default value together for 'my_argument'", 

219 ), 

220 ( 

221 MultipleTyperAnnotationsError( 

222 argument_name="my_argument", 

223 ), 

224 "Cannot specify multiple `Annotated` Typer arguments for 'my_argument'", 

225 ), 

226 ( 

227 DefaultFactoryAndDefaultValueError( 

228 argument_name="my_argument", 

229 param_type=typer.models.OptionInfo, 

230 ), 

231 "Cannot specify `default_factory` and a default value together for `Option`", 

232 ), 

233 ], 

234) 

235def test_error_rendering(error, message): 1abcdefg

236 assert str(error) == message 1habcdefg