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 Overflowpython - How to pass several list of arguments to @click.option - Stack Overflow
python - Click command default arguments and/or options - Stack Overflow
Replacing @click.option with @click.argument in Python - Stack Overflow
Take arguments after command in click python module - Stack Overflow
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
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']
import sys
import click
import pandas as pd
from sklearn import metrics
@click.command()
# @click.option('--filename')
## @click.argument('result.csv', type=click.Path(exists=True))
@click.argument('filename', type=click.Path(exists=True))
def main(filename):
df = pd.read_csv(filename)
y_test = df["actual"].values
y_pred = df["predicted"].values
print('R Squared: ' + str(metrics.r2_score(y_test, y_pred)))
if __name__ == '__main__':
# sys.argv = ['', '--filename', 'result.csv']
sys.argv = ['', 'result.csv']
main()
Instead of a named option @click.option('--filename') you can use the argument decorator and specifying:
- the parameter-name of your function like here
filenameand - the type of the parameter, e.g. a file-path that is validated for existence:
click.Path(exists=True)
Click Argument for file-paths
You can define File Path Arguments as follows with click:
@click.command()
@click.argument('filename', type=click.Path(exists=True))
def main(filename):
print(f"{filename=}")
df = pd.read_csv(filename)
However this means changing your command-line, e.g. by removing the option '--filename' element from the array:
if __name__ == '__main__':
sys.argv = ['', 'result.csv']
main()
Attribute Error
The runtime error mentioned in a comment to guolei's answer:
AttributeError: 'tuple' object has no attribute 'tb_frame'
is most likely not related to your presented Python script (incl. click, pandas or scikit-learn). It rather comes from your runtime-environment like IPython or Jupyter Notebook. See AttributeError: 'TypeError' object has no attribute 'tb_frame' bug ยท Issue #12877 ยท ipython/ipython.
One way that I have managed to achieve this behaviour was by actually using arguments as below. I'll post this as a workaround, while I try to see if it could be done with an option, and I'll update my post accordingly
@click.command(context_settings={"ignore_unknown_options": True})
@click.argument("options", nargs=-1)
def myscript(options):
option = False
if options is ():
option = False
if '-o' in options or '--option' in options:
option = True
if len(options) > 1:
option = options[1]
print(option)
Later Edit Using an option, I have managed to achieve this by adding an argument to the command definition.
@click.command()
@click.option('-o', '--option', is_flag=True, default=False, is_eager=True)
@click.argument('value', nargs=-1)
def myscript(option, value):
if option and value != ():
option = value[0]
print(option)
The nargs can be removed if you only expect at most one argument to follow, and can be treated as not required.
@click.command()
@click.option('-o', '--option', is_flag=True, default=False, is_eager=True)
@click.argument('value', required=False)
def myscript(option, value=None):
if option and value is not None:
option = value
print(option)
It might also be possible by putting together a context generator and storing some state, but that seems the least desirable solution, since you would be relying on the context storing your state.
FYI, if anybody bumps into this problem in 2024, it seems that click has made something specific for this use case, see: https://click.palletsprojects.com/en/8.1.x/options/#optional-value
Setting is_flag=False, flag_value=value tells Click that the option can still be passed a value, but if only the flag is given the flag_value is used.
@click.command()
@click.option("--name", is_flag=False, flag_value="Flag", default="Default")
def hello(name):
click.echo(f"Hello, {name}!")
>> hello
Hello, Default!
>> hello --name Value
Hello, Value!
>> hello --name
Hello, Flag!