Coverage for tests/test_type_conversion.py: 100%
101 statements
« prev ^ index » next coverage.py v7.6.1, created at 2024-09-09 18:26 +0000
« prev ^ index » next coverage.py v7.6.1, created at 2024-09-09 18:26 +0000
1from enum import Enum 1habfcgde
2from pathlib import Path 1habfcgde
3from typing import Any, List, Optional, Tuple 1habfcgde
5import click 1habfcgde
6import pytest 1habfcgde
7import typer 1habfcgde
8from typer.testing import CliRunner 1habfcgde
10from .utils import needs_py310 1habfcgde
12runner = CliRunner() 1habfcgde
15def test_optional(): 1habfcgde
16 app = typer.Typer() 1habfcgde
18 @app.command() 1habfcgde
19 def opt(user: Optional[str] = None): 1habfcgde
20 if user: 1habfcgde
21 print(f"User: {user}") 1habfcgde
22 else:
23 print("No user") 1habfcgde
25 result = runner.invoke(app) 1habfcgde
26 assert result.exit_code == 0 1habfcgde
27 assert "No user" in result.output 1habfcgde
29 result = runner.invoke(app, ["--user", "Camila"]) 1habfcgde
30 assert result.exit_code == 0 1habfcgde
31 assert "User: Camila" in result.output 1habfcgde
34@needs_py310 1habfcgde
35def test_union_type_optional(): 1abfcgde
36 app = typer.Typer() 1abcde
38 @app.command() 1abcde
39 def opt(user: str | None = None): 1abcde
40 if user: 1abcde
41 print(f"User: {user}") 1abcde
42 else:
43 print("No user") 1abcde
45 result = runner.invoke(app) 1abcde
46 assert result.exit_code == 0 1abcde
47 assert "No user" in result.output 1abcde
49 result = runner.invoke(app, ["--user", "Camila"]) 1abcde
50 assert result.exit_code == 0 1abcde
51 assert "User: Camila" in result.output 1abcde
54def test_optional_tuple(): 1habfcgde
55 app = typer.Typer() 1habfcgde
57 @app.command() 1habfcgde
58 def opt(number: Optional[Tuple[int, int]] = None): 1habfcgde
59 if number: 1habfcgde
60 print(f"Number: {number}") 1habfcgde
61 else:
62 print("No number") 1habfcgde
64 result = runner.invoke(app) 1habfcgde
65 assert result.exit_code == 0 1habfcgde
66 assert "No number" in result.output 1habfcgde
68 result = runner.invoke(app, ["--number", "4", "2"]) 1habfcgde
69 assert result.exit_code == 0 1habfcgde
70 assert "Number: (4, 2)" in result.output 1habfcgde
73def test_no_type(): 1habfcgde
74 app = typer.Typer() 1habfcgde
76 @app.command() 1habfcgde
77 def no_type(user): 1abfcgde
78 print(f"User: {user}") 1habfcgde
80 result = runner.invoke(app, ["Camila"]) 1habfcgde
81 assert result.exit_code == 0 1habfcgde
82 assert "User: Camila" in result.output 1habfcgde
85class SomeEnum(Enum): 1habfcgde
86 ONE = "one" 1habfcgde
87 TWO = "two" 1habfcgde
88 THREE = "three" 1habfcgde
91@pytest.mark.parametrize( 1habfcgde
92 "type_annotation",
93 [List[Path], List[SomeEnum], List[str]],
94)
95def test_list_parameters_convert_to_lists(type_annotation): 1abfcgde
96 # Lists containing objects that are converted by Click (i.e. not Path or Enum)
97 # should not be inadvertently converted to tuples
98 expected_element_type = type_annotation.__args__[0] 1habfcgde
99 app = typer.Typer() 1habfcgde
101 @app.command() 1habfcgde
102 def list_conversion(container: type_annotation): 1habfcgde
103 assert isinstance(container, list) 1habfcgde
104 for element in container: 1habfcgde
105 assert isinstance(element, expected_element_type) 1habfcgde
107 result = runner.invoke(app, ["one", "two", "three"]) 1habfcgde
108 assert result.exit_code == 0 1habfcgde
111@pytest.mark.parametrize( 1habfcgde
112 "type_annotation",
113 [
114 Tuple[str, str],
115 Tuple[str, Path],
116 Tuple[Path, Path],
117 Tuple[str, SomeEnum],
118 Tuple[SomeEnum, SomeEnum],
119 ],
120)
121def test_tuple_parameter_elements_are_converted_recursively(type_annotation): 1abfcgde
122 # Tuple elements that aren't converted by Click (i.e. Path or Enum)
123 # should be recursively converted by Typer
124 expected_element_types = type_annotation.__args__ 1habfcgde
125 app = typer.Typer() 1habfcgde
127 @app.command() 1habfcgde
128 def tuple_recursive_conversion(container: type_annotation): 1habfcgde
129 assert isinstance(container, tuple) 1habfcgde
130 for element, expected_type in zip(container, expected_element_types): 1habfcgde
131 assert isinstance(element, expected_type) 1habfcgde
133 result = runner.invoke(app, ["one", "two"]) 1habfcgde
134 assert result.exit_code == 0 1habfcgde
137def test_custom_parse(): 1habfcgde
138 app = typer.Typer() 1habfcgde
140 @app.command() 1habfcgde
141 def custom_parser( 1abfcgde
142 hex_value: int = typer.Argument(None, parser=lambda x: int(x, 0)),
143 ):
144 assert hex_value == 0x56 1habfcgde
146 result = runner.invoke(app, ["0x56"]) 1habfcgde
147 assert result.exit_code == 0 1habfcgde
150def test_custom_click_type(): 1habfcgde
151 class BaseNumberParamType(click.ParamType): 1habfcgde
152 name = "base_integer" 1habfcgde
154 def convert( 1abfcgde
155 self,
156 value: Any,
157 param: Optional[click.Parameter],
158 ctx: Optional[click.Context],
159 ) -> Any:
160 return int(value, 0) 1habfcgde
162 app = typer.Typer() 1habfcgde
164 @app.command() 1habfcgde
165 def custom_click_type( 1abfcgde
166 hex_value: int = typer.Argument(None, click_type=BaseNumberParamType()),
167 ):
168 assert hex_value == 0x56 1habfcgde
170 result = runner.invoke(app, ["0x56"]) 1habfcgde
171 assert result.exit_code == 0 1habfcgde