The simplest solution for single arguments or options seems to be to just create a variable ARG = click.option(...) and then use @ARG as a decorator of the command function.

Applying multiple opts/args at once involves creating a decorator factory or a callable class. This is the cleanest solution I found, credits go to @altendky and @SnoopJ from #python channel on Libera IRC:

from typing import List, Callable

import attr
import click

from click.decorators import FC

options = [
    click.option("-a"),
    click.option("-b"),
]


# Solution 1: decorator factory
def multi_decorator(options: List[Callable[[FC], FC]]) -> Callable[[FC], FC]:
    def decorator(f: FC) -> FC:
        for option in reversed(options):
            f = option(f)

        return f

    return decorator


@click.command()
@multi_decorator(options)
def cmd1(a, b):
    pass


# Solution 2: callable class
@attr.s
class MultiDecorator:
    options: List[Callable[[FC], FC]] = attr.ib(factory=list)

    def __call__(self, f: FC):
        for option in reversed(self.options):
            f = option(f)

        return f


@click.command()
@MultiDecorator(options)
def cmd2(a, b):
    pass


if __name__ == "__main__":
    from click.testing import CliRunner
    runner = CliRunner()

    print(runner.invoke(cmd1, ["--help"]).output)
    print(runner.invoke(cmd2, ["--help"]).output)

If you want to test or improve this, head over to https://replit.com/@ldericher/multidecorator.

Answer from LDericher on Stack Overflow
๐ŸŒ
Click Documentation
click.palletsprojects.com โ€บ en โ€บ stable โ€บ arguments
Arguments โ€” Click Documentation (8.5.x)
Arguments can use environment variables. To do so, pass the name(s) of the environment variable(s) via envvar in click.argument.
๐ŸŒ
Click Documentation
click.palletsprojects.com โ€บ en โ€บ stable โ€บ parameters
Parameters โ€” Click Documentation (8.5.x)
In the example, the argumentโ€™s name is filename. The name must match the python arg name. To provide a different name for use in help text, see Placeholder / Meta Variable. The optionโ€™s names are -t and --times. More names are available for options and are covered in Options. @click.command() @click.argument('filename') @click.option('-t', '--times', type=int) def multi_echo(filename, times): """Print value filename multiple times.""" for x in range(times): click.echo(filename)
Discussions

