Skip to content

Commit

Permalink
fix: type conversion for parameters with multiple values
Browse files Browse the repository at this point in the history
1. Tuple parameters: previously, Path and Enum arguments inside a
tuple were included as strings rather than being converted to their
respective types.
2. List parameters: previously, only lists with Path and Enum
main types were properly converted to a list while all other types
of lists were sent into the function as a tuple.
  • Loading branch information
hellowhistler committed Jul 16, 2020
1 parent 73ddcaa commit c14ef6e
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 13 deletions.
4 changes: 2 additions & 2 deletions docs/tutorial/multiple-values/options-with-multiple-values.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ You can set the number of values and types to anything you want, but it has to b

For this, use the standard Python `typing.Tuple`:

```Python hl_lines="1 13"
```Python hl_lines="2 13"
{!../docs_src/multiple_values/options_with_multiple_values/tutorial001.py!}
```

Expand Down Expand Up @@ -47,7 +47,7 @@ is equivalent to this:
```Python
username = user[0]
coins = user[1]
is_wizard = user[2]
magician_type = user[2]
```

!!! tip
Expand Down
44 changes: 33 additions & 11 deletions typer/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,15 @@ def get_command_from_info(command_info: CommandInfo) -> click.Command:
return command


def determine_type_convertor(type_: Any) -> Optional[Callable]:
convertor: Optional[Callable] = None
if lenient_issubclass(type_, Path):
convertor = param_path_convertor
if lenient_issubclass(type_, Enum):
convertor = generate_enum_convertor(type_)
return convertor


def param_path_convertor(value: Optional[str] = None) -> Optional[Path]:
if value is not None:
return Path(value)
Expand All @@ -463,9 +472,23 @@ def convertor(value: Any) -> Any:
return convertor


def generate_iter_convertor(convertor: Callable[[Any], Any]) -> Callable:
def internal_convertor(value: Any) -> List[Any]:
return [convertor(v) for v in value]
def generate_list_convertor(
convertor: Optional[Callable[[Any], Any]]
) -> Callable[[Sequence], List]:
def internal_convertor(value: Sequence) -> List:
return [convertor(v) if convertor else v for v in value]

return internal_convertor


def generate_tuple_convertor(types: Sequence) -> Callable[[Tuple], Tuple]:
convertors = [determine_type_convertor(type_) for type_ in types]

def internal_convertor(param_args: Tuple) -> Tuple:
return tuple(
convertor(arg) if convertor else arg
for (convertor, arg) in zip(convertors, param_args)
)

return internal_convertor

Expand Down Expand Up @@ -621,6 +644,7 @@ def get_click_param(
annotation = str
main_type = annotation
is_list = False
is_tuple = False
parameter_type: Any = None
is_flag = None
origin = getattr(main_type, "__origin__", None)
Expand Down Expand Up @@ -652,18 +676,16 @@ def get_click_param(
get_click_type(annotation=type_, parameter_info=parameter_info)
)
parameter_type = tuple(types)
is_tuple = True
if parameter_type is None:
parameter_type = get_click_type(
annotation=main_type, parameter_info=parameter_info
)
convertor = None
if lenient_issubclass(main_type, Path):
convertor = param_path_convertor
if lenient_issubclass(main_type, Enum):
convertor = generate_enum_convertor(main_type)
if convertor and is_list:
convertor = generate_iter_convertor(convertor)
# TODO: handle recursive conversion for tuples
convertor: Optional[Callable] = determine_type_convertor(main_type)
if is_list:
convertor = generate_list_convertor(convertor)
if is_tuple:
convertor = generate_tuple_convertor(main_type.__args__)
if isinstance(parameter_info, OptionInfo):
if main_type is bool and not (parameter_info.is_flag is False):
is_flag = True
Expand Down

0 comments on commit c14ef6e

Please sign in to comment.