Coverage for tests / test_annotated.py: 100%

66 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-02-09 12:36 +0000

1import sys 1acdhefbg

2from pathlib import Path 1acdhefbg

3from typing import Annotated 1acdhefbg

4 

5import typer 1acdhefbg

6from typer.testing import CliRunner 1acdhefbg

7 

8from .utils import needs_py310 1acdhefbg

9 

10runner = CliRunner() 1acdhefbg

11 

12 

13def test_annotated_argument_with_default(): 1acdhefbg

14 app = typer.Typer() 1acdhefbg

15 

16 @app.command() 1acdhefbg

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

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

19 

20 result = runner.invoke(app) 1acdhefbg

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

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

23 

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

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

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

27 

28 

29@needs_py310 1acdhefbg

30def test_annotated_argument_in_string_type_with_default(): 1acdhefbg

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(): 1acdhefbg

47 app = typer.Typer() 1acdhefbg

48 

49 def make_string(): 1acdhefbg

50 return "I made it" 1acdhefbg

51 

52 @app.command() 1acdhefbg

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

54 print(val) 1acdhefbg

55 

56 result = runner.invoke(app) 1acdhefbg

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

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

59 

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

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

62 assert "overridden" in result.output 1acdhefbg

63 

64 

65def test_annotated_option_with_argname_doesnt_mutate_multiple_calls(): 1acdhefbg

66 app = typer.Typer() 1acdhefbg

67 

68 @app.command() 1acdhefbg

69 def cmd(force: Annotated[bool, typer.Option("--force")] = False): 1acdhefbg

70 if force: 1acdhefbg

71 print("Forcing operation") 1acdhefbg

72 else: 

73 print("Not forcing") 1acdhefbg

74 

75 result = runner.invoke(app) 1acdhefbg

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

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

78 

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

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

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

82 

83 

84def test_annotated_custom_path(): 1acdhefbg

85 app = typer.Typer() 1acdhefbg

86 

87 class CustomPath(Path): 1acdhefbg

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): 1acdhefbg

91 _flavour = type(Path())._flavour 1ahb

92 

93 @app.command() 1acdhefbg

94 def custom_parser( 1acdhefbg

95 my_path: Annotated[CustomPath, typer.Argument(parser=CustomPath)], 

96 ): 

97 assert isinstance(my_path, CustomPath) 1acdhefbg

98 

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

100 assert result.exit_code == 0 1acdhefbg