python - Click command default arguments and/or options - Stack Overflow
The simplest solution for single arguments or options seems to be to just create a variable ARG = click.option(...) and then use @ARG as a decorator of the command function. Applying multiple opts/args at once involves creating a decorator factory or a callable class. This is the cleanest solution I found, credits go to @altendky and @SnoopJ from #python ... More on stackoverflow.com
๐ŸŒ stackoverflow.com
Replacing @click.option with @click.argument in Python - Stack Overflow
I have the following script where I utilize @click.option from the click library in Python. I want to know how to replace it with @click.argument without changing the output. import sys import click More on stackoverflow.com
๐ŸŒ stackoverflow.com
python - How to pass several list of arguments to @click.option - Stack Overflow
I want to call a python script through the command line with this kind of parameter (list could be any size, eg with 3): python test.py --option1 ["o11", "o12", "o13"] --option2 ["o21", "o22", "o2... More on stackoverflow.com
๐ŸŒ stackoverflow.com
command line interface - Optional CLI arguments with python Click library option - Stack Overflow
I'm having a conundrum with the Python Click library when parsing some CLI options. I would like an option to act as a flag by itself, but optionally accept string values. E.g.: $ myscript โ‡’ optio... More on stackoverflow.com
๐ŸŒ stackoverflow.com
๐ŸŒ
ZetCode
zetcode.com โ€บ python โ€บ click
Python click - creating command line interfaces
January 29, 2024 - The commands are basic building blocks of an application. Click defines commands through decorators. They are created with the click.command decorator. Values are passed to the commands via options or arguments. Options are added with the click.option decorator, arguments with the click.argument.
๐ŸŒ
Real Python
realpython.com โ€บ python-click
Click and Python: Build Extensible and Composable CLI Apps โ€“ Real Python
March 18, 2026 - How can you emulate this behavior using Click and Python? The @click.argument decorator takes an argument called nargs that allows you to predefine the number of values that an argument can accept at the command line.
๐ŸŒ
Digitalk
digitalk.tech โ€บ blog โ€บ python-click-or-how-to-write-professional-cli-applications
Mndwrk Blog - Python click or how to write professional CLI applications
June 17, 2022 - The order in which they are given does not matter, while arguments are given in a sequence after options have been processed. Take the following example: $ python3 myscript.py --option opt1 -s opt2 arg1 arg2 arg3 ยท In this case --option opt1 and -s opt2 are the options and are defined by the previously demonstrated @click.option decorators, while arg1, arg2, and arg3 are arguments that are defined with @click.argument.
Find elsewhere
๐ŸŒ
Readthedocs
pocoo-click.readthedocs.io โ€บ en โ€บ latest โ€บ arguments
Arguments - the Click Documentation
Arguments work similarly to options but are positional. They also only support a subset of the features of options due to their syntactical nature. Click will also not attempt to document arguments for you and wants you to document them manually in order to avoid ugly help pages.
Top answer
1 of 2
1
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()
2 of 2
0

Instead of a named option @click.option('--filename') you can use the argument decorator and specifying:

  1. the parameter-name of your function like here filename and
  2. 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.

Top answer
1 of 6
93

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
2 of 6
41

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 Documentation
click.palletsprojects.com โ€บ en โ€บ stable โ€บ documentation
Help Pages โ€” Click Documentation (8.5.x)
Positional arguments: NAME The name to print. Options: --count INTEGER number of greetings --help Show this message and exit. For subcommands, a short help snippet is generated. By default, itโ€™s the first sentence of the docstring. If too long, then it will ellipsize what cannot be fit on a single line with .... The short help snippet can also be overridden with short_help: import click @click.group() def cli(): """A simple command line tool.""" @cli.command('init', short_help='init the repo') def init(): """Initializes the repository."""
๐ŸŒ
Snyk
snyk.io โ€บ advisor โ€บ click โ€บ functions โ€บ click.argument
How to use the click.argument function in click | Snyk
@click.argument("file", type=click.Path(exists=True)) @click.argument("namespace", default="default") @click.argument("tag") @click.option("--set", "-s", callback=parse_set, multiple=True) @click.option("--edit", "-e", is_flag=True, help="Edit generated manifest before deploying") @click.option("--timeout", default=300, help="Wait timeout (default 300s, 0 to disable)") @click.pass_context def job(ctx, file, namespace, tag, edit, timeout, **kwargs): exit_status = 0 kwargs["set"].append(("IMAGE_TAG", tag)) manifest = format_yaml(file, namespace, edit=edit, extra=kwargs["set"], print=False) # Modify the name to contain imageTag manifest = list(yaml.load_all(manifest)) found_job = False for item in manifest:
๐ŸŒ
Readthedocs
pymbook.readthedocs.io โ€บ en โ€บ latest โ€บ click.html
Building command line applications with Click โ€” Python for you and me 0.5.beta1 documentation
$ myhello --help Usage: myhello [OPTIONS] This is an example script to learn Click. Options: --verbose Will print verbose messages. -n, --name TEXT Who are you? --password TEXT --help Show this message and exit. $ myhello Password: Repeat for confirmation: Hello World We received hello as password. To learn the full usage of password prompts, read this section. You can also add arguments to your tool.
๐ŸŒ
Click Documentation
click.palletsprojects.com โ€บ en โ€บ stable โ€บ options
Options โ€” Click Documentation (8.5.x)
More formally, Click will try to infer the decorated function argument name as follows: If a positional argument is a valid Python identifier (and thus does not have dashes), it is chosen. If multiple positional arguments are prefixed with --, the first one declared is chosen.
๐ŸŒ
AssemblyAI
assemblyai.com โ€บ blog โ€บ the-definitive-guide-to-python-click
The Definitive Guide to Python Click
July 28, 2021 - Now letโ€™s go through how to use the click.options decorator. This decorator is used when you want to pass optional parameters to your commands. It can be used to pass an argument, or to be passed as a flag.
Top answer
1 of 2
5

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.

2 of 2
5

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!
๐ŸŒ
Better Stack
betterstack.com โ€บ community โ€บ guides โ€บ scaling-python โ€บ click-explained
Creating composable CLIs with click in Python | Better Stack Community
CLI tools are most useful when they can accept user input. Click provides two primary ways to take input: options and arguments.
๐ŸŒ
dbader.org
dbader.org โ€บ blog โ€บ mastering-click-advanced-python-command-line-apps
Mastering Click: Writing Advanced Python Command-Line Apps โ€“ dbader.org
January 2, 2018 - The most obvious ones are the builtin Python types such as str, int, float but Click also provides additional types: Path, File and more. The complete list is available in the section on Parameter Types. Ensuring that an input value is of a specific type is as easy as you can make it. You simply pass the parameter type youโ€™re expecting to the decorator as type argument ...