Coverage for tests/test_ambiguous_params.py: 100%

71 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2024-09-09 18:26 +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")]): ... # pragma: no cover 1habcdefg

33 

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

35 runner.invoke(app) 1habcdefg

36 

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

38 "param_type": typer.models.ArgumentInfo, 

39 "argument_name": "my_param", 

40 } 

41 

42 

43def test_allow_options_to_have_names(): 1habcdefg

44 app = typer.Typer() 1habcdefg

45 

46 @app.command() 1habcdefg

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

48 print(my_param) 1habcdefg

49 

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

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

52 assert "hello" in result.output 1habcdefg

53 

54 

55@pytest.mark.parametrize( 1habcdefg

56 ["param", "param_info_type"], 

57 [ 

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

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

60 ], 

61) 

62def test_forbid_annotated_param_and_default_param(param, param_info_type): 1abcdefg

63 app = typer.Typer() 1habcdefg

64 

65 @app.command() 1habcdefg

66 def cmd(my_param: Annotated[str, param()] = param("foo")): ... # pragma: no cover 1habcdefg

67 

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

69 runner.invoke(app) 1habcdefg

70 

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

72 "argument_name": "my_param", 

73 "annotated_param_type": param_info_type, 

74 "default_param_type": param_info_type, 

75 } 

76 

77 

78def test_forbid_multiple_typer_params_in_annotated(): 1habcdefg

79 app = typer.Typer() 1habcdefg

80 

81 @app.command() 1habcdefg

82 def cmd( 1abcdefg

83 my_param: Annotated[str, typer.Argument(), typer.Argument()], 1habcdefg

84 ): ... # pragma: no cover 

85 

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

87 runner.invoke(app) 1habcdefg

88 

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

90 

91 

92def test_allow_multiple_non_typer_params_in_annotated(): 1habcdefg

93 app = typer.Typer() 1habcdefg

94 

95 @app.command() 1habcdefg

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

97 print(my_param) 1habcdefg

98 

99 result = runner.invoke(app) 1habcdefg

100 # Should behave like normal 

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

102 assert "hello" in result.output 1habcdefg

103 

104 

105@pytest.mark.parametrize( 1habcdefg

106 ["param", "param_info_type"], 

107 [ 

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

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

110 ], 

111) 

112def test_forbid_default_factory_and_default_value_in_annotated(param, param_info_type): 1abcdefg

113 def make_string(): 1habcdefg

114 return "foo" # pragma: no cover 

115 

116 app = typer.Typer() 1habcdefg

117 

118 @app.command() 1habcdefg

119 def cmd( 1abcdefg

120 my_param: Annotated[str, param(default_factory=make_string)] = "hello", 1habcdefg

121 ): ... # pragma: no cover 

122 

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

124 runner.invoke(app) 1habcdefg

125 

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

127 "argument_name": "my_param", 

128 "param_type": param_info_type, 

129 } 

130 

131 

132@pytest.mark.parametrize( 1habcdefg

133 "param", 

134 [ 

135 typer.Argument, 

136 typer.Option, 

137 ], 

138) 

139def test_allow_default_factory_with_default_param(param): 1abcdefg

140 def make_string(): 1habcdefg

141 return "foo" 1habcdefg

142 

143 app = typer.Typer() 1habcdefg

144 

145 @app.command() 1habcdefg

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

147 print(my_param) 1habcdefg

148 

149 result = runner.invoke(app) 1habcdefg

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

151 assert "foo" in result.output 1habcdefg

152 

153 

154@pytest.mark.parametrize( 1habcdefg

155 ["param", "param_info_type"], 

156 [ 

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

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

159 ], 

160) 

161def test_forbid_default_and_default_factory_with_default_param(param, param_info_type): 1abcdefg

162 def make_string(): 1habcdefg

163 return "foo" # pragma: no cover 

164 

165 app = typer.Typer() 1habcdefg

166 

167 @app.command() 1habcdefg

168 def cmd( 1abcdefg

169 my_param: str = param("hi", default_factory=make_string), 1habcdefg

170 ): ... # pragma: no cover 

171 

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

173 runner.invoke(app) 1habcdefg

174 

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

176 "argument_name": "my_param", 

177 "param_type": param_info_type, 

178 } 

179 

180 

181@pytest.mark.parametrize( 1habcdefg

182 ["error", "message"], 

183 [ 

184 ( 

185 AnnotatedParamWithDefaultValueError( 

186 argument_name="my_argument", 

187 param_type=typer.models.ArgumentInfo, 

188 ), 

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

190 ), 

191 ( 

192 MixedAnnotatedAndDefaultStyleError( 

193 argument_name="my_argument", 

194 annotated_param_type=typer.models.OptionInfo, 

195 default_param_type=typer.models.ArgumentInfo, 

196 ), 

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

198 ), 

199 ( 

200 MixedAnnotatedAndDefaultStyleError( 

201 argument_name="my_argument", 

202 annotated_param_type=typer.models.OptionInfo, 

203 default_param_type=typer.models.OptionInfo, 

204 ), 

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

206 ), 

207 ( 

208 MixedAnnotatedAndDefaultStyleError( 

209 argument_name="my_argument", 

210 annotated_param_type=typer.models.ArgumentInfo, 

211 default_param_type=typer.models.ArgumentInfo, 

212 ), 

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

214 ), 

215 ( 

216 MultipleTyperAnnotationsError( 

217 argument_name="my_argument", 

218 ), 

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

220 ), 

221 ( 

222 DefaultFactoryAndDefaultValueError( 

223 argument_name="my_argument", 

224 param_type=typer.models.OptionInfo, 

225 ), 

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

227 ), 

228 ], 

229) 

230def test_error_rendering(error, message): 1abcdefg

231 assert str(error) == message 1habcdefg