Skip to content

Commit

Permalink
🚨 Get full test coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
MatteoBouvier committed Nov 26, 2024
1 parent 9e7afc3 commit 37b74b4
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 91 deletions.
66 changes: 0 additions & 66 deletions tests/test_annotated.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
from enum import Enum
from typing import Union

import typer
from typer.testing import CliRunner
from typing_extensions import Annotated
Expand Down Expand Up @@ -79,66 +76,3 @@ def cmd(force: Annotated[bool, typer.Option("--force")] = False):
result = runner.invoke(app, ["--force"])
assert result.exit_code == 0, result.output
assert "Forcing operation" in result.output


class TestAnnotatedOptionAcceptsOptionalValue:
def test_enum(self):
app = typer.Typer()

class OptEnum(str, Enum):
val1 = "val1"
val2 = "val2"

@app.command()
def cmd(opt: Annotated[Union[bool, OptEnum], typer.Option()] = OptEnum.val1):
if opt is False:
print("False")
elif opt is True:
print("True")
else:
print(opt.value)

result = runner.invoke(app)
assert result.exit_code == 0, result.output
assert "False" in result.output

result = runner.invoke(app, ["--opt"])
assert result.exit_code == 0, result.output
assert "val1" in result.output

result = runner.invoke(app, ["--opt", "val1"])
assert result.exit_code == 0, result.output
assert "val1" in result.output

result = runner.invoke(app, ["--opt", "val2"])
assert result.exit_code == 0, result.output
assert "val2" in result.output

result = runner.invoke(app, ["--opt", "val3"])
assert result.exit_code != 0
assert "Invalid value for '--opt': 'val3' is not one of" in result.output

def test_int(self):
app = typer.Typer()

@app.command()
def cmd(opt: Annotated[Union[bool, int], typer.Option()] = 1):
print(opt)

result = runner.invoke(app)
assert result.exit_code == 0, result.output
assert "False" in result.output

result = runner.invoke(app, ["--opt"])
assert result.exit_code == 0, result.output
assert "1" in result.output

result = runner.invoke(app, ["--opt", "2"])
assert result.exit_code == 0, result.output
assert "2" in result.output

result = runner.invoke(app, ["--opt", "test"])
assert result.exit_code != 0
assert (
"Invalid value for '--opt': 'test' is not a valid integer" in result.output
)
101 changes: 100 additions & 1 deletion tests/test_type_conversion.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
from enum import Enum
from pathlib import Path
from typing import Any, List, Optional, Tuple
from typing import Any, List, Optional, Tuple, Union

import click
import pytest
import typer
from typer.testing import CliRunner
from typing_extensions import Annotated

from .utils import needs_py310

Expand Down Expand Up @@ -169,3 +170,101 @@ def custom_click_type(

result = runner.invoke(app, ["0x56"])
assert result.exit_code == 0


class TestOptionAcceptsOptionalValue:
def test_enum(self):
app = typer.Typer()

class OptEnum(str, Enum):
val1 = "val1"
val2 = "val2"

@app.command()
def cmd(opt: Annotated[Union[bool, OptEnum], typer.Option()] = OptEnum.val1):
if opt is False:
print("False")

else:
print(opt.value)

result = runner.invoke(app)
assert result.exit_code == 0, result.output
assert "False" in result.output

result = runner.invoke(app, ["--opt"])
assert result.exit_code == 0, result.output
assert "val1" in result.output

result = runner.invoke(app, ["--opt", "val1"])
assert result.exit_code == 0, result.output
assert "val1" in result.output

result = runner.invoke(app, ["--opt", "val2"])
assert result.exit_code == 0, result.output
assert "val2" in result.output

result = runner.invoke(app, ["--opt", "val3"])
assert result.exit_code != 0
assert "Invalid value for '--opt': 'val3' is not one of" in result.output

result = runner.invoke(app, ["--opt", "0"])
assert result.exit_code == 0, result.output
assert "False" in result.output

result = runner.invoke(app, ["--opt", "1"])
assert result.exit_code == 0, result.output
assert "val1" in result.output

def test_int(self):
app = typer.Typer()

@app.command()
def cmd(opt: Annotated[Union[bool, int], typer.Option()] = 1):
print(opt)

result = runner.invoke(app)
assert result.exit_code == 0, result.output
assert "False" in result.output

result = runner.invoke(app, ["--opt"])
assert result.exit_code == 0, result.output
assert "1" in result.output

result = runner.invoke(app, ["--opt", "2"])
assert result.exit_code == 0, result.output
assert "2" in result.output

result = runner.invoke(app, ["--opt", "test"])
assert result.exit_code != 0
assert (
"Invalid value for '--opt': 'test' is not a valid integer" in result.output
)

result = runner.invoke(app, ["--opt", "true"])
assert result.exit_code == 0, result.output
assert "1" in result.output

result = runner.invoke(app, ["--opt", "off"])
assert result.exit_code == 0, result.output
assert "False" in result.output

def test_path(self):
app = typer.Typer()

@app.command()
def cmd(opt: Annotated[Union[bool, Path], typer.Option()] = Path(".")):
if isinstance(opt, Path):
print((opt / "file.py").as_posix())

result = runner.invoke(app, ["--opt"])
assert result.exit_code == 0, result.output
assert "file.py" in result.output

result = runner.invoke(app, ["--opt", "/test/path/file.py"])
assert result.exit_code == 0, result.output
assert "/test/path/file.py" in result.output

result = runner.invoke(app, ["--opt", "False"])
assert result.exit_code == 0, result.output
assert "file.py" not in result.output
31 changes: 7 additions & 24 deletions typer/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -660,19 +660,21 @@ def generate_enum_convertor(
) -> Callable[[Any], Union[None, bool, Enum]]:
val_map = {str(val.value): val for val in enum}

def convertor(value: Any) -> Union[None, bool, Enum]:
if value is None:
return None

def convertor(value: Any) -> Union[bool, Enum]:
if isinstance(value, bool) and skip_bool:
return value

if isinstance(value, enum):
return value

val = str(value)
if val in val_map:
key = val_map[val]
return enum(key)

return None
raise click.BadParameter(
f"Invalid value '{value}' for enum '{enum.__name__}'"
) # pragma: no cover

return convertor

Expand Down Expand Up @@ -845,22 +847,6 @@ def __init__(self, type_: click.ParamType, default: Any) -> None:
self._default: Any = default
self.name: str = f"BOOLEAN|{type_.name}"

@override
def __repr__(self) -> str:
return f"DefaultOption({self._type})"

@override
def to_info_dict(self) -> Dict[str, Any]:
return self._type.to_info_dict()

@override
def get_metavar(self, param: click.Parameter) -> Optional[str]:
return self._type.get_metavar(param)

@override
def get_missing_message(self, param: click.Parameter) -> Optional[str]:
return self._type.get_missing_message(param)

@override
def convert(
self, value: Any, param: Optional[click.Parameter], ctx: Optional[click.Context]
Expand Down Expand Up @@ -895,9 +881,6 @@ class DefaultFalse:
def __init__(self, value: Any) -> None:
self._value = value

def __repr__(self) -> str:
return f"False ({repr(self._value)})"

def __str__(self) -> str:
return f"False ({str(self._value)})"

Expand Down

0 comments on commit 37b74b4

Please sign in to comment.