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

1import sys 1acdhefibg

2from pathlib import Path 1acdhefibg

3 

4import typer 1acdhefibg

5from typer.testing import CliRunner 1acdhefibg

6from typing_extensions import Annotated 1acdhefibg

7 

8from .utils import needs_py310 1acdhefibg

9 

10runner = CliRunner() 1acdhefibg

11 

12 

13def test_annotated_argument_with_default(): 1acdhefibg

14 app = typer.Typer() 1acdhefibg

15 

16 @app.command() 1acdhefibg

17 def cmd(val: Annotated[int, typer.Argument()] = 0): 1acdhefibg

18 print(f"hello {val}") 1acdhefibg

19 

20 result = runner.invoke(app) 1acdhefibg

21 assert result.exit_code == 0, result.output 1acdhefibg

22 assert "hello 0" in result.output 1acdhefibg

23 

24 result = runner.invoke(app, ["42"]) 1acdhefibg

25 assert result.exit_code == 0, result.output 1acdhefibg

26 assert "hello 42" in result.output 1acdhefibg

27 

28 

29@needs_py310 1acdhefibg

30def test_annotated_argument_in_string_type_with_default(): 1acdhefibg

31 app = typer.Typer() 1acdefbg

32 

33 @app.command() 1acdefbg

34 def cmd(val: "Annotated[int, typer.Argument()]" = 0): 1acdefbg

35 print(f"hello {val}") 1acdefbg

36 

37 result = runner.invoke(app) 1acdefbg

38 assert result.exit_code == 0, result.output 1acdefbg

39 assert "hello 0" in result.output 1acdefbg

40 

41 result = runner.invoke(app, ["42"]) 1acdefbg

42 assert result.exit_code == 0, result.output 1acdefbg

43 assert "hello 42" in result.output 1acdefbg

44 

45 

46def test_annotated_argument_with_default_factory(): 1acdhefibg

47 app = typer.Typer() 1acdhefibg

48 

49 def make_string(): 1acdhefibg

50 return "I made it" 1acdhefibg

51 

52 @app.command() 1acdhefibg

53 def cmd(val: Annotated[str, typer.Argument(default_factory=make_string)]): 1acdhefibg

54 print(val) 1acdhefibg

55 

56 result = runner.invoke(app) 1acdhefibg

57 assert result.exit_code == 0, result.output 1acdhefibg

58 assert "I made it" in result.output 1acdhefibg

59 

60 result = runner.invoke(app, ["overridden"]) 1acdhefibg

61 assert result.exit_code == 0, result.output 1acdhefibg

62 assert "overridden" in result.output 1acdhefibg

63 

64 

65def test_annotated_option_with_argname_doesnt_mutate_multiple_calls(): 1acdhefibg

66 app = typer.Typer() 1acdhefibg

67 

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

74 

75 result = runner.invoke(app) 1acdhefibg

76 assert result.exit_code == 0, result.output 1acdhefibg

77 assert "Not forcing" in result.output 1acdhefibg

78 

79 result = runner.invoke(app, ["--force"]) 1acdhefibg

80 assert result.exit_code == 0, result.output 1acdhefibg

81 assert "Forcing operation" in result.output 1acdhefibg

82 

83 

84def test_annotated_custom_path(): 1acdhefibg

85 app = typer.Typer() 1acdhefibg

86 

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

92 

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

98 

99 result = runner.invoke(app, "/some/quirky/path/implementation") 1acdhefibg

100 assert result.exit_code == 0 1acdhefibg