Coverage for tests / test_annotated.py: 100%

64 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-03-26 21:46 +0000

1import sys 1acdefbg

2from pathlib import Path 1acdefbg

3from typing import Annotated 1acdefbg

4 

5import typer 1acdefbg

6from typer.testing import CliRunner 1acdefbg

7 

8runner = CliRunner() 1acdefbg

9 

10 

11def test_annotated_argument_with_default(): 1acdefbg

12 app = typer.Typer() 1acdefbg

13 

14 @app.command() 1acdefbg

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

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

17 

18 result = runner.invoke(app) 1acdefbg

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

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

21 

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

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

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

25 

26 

27def test_annotated_argument_in_string_type_with_default(): 1acdefbg

28 app = typer.Typer() 1acdefbg

29 

30 @app.command() 1acdefbg

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

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

33 

34 result = runner.invoke(app) 1acdefbg

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

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

37 

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

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

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

41 

42 

43def test_annotated_argument_with_default_factory(): 1acdefbg

44 app = typer.Typer() 1acdefbg

45 

46 def make_string(): 1acdefbg

47 return "I made it" 1acdefbg

48 

49 @app.command() 1acdefbg

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

51 print(val) 1acdefbg

52 

53 result = runner.invoke(app) 1acdefbg

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

55 assert "I made it" in result.output 1acdefbg

56 

57 result = runner.invoke(app, ["overridden"]) 1acdefbg

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

59 assert "overridden" in result.output 1acdefbg

60 

61 

62def test_annotated_option_with_argname_doesnt_mutate_multiple_calls(): 1acdefbg

63 app = typer.Typer() 1acdefbg

64 

65 @app.command() 1acdefbg

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

67 if force: 1acdefbg

68 print("Forcing operation") 1acdefbg

69 else: 

70 print("Not forcing") 1acdefbg

71 

72 result = runner.invoke(app) 1acdefbg

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

74 assert "Not forcing" in result.output 1acdefbg

75 

76 result = runner.invoke(app, ["--force"]) 1acdefbg

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

78 assert "Forcing operation" in result.output 1acdefbg

79 

80 

81def test_annotated_custom_path(): 1acdefbg

82 app = typer.Typer() 1acdefbg

83 

84 class CustomPath(Path): 1acdefbg

85 # Subclassing Path was not fully supported before 3.12 

86 # https://docs.python.org/3.12/whatsnew/3.12.html 

87 if sys.version_info < (3, 12): 1acdefbg

88 _flavour = type(Path())._flavour 1ab

89 

90 @app.command() 1acdefbg

91 def custom_parser( 1acdefbg

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

93 ): 

94 assert isinstance(my_path, CustomPath) 1acdefbg

95 

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

97 assert result.exit_code == 0 1acdefbg