diff --git a/tests/test_type_conversion.py b/tests/test_type_conversion.py index 21ad57436c..51e83314de 100644 --- a/tests/test_type_conversion.py +++ b/tests/test_type_conversion.py @@ -29,6 +29,25 @@ def opt(user: Optional[str] = None): assert "User: Camila" in result.output +def test_optional_tuple(): + app = typer.Typer() + + @app.command() + def opt(number: Optional[Tuple[int, int]] = None): + if number: + print(f"Number: {number}") + else: + print("No number") + + result = runner.invoke(app) + assert result.exit_code == 0 + assert "No number" in result.output + + result = runner.invoke(app, ["--number", "4", "2"]) + assert result.exit_code == 0 + assert "Number: (4, 2)" in result.output + + def test_no_type(): app = typer.Typer() diff --git a/typer/main.py b/typer/main.py index ccf9f0956c..9db26975ca 100644 --- a/typer/main.py +++ b/typer/main.py @@ -645,10 +645,14 @@ def internal_convertor(value: Sequence[Any]) -> Optional[List[Any]]: def generate_tuple_convertor( types: Sequence[Any], -) -> Callable[[Tuple[Any, ...]], Tuple[Any, ...]]: +) -> Callable[[Optional[Tuple[Any, ...]]], Optional[Tuple[Any, ...]]]: convertors = [determine_type_convertor(type_) for type_ in types] - def internal_convertor(param_args: Tuple[Any, ...]) -> Tuple[Any, ...]: + def internal_convertor( + param_args: Optional[Tuple[Any, ...]], + ) -> Optional[Tuple[Any, ...]]: + if param_args is None: + return None return tuple( convertor(arg) if convertor else arg for (convertor, arg) in zip(convertors, param_args)