diff --git a/tests/test_others.py b/tests/test_others.py index 1078e63d1f..f5e409214a 100644 --- a/tests/test_others.py +++ b/tests/test_others.py @@ -144,6 +144,32 @@ def main(name: str = typer.Option(..., callback=name_callback)): assert "value is: Camila" in result.stdout +def test_callback_4_list_none(): + app = typer.Typer() + + def names_callback(ctx, param, values: typing.Optional[typing.List[str]]): + if values is None: + return values + return [value.upper() for value in values] + + @app.command() + def main( + names: typing.Optional[typing.List[str]] = typer.Option( + None, "--name", callback=names_callback + ), + ): + if names is None: + print("Hello World") + else: + print(f"Hello {', '.join(names)}") + + result = runner.invoke(app, ["--name", "Sideshow", "--name", "Bob"]) + assert "Hello SIDESHOW, BOB" in result.stdout + + result = runner.invoke(app, []) + assert "Hello World" in result.stdout + + def test_completion_argument(): file_path = Path(__file__).parent / "assets/completion_argument.py" result = subprocess.run( diff --git a/typer/main.py b/typer/main.py index 36737e49ef..82a7c3a7bb 100644 --- a/typer/main.py +++ b/typer/main.py @@ -640,10 +640,10 @@ def convertor(value: Any) -> Any: def generate_list_convertor( convertor: Optional[Callable[[Any], Any]], default_value: Optional[Any] -) -> Callable[[Sequence[Any]], Optional[List[Any]]]: - def internal_convertor(value: Sequence[Any]) -> Optional[List[Any]]: - if default_value is None and len(value) == 0: - return None +) -> Callable[[Optional[Sequence[Any]]], Optional[List[Any]]]: + def internal_convertor(value: Optional[Sequence[Any]]) -> Optional[List[Any]]: + if value is None or len(value) == 0: + return default_value return [convertor(v) if convertor else v for v in value] return internal_convertor