Coverage for tests/test_annotated.py: 100%
66 statements
« prev ^ index » next coverage.py v7.6.1, created at 2025-12-19 20:29 +0000
« prev ^ index » next coverage.py v7.6.1, created at 2025-12-19 20:29 +0000
1import sys 1acdhefibg
2from pathlib import Path 1acdhefibg
4import typer 1acdhefibg
5from typer.testing import CliRunner 1acdhefibg
6from typing_extensions import Annotated 1acdhefibg
8from .utils import needs_py310 1acdhefibg
10runner = CliRunner() 1acdhefibg
13def test_annotated_argument_with_default(): 1acdhefibg
14 app = typer.Typer() 1acdhefibg
16 @app.command() 1acdhefibg
17 def cmd(val: Annotated[int, typer.Argument()] = 0): 1acdhefibg
18 print(f"hello {val}") 1acdhefibg
20 result = runner.invoke(app) 1acdhefibg
21 assert result.exit_code == 0, result.output 1acdhefibg
22 assert "hello 0" in result.output 1acdhefibg
24 result = runner.invoke(app, ["42"]) 1acdhefibg
25 assert result.exit_code == 0, result.output 1acdhefibg
26 assert "hello 42" in result.output 1acdhefibg
29@needs_py310 1acdhefibg
30def test_annotated_argument_in_string_type_with_default(): 1acdhefibg
31 app = typer.Typer() 1acdefbg
33 @app.command() 1acdefbg
34 def cmd(val: "Annotated[int, typer.Argument()]" = 0): 1acdefbg
35 print(f"hello {val}") 1acdefbg
37 result = runner.invoke(app) 1acdefbg
38 assert result.exit_code == 0, result.output 1acdefbg
39 assert "hello 0" in result.output 1acdefbg
41 result = runner.invoke(app, ["42"]) 1acdefbg
42 assert result.exit_code == 0, result.output 1acdefbg
43 assert "hello 42" in result.output 1acdefbg
46def test_annotated_argument_with_default_factory(): 1acdhefibg
47 app = typer.Typer() 1acdhefibg
49 def make_string(): 1acdhefibg
50 return "I made it" 1acdhefibg
52 @app.command() 1acdhefibg
53 def cmd(val: Annotated[str, typer.Argument(default_factory=make_string)]): 1acdhefibg
54 print(val) 1acdhefibg
56 result = runner.invoke(app) 1acdhefibg
57 assert result.exit_code == 0, result.output 1acdhefibg
58 assert "I made it" in result.output 1acdhefibg
60 result = runner.invoke(app, ["overridden"]) 1acdhefibg
61 assert result.exit_code == 0, result.output 1acdhefibg
62 assert "overridden" in result.output 1acdhefibg
65def test_annotated_option_with_argname_doesnt_mutate_multiple_calls(): 1acdhefibg
66 app = typer.Typer() 1acdhefibg
68 @app.command() 1acdhefibg
69 def cmd(force: Annotated[bool, typer.Option("--force")] = False): 1acdhefibg
70 if force: 1acdhefibg
71 print("Forcing operation") 1acdhefibg
72 else:
73 print("Not forcing") 1acdhefibg
75 result = runner.invoke(app) 1acdhefibg
76 assert result.exit_code == 0, result.output 1acdhefibg
77 assert "Not forcing" in result.output 1acdhefibg
79 result = runner.invoke(app, ["--force"]) 1acdhefibg
80 assert result.exit_code == 0, result.output 1acdhefibg
81 assert "Forcing operation" in result.output 1acdhefibg
84def test_annotated_custom_path(): 1acdhefibg
85 app = typer.Typer() 1acdhefibg
87 class CustomPath(Path): 1acdhefibg
88 # Subclassing Path was not fully supported before 3.12
89 # https://docs.python.org/3.12/whatsnew/3.12.html
90 if sys.version_info < (3, 12): 1acdhefibg
91 _flavour = type(Path())._flavour 1ahib
93 @app.command() 1acdhefibg
94 def custom_parser( 1acdhefibg
95 my_path: Annotated[CustomPath, typer.Argument(parser=CustomPath)],
96 ):
97 assert isinstance(my_path, CustomPath) 1acdhefibg
99 result = runner.invoke(app, "/some/quirky/path/implementation") 1acdhefibg
100 assert result.exit_code == 0 1acdhefibg