Coverage for tests/test_others.py: 100%
149 statements
« prev ^ index » next coverage.py v7.6.1, created at 2025-04-14 00:18 +0000
« prev ^ index » next coverage.py v7.6.1, created at 2025-04-14 00:18 +0000
1import os 1iabcdefgh
2import subprocess 1iabcdefgh
3import sys 1iabcdefgh
4import typing 1iabcdefgh
5from pathlib import Path 1iabcdefgh
6from unittest import mock 1iabcdefgh
8import click 1iabcdefgh
9import pytest 1iabcdefgh
10import shellingham 1iabcdefgh
11import typer 1iabcdefgh
12import typer.completion 1iabcdefgh
13from typer.core import _split_opt 1iabcdefgh
14from typer.main import solve_typer_info_defaults, solve_typer_info_help 1iabcdefgh
15from typer.models import ParameterInfo, TyperInfo 1iabcdefgh
16from typer.testing import CliRunner 1iabcdefgh
18from .utils import requires_completion_permission 1iabcdefgh
20runner = CliRunner() 1iabcdefgh
23def test_help_from_info(): 1iabcdefgh
24 # Mainly for coverage/completeness
25 value = solve_typer_info_help(TyperInfo()) 1iabcdefgh
26 assert value is None 1iabcdefgh
29def test_defaults_from_info(): 1iabcdefgh
30 # Mainly for coverage/completeness
31 value = solve_typer_info_defaults(TyperInfo()) 1iabcdefgh
32 assert value 1iabcdefgh
35def test_too_many_parsers(): 1iabcdefgh
36 def custom_parser(value: str) -> int: 1iabcdefgh
37 return int(value) # pragma: no cover
39 class CustomClickParser(click.ParamType): 1iabcdefgh
40 name = "custom_parser" 1iabcdefgh
42 def convert( 1abcdefgh
43 self,
44 value: str,
45 param: typing.Optional[click.Parameter],
46 ctx: typing.Optional[click.Context],
47 ) -> typing.Any:
48 return int(value) # pragma: no cover
50 expected_error = ( 1abcdefgh
51 "Multiple custom type parsers provided. "
52 "`parser` and `click_type` may not both be provided."
53 )
55 with pytest.raises(ValueError, match=expected_error): 1iabcdefgh
56 ParameterInfo(parser=custom_parser, click_type=CustomClickParser()) 1iabcdefgh
59def test_valid_parser_permutations(): 1iabcdefgh
60 def custom_parser(value: str) -> int: 1iabcdefgh
61 return int(value) # pragma: no cover
63 class CustomClickParser(click.ParamType): 1iabcdefgh
64 name = "custom_parser" 1iabcdefgh
66 def convert( 1abcdefgh
67 self,
68 value: str,
69 param: typing.Optional[click.Parameter],
70 ctx: typing.Optional[click.Context],
71 ) -> typing.Any:
72 return int(value) # pragma: no cover
74 ParameterInfo() 1iabcdefgh
75 ParameterInfo(parser=custom_parser) 1iabcdefgh
76 ParameterInfo(click_type=CustomClickParser()) 1iabcdefgh
79@requires_completion_permission 1iabcdefgh
80def test_install_invalid_shell(): 1abcdefgh
81 app = typer.Typer() 1iabcdefgh
83 @app.command() 1iabcdefgh
84 def main(): 1abcdefgh
85 print("Hello World") 1iabcdefgh
87 with mock.patch.object( 1iabcdefgh
88 shellingham, "detect_shell", return_value=("xshell", "/usr/bin/xshell")
89 ):
90 result = runner.invoke(app, ["--install-completion"]) 1iabcdefgh
91 assert "Shell xshell is not supported." in result.stdout 1iabcdefgh
92 result = runner.invoke(app) 1iabcdefgh
93 assert "Hello World" in result.stdout 1iabcdefgh
96def test_callback_too_many_parameters(): 1iabcdefgh
97 app = typer.Typer() 1iabcdefgh
99 def name_callback(ctx, param, val1, val2): 1iabcdefgh
100 pass # pragma: no cover
102 @app.command() 1iabcdefgh
103 def main(name: str = typer.Option(..., callback=name_callback)): 1iabcdefgh
104 pass # pragma: no cover
106 with pytest.raises(click.ClickException) as exc_info: 1iabcdefgh
107 runner.invoke(app, ["--name", "Camila"]) 1iabcdefgh
108 assert ( 1iabcdefgh
109 exc_info.value.message == "Too many CLI parameter callback function parameters"
110 )
113def test_callback_2_untyped_parameters(): 1iabcdefgh
114 app = typer.Typer() 1iabcdefgh
116 def name_callback(ctx, value): 1iabcdefgh
117 print(f"info name is: {ctx.info_name}") 1iabcdefgh
118 print(f"value is: {value}") 1iabcdefgh
120 @app.command() 1iabcdefgh
121 def main(name: str = typer.Option(..., callback=name_callback)): 1iabcdefgh
122 print("Hello World") 1iabcdefgh
124 result = runner.invoke(app, ["--name", "Camila"]) 1iabcdefgh
125 assert "info name is: main" in result.stdout 1iabcdefgh
126 assert "value is: Camila" in result.stdout 1iabcdefgh
129def test_callback_3_untyped_parameters(): 1iabcdefgh
130 app = typer.Typer() 1iabcdefgh
132 def name_callback(ctx, param, value): 1iabcdefgh
133 print(f"info name is: {ctx.info_name}") 1iabcdefgh
134 print(f"param name is: {param.name}") 1iabcdefgh
135 print(f"value is: {value}") 1iabcdefgh
137 @app.command() 1iabcdefgh
138 def main(name: str = typer.Option(..., callback=name_callback)): 1iabcdefgh
139 print("Hello World") 1iabcdefgh
141 result = runner.invoke(app, ["--name", "Camila"]) 1iabcdefgh
142 assert "info name is: main" in result.stdout 1iabcdefgh
143 assert "param name is: name" in result.stdout 1iabcdefgh
144 assert "value is: Camila" in result.stdout 1iabcdefgh
147def test_completion_argument(): 1iabcdefgh
148 file_path = Path(__file__).parent / "assets/completion_argument.py" 1iabcdefgh
149 result = subprocess.run( 1iabcdefgh
150 [sys.executable, "-m", "coverage", "run", str(file_path), "E"],
151 capture_output=True,
152 encoding="utf-8",
153 env={
154 **os.environ,
155 "_COMPLETION_ARGUMENT.PY_COMPLETE": "complete_zsh",
156 "_TYPER_COMPLETE_ARGS": "completion_argument.py E",
157 "_TYPER_COMPLETE_TESTING": "True",
158 },
159 )
160 assert "Emma" in result.stdout or "_files" in result.stdout 1iabcdefgh
161 assert "ctx: completion_argument" in result.stderr 1iabcdefgh
162 assert "arg is: name" in result.stderr 1iabcdefgh
163 assert "incomplete is: E" in result.stderr 1iabcdefgh
166def test_completion_untyped_parameters(): 1iabcdefgh
167 file_path = Path(__file__).parent / "assets/completion_no_types.py" 1iabcdefgh
168 result = subprocess.run( 1iabcdefgh
169 [sys.executable, "-m", "coverage", "run", str(file_path)],
170 capture_output=True,
171 encoding="utf-8",
172 env={
173 **os.environ,
174 "_COMPLETION_NO_TYPES.PY_COMPLETE": "complete_zsh",
175 "_TYPER_COMPLETE_ARGS": "completion_no_types.py --name Sebastian --name Ca",
176 },
177 )
178 assert "info name is: completion_no_types.py" in result.stderr 1iabcdefgh
179 assert "args is: []" in result.stderr 1iabcdefgh
180 assert "incomplete is: Ca" in result.stderr 1iabcdefgh
181 assert '"Camila":"The reader of books."' in result.stdout 1iabcdefgh
182 assert '"Carlos":"The writer of scripts."' in result.stdout 1iabcdefgh
184 result = subprocess.run( 1iabcdefgh
185 [sys.executable, "-m", "coverage", "run", str(file_path)],
186 capture_output=True,
187 encoding="utf-8",
188 )
189 assert "Hello World" in result.stdout 1iabcdefgh
192def test_completion_untyped_parameters_different_order_correct_names(): 1iabcdefgh
193 file_path = Path(__file__).parent / "assets/completion_no_types_order.py" 1iabcdefgh
194 result = subprocess.run( 1iabcdefgh
195 [sys.executable, "-m", "coverage", "run", str(file_path)],
196 capture_output=True,
197 encoding="utf-8",
198 env={
199 **os.environ,
200 "_COMPLETION_NO_TYPES_ORDER.PY_COMPLETE": "complete_zsh",
201 "_TYPER_COMPLETE_ARGS": "completion_no_types_order.py --name Sebastian --name Ca",
202 },
203 )
204 assert "info name is: completion_no_types_order.py" in result.stderr 1iabcdefgh
205 assert "args is: []" in result.stderr 1iabcdefgh
206 assert "incomplete is: Ca" in result.stderr 1iabcdefgh
207 assert '"Camila":"The reader of books."' in result.stdout 1iabcdefgh
208 assert '"Carlos":"The writer of scripts."' in result.stdout 1iabcdefgh
210 result = subprocess.run( 1iabcdefgh
211 [sys.executable, "-m", "coverage", "run", str(file_path)],
212 capture_output=True,
213 encoding="utf-8",
214 )
215 assert "Hello World" in result.stdout 1iabcdefgh
218def test_autocompletion_too_many_parameters(): 1iabcdefgh
219 app = typer.Typer() 1iabcdefgh
221 def name_callback(ctx, args, incomplete, val2): 1iabcdefgh
222 pass # pragma: no cover
224 @app.command() 1iabcdefgh
225 def main(name: str = typer.Option(..., autocompletion=name_callback)): 1iabcdefgh
226 pass # pragma: no cover
228 with pytest.raises(click.ClickException) as exc_info: 1iabcdefgh
229 runner.invoke(app, ["--name", "Camila"]) 1iabcdefgh
230 assert exc_info.value.message == "Invalid autocompletion callback parameters: val2" 1iabcdefgh
233def test_forward_references(): 1iabcdefgh
234 app = typer.Typer() 1iabcdefgh
236 @app.command() 1iabcdefgh
237 def main(arg1, arg2: int, arg3: "int", arg4: bool = False, arg5: "bool" = False): 1iabcdefgh
238 print(f"arg1: {type(arg1)} {arg1}") 1iabcdefgh
239 print(f"arg2: {type(arg2)} {arg2}") 1iabcdefgh
240 print(f"arg3: {type(arg3)} {arg3}") 1iabcdefgh
241 print(f"arg4: {type(arg4)} {arg4}") 1iabcdefgh
242 print(f"arg5: {type(arg5)} {arg5}") 1iabcdefgh
244 result = runner.invoke(app, ["Hello", "2", "invalid"]) 1iabcdefgh
246 assert "Invalid value for 'ARG3': 'invalid' is not a valid integer" in result.stdout 1iabcdefgh
247 result = runner.invoke(app, ["Hello", "2", "3", "--arg4", "--arg5"]) 1iabcdefgh
248 assert ( 1iabcdefgh
249 "arg1: <class 'str'> Hello\narg2: <class 'int'> 2\narg3: <class 'int'> 3\narg4: <class 'bool'> True\narg5: <class 'bool'> True\n"
250 in result.stdout
251 )
254def test_context_settings_inheritance_single_command(): 1iabcdefgh
255 app = typer.Typer(context_settings={"help_option_names": ["-h", "--help"]}) 1iabcdefgh
257 @app.command() 1iabcdefgh
258 def main(name: str): 1iabcdefgh
259 pass # pragma: no cover
261 result = runner.invoke(app, ["main", "-h"]) 1iabcdefgh
262 assert "Show this message and exit." in result.stdout 1iabcdefgh
265def test_split_opt(): 1iabcdefgh
266 prefix, opt = _split_opt("--verbose") 1iabcdefgh
267 assert prefix == "--" 1iabcdefgh
268 assert opt == "verbose" 1iabcdefgh
270 prefix, opt = _split_opt("//verbose") 1iabcdefgh
271 assert prefix == "//" 1iabcdefgh
272 assert opt == "verbose" 1iabcdefgh
274 prefix, opt = _split_opt("-verbose") 1iabcdefgh
275 assert prefix == "-" 1iabcdefgh
276 assert opt == "verbose" 1iabcdefgh
278 prefix, opt = _split_opt("verbose") 1iabcdefgh
279 assert prefix == "" 1iabcdefgh
280 assert opt == "verbose" 1iabcdefgh