click.Choice() doesn't like integers while getting --help.
Given:
click.option(
"--pk",
type=click.Choice([1, 2, 3]),
help="Select the primary key.",
default=1
)
Gives:
```
803, in show_help
echo(ctx.get_help(), color=ctx.color)
File "{...}/lib/python3.5/site-packages/click/core.py", line 495, in get_help
return self.command.get_help(self)
File "{...}/lib/python3.5/site-packages/click/core.py", line 824, in get_help
self.format_help(ctx, formatter)
File "{...}/lib/python3.5/site-packages/click/core.py", line 839, in format_help
self.format_options(ctx, formatter)
File "{...}/lib/python3.5/site-packages/click/core.py", line 853, in format_options
rv = param.get_help_record(ctx)
File "{...}/lib/python3.5/site-packages/click/core.py", line 1602, in get_help_record
rv = [_write_opts(self.opts)]
File "{...}/lib/python3.5/site-packages/click/core.py", line 1599, in _write_opts
rv += ' ' + self.make_metavar()
File "{...}/lib/python3.5/site-packages/click/core.py", line 1292, in make_metavar
metavar = self.type.get_metavar(self)
File "{...}/lib/python3.5/site-packages/click/types.py", line 140, in get_metavar
return '[%s]' % '|'.join(self.choices)
TypeError: sequence item 0: expected str instance, int found
````
Just hit that issue as well.
Seems that most of the functions of class Choice are expecting self.choices as list of strings, but I guess there is no reason why we should not be able to use ints instead.
I am workarounding the issue by using list of strings though and than calling int(foo) when working with the passed value.
@prusnak , @fdavis are you sure that this is bug?
To fix it need to change .join(self.choices) to .join([str(i) for i in self.choices]) here, here and here. But we will have a new problem. List of choices is integer, but we will get value as string from command line. So this means that validation will never be successful(because '1' != 1).
I think you can describe choices as strings and convert type inside command if you need. What do you think?
@cli.command()
@click.option("--pk",
type=click.Choice(['1', '2', '3']),
help="Select the primary key.",
default='1')
def test(pk):
do_something(int(pk))
@d-ganchar Probably not a bug per se, but totally not following the Principle of least surprise.
I know it's possible to create a new Choice type (example), but I think passing ints is quite common, so this might deserve a special treatment. Maybe creating a click.ChoiceInt type derived from click.Choice and with overridden methods?
@prusnak I think that your decision makes sense
While docstring on class Choice says that choices must be a string I agree with @prusnak that its surprising and at the very least we should just enforce the type self.choices in __init__ so it doesn't fail elsewhere
Was this issue resolved?
We are happy to review PRs on this, but I feel that adding a click.ChoiceInt type or similar will add unnecessary complication to the API.
The user is going to be inputting strings from the command line, so I think the default of specifying a choice as a list of strings makes sense.
I am going to close this for now, but feel free to open if someone has an implementation or a PR.
Most helpful comment
Just hit that issue as well.
Seems that most of the functions of
class Choiceare expectingself.choicesas list of strings, but I guess there is no reason why we should not be able to use ints instead.I am workarounding the issue by using list of strings though and than calling int(foo) when working with the passed value.