I'm using the Python Click library for my command-line interface. I'd like to have a command that takes multiple key value pairs. I'm flexible on the api. For example
my_cli my_command FOO=1 BAR=2
or maybe
my_cli my_command FOO 1 BAR 2
or even
my_cli my_command {"FOO": 1, "BAR": 2}
Is there an easy way to do this with Click?
The simplest solution is basically the same thing you'd do with a regular Python function where you wanted an API like this.
Take a single parameter that groups the variable-length stream of arguments into a tuple. Then, what you do depends on whether you want separate arguments:
>>> def func(*args):
... d = dict(zip(args[::2], args[1::2]))
... print(d)
>>> func('FOO', 1, 'BAR', 2)
{'FOO': 1, 'BAR': 2}
… or combined key:value
arguments:
>>> def func(*args):
... d = dict(arg.split(':') for arg in args)
... print(d)
This one is a bit hacky to use, because in Python, arguments aren't just space-separated words, but bear with me on that:
>>> func('FOO:1', 'BAR:2')
{'FOO': 1, 'BAR': 2}
The click
equivalent for the first looks like this:
@click.command()
@click.argument('args', nargs=-1)
def my_command(args):
d = dict(zip(args[::2], args[1::2]))
click.echo(d)
(Obviously you can stick that in a click.group
, etc., just like any other command.)
And now:
$ ./clicky.py FOO 1 BAR 2
{'FOO': 1, 'BAR': 2}
And the second looks like this:
@click.command()
@click.argument('args', nargs=-1)
def my_command(args):
d = dict(arg.split(':') for arg in args)
click.echo(d)
And notice that now, using it is not hacky at all, because to your shell, arguments are just words separated by spaces:
$ ./clicky.py FOO:1 BAR:2
{'FOO': 1, 'BAR': 2}
What if you want to handle both KEY=VALUE
and KEY:VALUE
? Then you just have to write something slightly more complicated than arg.split(':')
. And you'll probably want some better error handling too. But that should be enough to get you started.