If you don't insist on passing something that looks like a list, but simply want to pass multiple variadic arguments, you can use the multiple option.
From the click documentation
@click.command()
@click.option('--message', '-m', multiple=True)
def commit(message):
click.echo('\n'.join(message))
$ commit -m foo -m bar
foo
bar
Answer from Jarno on Stack OverflowIf you don't insist on passing something that looks like a list, but simply want to pass multiple variadic arguments, you can use the multiple option.
From the click documentation
@click.command()
@click.option('--message', '-m', multiple=True)
def commit(message):
click.echo('\n'.join(message))
$ commit -m foo -m bar
foo
bar
You can coerce click into taking multiple list arguments, if the lists are formatted as a string literals of python lists by using a custom option class like:
Custom Class:
import click
import ast
class PythonLiteralOption(click.Option):
def type_cast_value(self, ctx, value):
try:
return ast.literal_eval(value)
except:
raise click.BadParameter(value)
This class will use Python's Abstract Syntax Tree module to parse the parameter as a python literal.
Custom Class Usage:
To use the custom class, pass the cls parameter to @click.option() decorator like:
@click.option('--option1', cls=PythonLiteralOption, default=[])
How does this work?
This works because click is a well designed OO framework. The @click.option() decorator usually instantiates a click.Option object but allows this behavior to be over ridden with the cls parameter. So it is a relatively easy matter to inherit from click.Option in our own class and over ride the desired methods.
In this case we over ride click.Option.type_cast_value() and then call ast.literal_eval() to parse the list.
Test Code:
@click.command(context_settings=dict(help_option_names=['-h', '--help']))
@click.option('--option1', cls=PythonLiteralOption, default=[])
@click.option('--option2', cls=PythonLiteralOption, default=[])
def cli(option1, option2):
click.echo("Option 1, type: {} value: {}".format(
type(option1), option1))
click.echo("Option 2, type: {} value: {}".format(
type(option2), option2))
# do stuff
if __name__ == '__main__':
import shlex
cli(shlex.split(
'''--option1 '["o11", "o12", "o13"]'
--option2 '["o21", "o22", "o23"]' '''))
Test Results:
Option 1, type: <type 'list'> value: ['o11', 'o12', 'o13']
Option 2, type: <type 'list'> value: ['o21', 'o22', 'o23']
click vs. argparse: Should the 'multiple' parameter be equivalent to 'nargs'?
Option(prompt=True, multiple=True)
python - click.Choice for multiple arguments - Stack Overflow
multiple input types ("either / or") for option in python click - Stack Overflow
I wrote a command line tool using Python's built-in argparse module, but then I heard good things about the Click package, so I thought I would give it a try and play around with it by rewriting my code.
The first difference I noticed is that Click has the multiple=True parameter to allow multiple arguments to be passed, but the way it works seems odd to me.
For example, I have an option that takes up to three values, where the last two must be integers. With argparse I just added nargs='+' and wrote a function for type conversion and error handling.
Example usage:
$ myprog --opt str_value 5 8
However, I can't seem to do this with Click. Here's how I would have to type the command using Click:
$ myprog --opt str_value --opt 5 --opt 8
Is Click really that limited or am I missing something? The documentation says that multiple is similar to nargs, but I don't really see it that way.
Also, what if I wanted to pass multiple files or paths in general? Something like mydir/**/*.txt works fine with argparse because it produces a list object.
Looking for similar functionality with Click, I only found that I could use type=(str, int, int), but then again I can't make the third argument optional, can I?
It would be great to hear some suggestions on how to correctly work with multiple arguments using Click.
Thanks in advance.
You can use multiple with a tweak. Make the multiple=True. Accept the multiple values. And throw error if there are more than one item.
def check_multiple(ctx, param, value):
if len(name) > 1:
click.echo('Multiple options are not accepted!')
ctx.abort()
@cli.command()
@click.option('--name', '-n', required=True, multiple=True, callback=check_multiple, help='Specify the name of the student')
def student(name):
''' '''
Since, I don't have a current setup, the above code is not tested; Derived from the documentation.
Use the kwargs
multiple=True
Running this
import click
@click.group()
@click.version_option("Development", help="Echo version and exit")
@click.help_option(help="Use this on subcommands for more information")
def cli():
"""Add a student/teacher/principal"""
def validate_student(ctx, param, value):
try:
if len(value) > 1:
raise ValueError
else:
return value
except ValueError:
raise click.BadParameter('Students should be not more than 1')
@cli.command()
@click.option('--name', '-n', required=True, multiple=True, help='Specify the name of the student',
callback=validate_student)
def student(name):
"""Add a Student"""
print(name)
if __name__ == "__main__":
student()
Gives me this
$ python3 add.py -n xx -n yy
Usage: add.py [OPTIONS]
Error: Invalid value for "--name" / "-n": Students should be not more than 1
Process finished with exit code 0
But passing only 1 param returns:
$ python3 add.py -n xx
('xx',)
This is documented here: http://click.pocoo.org/5/options/#multi-value-options and here http://click.pocoo.org/5/options/#callbacks-for-validation