Coverage for tests/test_type_conversion.py: 100%

86 statements  

« prev     ^ index     » next       coverage.py v7.5.4, created at 2024-06-24 00:17 +0000

1from enum import Enum 1habcdefg

2from pathlib import Path 1habcdefg

3from typing import Any, List, Optional, Tuple 1habcdefg

4 

5import click 1habcdefg

6import pytest 1habcdefg

7import typer 1habcdefg

8from typer.testing import CliRunner 1habcdefg

9 

10runner = CliRunner() 1habcdefg

11 

12 

13def test_optional(): 1habcdefg

14 app = typer.Typer() 1habcdefg

15 

16 @app.command() 1habcdefg

17 def opt(user: Optional[str] = None): 1habcdefg

18 if user: 1habcdefg

19 print(f"User: {user}") 1habcdefg

20 else: 

21 print("No user") 1habcdefg

22 

23 result = runner.invoke(app) 1habcdefg

24 assert result.exit_code == 0 1habcdefg

25 assert "No user" in result.output 1habcdefg

26 

27 result = runner.invoke(app, ["--user", "Camila"]) 1habcdefg

28 assert result.exit_code == 0 1habcdefg

29 assert "User: Camila" in result.output 1habcdefg

30 

31 

32def test_optional_tuple(): 1habcdefg

33 app = typer.Typer() 1habcdefg

34 

35 @app.command() 1habcdefg

36 def opt(number: Optional[Tuple[int, int]] = None): 1habcdefg

37 if number: 1habcdefg

38 print(f"Number: {number}") 1habcdefg

39 else: 

40 print("No number") 1habcdefg

41 

42 result = runner.invoke(app) 1habcdefg

43 assert result.exit_code == 0 1habcdefg

44 assert "No number" in result.output 1habcdefg

45 

46 result = runner.invoke(app, ["--number", "4", "2"]) 1habcdefg

47 assert result.exit_code == 0 1habcdefg

48 assert "Number: (4, 2)" in result.output 1habcdefg

49 

50 

51def test_no_type(): 1habcdefg

52 app = typer.Typer() 1habcdefg

53 

54 @app.command() 1habcdefg

55 def no_type(user): 1abcdefg

56 print(f"User: {user}") 1habcdefg

57 

58 result = runner.invoke(app, ["Camila"]) 1habcdefg

59 assert result.exit_code == 0 1habcdefg

60 assert "User: Camila" in result.output 1habcdefg

61 

62 

63class SomeEnum(Enum): 1habcdefg

64 ONE = "one" 1habcdefg

65 TWO = "two" 1habcdefg

66 THREE = "three" 1habcdefg

67 

68 

69@pytest.mark.parametrize( 1habcdefg

70 "type_annotation", 

71 [List[Path], List[SomeEnum], List[str]], 

72) 

73def test_list_parameters_convert_to_lists(type_annotation): 1abcdefg

74 # Lists containing objects that are converted by Click (i.e. not Path or Enum) 

75 # should not be inadvertently converted to tuples 

76 expected_element_type = type_annotation.__args__[0] 1habcdefg

77 app = typer.Typer() 1habcdefg

78 

79 @app.command() 1habcdefg

80 def list_conversion(container: type_annotation): 1habcdefg

81 assert isinstance(container, list) 1habcdefg

82 for element in container: 1habcdefg

83 assert isinstance(element, expected_element_type) 1habcdefg

84 

85 result = runner.invoke(app, ["one", "two", "three"]) 1habcdefg

86 assert result.exit_code == 0 1habcdefg

87 

88 

89@pytest.mark.parametrize( 1habcdefg

90 "type_annotation", 

91 [ 

92 Tuple[str, str], 

93 Tuple[str, Path], 

94 Tuple[Path, Path], 

95 Tuple[str, SomeEnum], 

96 Tuple[SomeEnum, SomeEnum], 

97 ], 

98) 

99def test_tuple_parameter_elements_are_converted_recursively(type_annotation): 1abcdefg

100 # Tuple elements that aren't converted by Click (i.e. Path or Enum) 

101 # should be recursively converted by Typer 

102 expected_element_types = type_annotation.__args__ 1habcdefg

103 app = typer.Typer() 1habcdefg

104 

105 @app.command() 1habcdefg

106 def tuple_recursive_conversion(container: type_annotation): 1habcdefg

107 assert isinstance(container, tuple) 1habcdefg

108 for element, expected_type in zip(container, expected_element_types): 1habcdefg

109 assert isinstance(element, expected_type) 1habcdefg

110 

111 result = runner.invoke(app, ["one", "two"]) 1habcdefg

112 assert result.exit_code == 0 1habcdefg

113 

114 

115def test_custom_parse(): 1habcdefg

116 app = typer.Typer() 1habcdefg

117 

118 @app.command() 1habcdefg

119 def custom_parser( 1abcdefg

120 hex_value: int = typer.Argument(None, parser=lambda x: int(x, 0)), 

121 ): 

122 assert hex_value == 0x56 1habcdefg

123 

124 result = runner.invoke(app, ["0x56"]) 1habcdefg

125 assert result.exit_code == 0 1habcdefg

126 

127 

128def test_custom_click_type(): 1habcdefg

129 class BaseNumberParamType(click.ParamType): 1habcdefg

130 name = "base_integer" 1habcdefg

131 

132 def convert( 1abcdefg

133 self, 

134 value: Any, 

135 param: Optional[click.Parameter], 

136 ctx: Optional[click.Context], 

137 ) -> Any: 

138 return int(value, 0) 1habcdefg

139 

140 app = typer.Typer() 1habcdefg

141 

142 @app.command() 1habcdefg

143 def custom_click_type( 1abcdefg

144 hex_value: int = typer.Argument(None, click_type=BaseNumberParamType()), 

145 ): 

146 assert hex_value == 0x56 1habcdefg

147 

148 result = runner.invoke(app, ["0x56"]) 1habcdefg

149 assert result.exit_code == 0 1habcdefg