Coverage for tests / test_ambiguous_params.py: 100%
71 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-02-09 12:36 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2026-02-09 12:36 +0000
1from typing import Annotated 1abfcdgeh
3import pytest 1abfcdgeh
4import typer 1abfcdgeh
5from typer.testing import CliRunner 1abfcdgeh
6from typer.utils import ( 1abfcdgeh
7 AnnotatedParamWithDefaultValueError,
8 DefaultFactoryAndDefaultValueError,
9 MixedAnnotatedAndDefaultStyleError,
10 MultipleTyperAnnotationsError,
11 _split_annotation_from_typer_annotations,
12)
14runner = CliRunner() 1abfcdgeh
17def test_split_annotations_from_typer_annotations_simple(): 1abfcdgeh
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()] 1abfcdgeh
21 base, typer_annotations = _split_annotation_from_typer_annotations(given) 1abfcdgeh
22 assert base is str 1abfcdgeh
23 # No equality check on the param types. Checking the length is sufficient.
24 assert len(typer_annotations) == 1 1abfcdgeh
27def test_forbid_default_value_in_annotated_argument(): 1abfcdgeh
28 app = typer.Typer() 1abfcdgeh
30 # This test case only works with `typer.Argument`. `typer.Option` uses positionals
31 # for param_decls too.
32 @app.command() 1abfcdgeh
33 def cmd(my_param: Annotated[str, typer.Argument("foo")]): ... # pragma: no cover 1abfcdgeh
35 with pytest.raises(AnnotatedParamWithDefaultValueError) as excinfo: 1abfcdgeh
36 runner.invoke(app) 1abfcdgeh
38 assert vars(excinfo.value) == { 1abfcdgeh
39 "param_type": typer.models.ArgumentInfo,
40 "argument_name": "my_param",
41 }
44def test_allow_options_to_have_names(): 1abfcdgeh
45 app = typer.Typer() 1abfcdgeh
47 @app.command() 1abfcdgeh
48 def cmd(my_param: Annotated[str, typer.Option("--some-opt")]): 1abfcdgeh
49 print(my_param) 1abfcdgeh
51 result = runner.invoke(app, ["--some-opt", "hello"]) 1abfcdgeh
52 assert result.exit_code == 0, result.output 1abfcdgeh
53 assert "hello" in result.output 1abfcdgeh
56@pytest.mark.parametrize( 1abfcdgeh
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): 1abfcdgeh
64 app = typer.Typer() 1abfcdgeh
66 @app.command() 1abfcdgeh
67 def cmd(my_param: Annotated[str, param()] = param("foo")): ... # pragma: no cover 1abfcdgeh
69 with pytest.raises(MixedAnnotatedAndDefaultStyleError) as excinfo: 1abfcdgeh
70 runner.invoke(app) 1abfcdgeh
72 assert vars(excinfo.value) == { 1abfcdgeh
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(): 1abfcdgeh
80 app = typer.Typer() 1abfcdgeh
82 @app.command() 1abfcdgeh
83 def cmd( 1abfcdgeh
84 my_param: Annotated[str, typer.Argument(), typer.Argument()], 1abcde
85 ): ... # pragma: no cover
87 with pytest.raises(MultipleTyperAnnotationsError) as excinfo: 1abfcdgeh
88 runner.invoke(app) 1abfcdgeh
90 assert vars(excinfo.value) == {"argument_name": "my_param"} 1abfcdgeh
93def test_allow_multiple_non_typer_params_in_annotated(): 1abfcdgeh
94 app = typer.Typer() 1abfcdgeh
96 @app.command() 1abfcdgeh
97 def cmd(my_param: Annotated[str, "someval", typer.Argument(), 4] = "hello"): 1abfcdgeh
98 print(my_param) 1abfcdgeh
100 result = runner.invoke(app) 1abfcdgeh
101 # Should behave like normal
102 assert result.exit_code == 0, result.output 1abfcdgeh
103 assert "hello" in result.output 1abfcdgeh
106@pytest.mark.parametrize( 1abfcdgeh
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): 1abfcdgeh
114 def make_string(): 1abfcdgeh
115 return "foo" # pragma: no cover
117 app = typer.Typer() 1abfcdgeh
119 @app.command() 1abfcdgeh
120 def cmd( 1abfcdgeh
121 my_param: Annotated[str, param(default_factory=make_string)] = "hello", 1abcde
122 ): ... # pragma: no cover
124 with pytest.raises(DefaultFactoryAndDefaultValueError) as excinfo: 1abfcdgeh
125 runner.invoke(app) 1abfcdgeh
127 assert vars(excinfo.value) == { 1abfcdgeh
128 "argument_name": "my_param",
129 "param_type": param_info_type,
130 }
133@pytest.mark.parametrize( 1abfcdgeh
134 "param",
135 [
136 typer.Argument,
137 typer.Option,
138 ],
139)
140def test_allow_default_factory_with_default_param(param): 1abfcdgeh
141 def make_string(): 1abfcdgeh
142 return "foo" 1abfcdgeh
144 app = typer.Typer() 1abfcdgeh
146 @app.command() 1abfcdgeh
147 def cmd(my_param: str = param(default_factory=make_string)): 1abfcdgeh
148 print(my_param) 1abfcdgeh
150 result = runner.invoke(app) 1abfcdgeh
151 assert result.exit_code == 0, result.output 1abfcdgeh
152 assert "foo" in result.output 1abfcdgeh
155@pytest.mark.parametrize( 1abfcdgeh
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): 1abfcdgeh
163 def make_string(): 1abfcdgeh
164 return "foo" # pragma: no cover
166 app = typer.Typer() 1abfcdgeh
168 @app.command() 1abfcdgeh
169 def cmd( 1abfcdgeh
170 my_param: str = param("hi", default_factory=make_string), 1abfcdgeh
171 ): ... # pragma: no cover
173 with pytest.raises(DefaultFactoryAndDefaultValueError) as excinfo: 1abfcdgeh
174 runner.invoke(app) 1abfcdgeh
176 assert vars(excinfo.value) == { 1abfcdgeh
177 "argument_name": "my_param",
178 "param_type": param_info_type,
179 }
182@pytest.mark.parametrize( 1abfcdgeh
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): 1abfcdgeh
232 assert str(error) == message 1abfcdgeh