Coverage for tests / test_annotated.py: 100%
71 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-11-09 00:15 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2026-11-09 00:15 +0000
1import sys 1acdebfg
2from pathlib import Path 1acdebfg
3from typing import Annotated 1acdebfg
5import pytest 1acdebfg
6import typer 1acdebfg
7from typer.testing import CliRunner 1acdebfg
9runner = CliRunner() 1acdebfg
12def test_annotated_argument_with_default(): 1acdebfg
13 app = typer.Typer() 1acdebfg
15 @app.command() 1acdebfg
16 def cmd(val: Annotated[int, typer.Argument()] = 0): 1acdebfg
17 print(f"hello {val}") 1acdebfg
19 result = runner.invoke(app) 1acdebfg
20 assert result.exit_code == 0, result.output 1acdebfg
21 assert "hello 0" in result.output 1acdebfg
23 result = runner.invoke(app, ["42"]) 1acdebfg
24 assert result.exit_code == 0, result.output 1acdebfg
25 assert "hello 42" in result.output 1acdebfg
28def test_annotated_argument_in_string_type_with_default(): 1acdebfg
29 app = typer.Typer() 1acdebfg
31 @app.command() 1acdebfg
32 def cmd(val: "Annotated[int, typer.Argument()]" = 0): 1acdebfg
33 print(f"hello {val}") 1acdebfg
35 result = runner.invoke(app) 1acdebfg
36 assert result.exit_code == 0, result.output 1acdebfg
37 assert "hello 0" in result.output 1acdebfg
39 result = runner.invoke(app, ["42"]) 1acdebfg
40 assert result.exit_code == 0, result.output 1acdebfg
41 assert "hello 42" in result.output 1acdebfg
44def test_annotated_argument_with_default_factory(): 1acdebfg
45 app = typer.Typer() 1acdebfg
47 def make_string(): 1acdebfg
48 return "I made it" 1acdebfg
50 @app.command() 1acdebfg
51 def cmd(val: Annotated[str, typer.Argument(default_factory=make_string)]): 1acdebfg
52 print(val) 1acdebfg
54 result = runner.invoke(app) 1acdebfg
55 assert result.exit_code == 0, result.output 1acdebfg
56 assert "I made it" in result.output 1acdebfg
58 result = runner.invoke(app, ["overridden"]) 1acdebfg
59 assert result.exit_code == 0, result.output 1acdebfg
60 assert "overridden" in result.output 1acdebfg
63def test_annotated_option_with_argname_doesnt_mutate_multiple_calls(): 1acdebfg
64 app = typer.Typer() 1acdebfg
66 @app.command() 1acdebfg
67 def cmd(force: Annotated[bool, typer.Option("--force")] = False): 1acdebfg
68 if force: 1acdebfg
69 print("Forcing operation") 1acdebfg
70 else:
71 print("Not forcing") 1acdebfg
73 result = runner.invoke(app) 1acdebfg
74 assert result.exit_code == 0, result.output 1acdebfg
75 assert "Not forcing" in result.output 1acdebfg
77 result = runner.invoke(app, ["--force"]) 1acdebfg
78 assert result.exit_code == 0, result.output 1acdebfg
79 assert "Forcing operation" in result.output 1acdebfg
82def test_annotated_custom_path(): 1acdebfg
83 app = typer.Typer() 1acdebfg
85 class CustomPath(Path): 1acdebfg
86 # Subclassing Path was not fully supported before 3.12
87 # https://docs.python.org/3.12/whatsnew/3.12.html
88 if sys.version_info < (3, 12): 1acdebfg
89 _flavour = type(Path())._flavour 1ab
91 @app.command() 1acdebfg
92 def custom_parser( 1acdebfg
93 my_path: Annotated[CustomPath, typer.Argument(parser=CustomPath)],
94 ):
95 assert isinstance(my_path, CustomPath) 1acdebfg
97 result = runner.invoke(app, "/some/quirky/path/implementation") 1acdebfg
98 assert result.exit_code == 0 1acdebfg
101def test_annotated_option_invalid(): 1acdebfg
102 app = typer.Typer() 1acdebfg
104 @app.command() 1acdebfg
105 def cmd(value: Annotated[str, typer.Option(..., "foo-bar")]): 1acdebfg
106 print(value) # pragma: no cover
108 with pytest.raises(ValueError, match="Invalid start character for option"): 1acdebfg
109 runner.invoke(app, ["--help"], catch_exceptions=False) 1acdebfg