Coverage for tests / test_ambiguous_params.py: 100%
71 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-03-26 21:46 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2026-03-26 21:46 +0000
1from typing import Annotated 1abecfdg
3import pytest 1abecfdg
4import typer 1abecfdg
5from typer.testing import CliRunner 1abecfdg
6from typer.utils import ( 1abecfdg
7 AnnotatedParamWithDefaultValueError,
8 DefaultFactoryAndDefaultValueError,
9 MixedAnnotatedAndDefaultStyleError,
10 MultipleTyperAnnotationsError,
11 _split_annotation_from_typer_annotations,
12)
14runner = CliRunner() 1abecfdg
17def test_split_annotations_from_typer_annotations_simple(): 1abecfdg
18 # Simple sanity check that this utility works. If this isn't working on a given
19 # python version, then no other tests for Annotated will work.
20 given = Annotated[str, typer.Argument()] 1abecfdg
21 base, typer_annotations = _split_annotation_from_typer_annotations(given) 1abecfdg
22 assert base is str 1abecfdg
23 # No equality check on the param types. Checking the length is sufficient.
24 assert len(typer_annotations) == 1 1abecfdg
27def test_forbid_default_value_in_annotated_argument(): 1abecfdg
28 app = typer.Typer() 1abecfdg
30 # This test case only works with `typer.Argument`. `typer.Option` uses positionals
31 # for param_decls too.
32 @app.command() 1abecfdg
33 def cmd(my_param: Annotated[str, typer.Argument("foo")]): ... # pragma: no cover 1abecfdg
35 with pytest.raises(AnnotatedParamWithDefaultValueError) as excinfo: 1abecfdg
36 runner.invoke(app) 1abecfdg
38 assert vars(excinfo.value) == { 1abecfdg
39 "param_type": typer.models.ArgumentInfo,
40 "argument_name": "my_param",
41 }
44def test_allow_options_to_have_names(): 1abecfdg
45 app = typer.Typer() 1abecfdg
47 @app.command() 1abecfdg
48 def cmd(my_param: Annotated[str, typer.Option("--some-opt")]): 1abecfdg
49 print(my_param) 1abecfdg
51 result = runner.invoke(app, ["--some-opt", "hello"]) 1abecfdg
52 assert result.exit_code == 0, result.output 1abecfdg
53 assert "hello" in result.output 1abecfdg
56@pytest.mark.parametrize( 1abecfdg
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): 1abecfdg
64 app = typer.Typer() 1abecfdg
66 @app.command() 1abecfdg
67 def cmd(my_param: Annotated[str, param()] = param("foo")): ... # pragma: no cover 1abecfdg
69 with pytest.raises(MixedAnnotatedAndDefaultStyleError) as excinfo: 1abecfdg
70 runner.invoke(app) 1abecfdg
72 assert vars(excinfo.value) == { 1abecfdg
73 "argument_name": "my_param",
74 "annotated_param_type": param_info_type,
75 "default_param_type": param_info_type,
76 }
79def test_forbid_multiple_typer_params_in_annotated(): 1abecfdg
80 app = typer.Typer() 1abecfdg
82 @app.command() 1abecfdg
83 def cmd( 1abecfdg
84 my_param: Annotated[str, typer.Argument(), typer.Argument()], 1abcd
85 ): ... # pragma: no cover
87 with pytest.raises(MultipleTyperAnnotationsError) as excinfo: 1abecfdg
88 runner.invoke(app) 1abecfdg
90 assert vars(excinfo.value) == {"argument_name": "my_param"} 1abecfdg
93def test_allow_multiple_non_typer_params_in_annotated(): 1abecfdg
94 app = typer.Typer() 1abecfdg
96 @app.command() 1abecfdg
97 def cmd(my_param: Annotated[str, "someval", typer.Argument(), 4] = "hello"): 1abecfdg
98 print(my_param) 1abecfdg
100 result = runner.invoke(app) 1abecfdg
101 # Should behave like normal
102 assert result.exit_code == 0, result.output 1abecfdg
103 assert "hello" in result.output 1abecfdg
106@pytest.mark.parametrize( 1abecfdg
107 ["param", "param_info_type"],
108 [
109 (typer.Argument, typer.models.ArgumentInfo),
110 (typer.Option, typer.models.OptionInfo),
111 ],
112)
113def test_forbid_default_factory_and_default_value_in_annotated(param, param_info_type): 1abecfdg
114 def make_string(): 1abecfdg
115 return "foo" # pragma: no cover
117 app = typer.Typer() 1abecfdg
119 @app.command() 1abecfdg
120 def cmd( 1abecfdg
121 my_param: Annotated[str, param(default_factory=make_string)] = "hello", 1abcd
122 ): ... # pragma: no cover
124 with pytest.raises(DefaultFactoryAndDefaultValueError) as excinfo: 1abecfdg
125 runner.invoke(app) 1abecfdg
127 assert vars(excinfo.value) == { 1abecfdg
128 "argument_name": "my_param",
129 "param_type": param_info_type,
130 }
133@pytest.mark.parametrize( 1abecfdg
134 "param",
135 [
136 typer.Argument,
137 typer.Option,
138 ],
139)
140def test_allow_default_factory_with_default_param(param): 1abecfdg
141 def make_string(): 1abecfdg
142 return "foo" 1abecfdg
144 app = typer.Typer() 1abecfdg
146 @app.command() 1abecfdg
147 def cmd(my_param: str = param(default_factory=make_string)): 1abecfdg
148 print(my_param) 1abecfdg
150 result = runner.invoke(app) 1abecfdg
151 assert result.exit_code == 0, result.output 1abecfdg
152 assert "foo" in result.output 1abecfdg
155@pytest.mark.parametrize( 1abecfdg
156 ["param", "param_info_type"],
157 [
158 (typer.Argument, typer.models.ArgumentInfo),
159 (typer.Option, typer.models.OptionInfo),
160 ],
161)
162def test_forbid_default_and_default_factory_with_default_param(param, param_info_type): 1abecfdg
163 def make_string(): 1abecfdg
164 return "foo" # pragma: no cover
166 app = typer.Typer() 1abecfdg
168 @app.command() 1abecfdg
169 def cmd( 1abecfdg
170 my_param: str = param("hi", default_factory=make_string), 1abecfdg
171 ): ... # pragma: no cover
173 with pytest.raises(DefaultFactoryAndDefaultValueError) as excinfo: 1abecfdg
174 runner.invoke(app) 1abecfdg
176 assert vars(excinfo.value) == { 1abecfdg
177 "argument_name": "my_param",
178 "param_type": param_info_type,
179 }
182@pytest.mark.parametrize( 1abecfdg
183 ["error", "message"],
184 [
185 (
186 AnnotatedParamWithDefaultValueError(
187 argument_name="my_argument",
188 param_type=typer.models.ArgumentInfo,
189 ),
190 "`Argument` default value cannot be set in `Annotated` for 'my_argument'. Set the default value with `=` instead.",
191 ),
192 (
193 MixedAnnotatedAndDefaultStyleError(
194 argument_name="my_argument",
195 annotated_param_type=typer.models.OptionInfo,
196 default_param_type=typer.models.ArgumentInfo,
197 ),
198 "Cannot specify `Option` in `Annotated` and `Argument` as a default value together for 'my_argument'",
199 ),
200 (
201 MixedAnnotatedAndDefaultStyleError(
202 argument_name="my_argument",
203 annotated_param_type=typer.models.OptionInfo,
204 default_param_type=typer.models.OptionInfo,
205 ),
206 "Cannot specify `Option` in `Annotated` and default value together for 'my_argument'",
207 ),
208 (
209 MixedAnnotatedAndDefaultStyleError(
210 argument_name="my_argument",
211 annotated_param_type=typer.models.ArgumentInfo,
212 default_param_type=typer.models.ArgumentInfo,
213 ),
214 "Cannot specify `Argument` in `Annotated` and default value together for 'my_argument'",
215 ),
216 (
217 MultipleTyperAnnotationsError(
218 argument_name="my_argument",
219 ),
220 "Cannot specify multiple `Annotated` Typer arguments for 'my_argument'",
221 ),
222 (
223 DefaultFactoryAndDefaultValueError(
224 argument_name="my_argument",
225 param_type=typer.models.OptionInfo,
226 ),
227 "Cannot specify `default_factory` and a default value together for `Option`",
228 ),
229 ],
230)
231def test_error_rendering(error, message): 1abecfdg
232 assert str(error) == message 1abecfdg