I'm trying to generate click
commands from a configuration file. Essentially, this pattern:
import click
@click.group()
def main():
pass
commands = ['foo', 'bar', 'baz']
for c in commands:
def _f():
print("I am the '{}' command".format(c))
_f = main.command(name=c)(_f)
if __name__ == '__main__':
main()
The --help
text looks good:
Usage: test_click.py [OPTIONS] COMMAND [ARGS]...
Options:
--help Show this message and exit.
Commands:
bar
baz
foo
However all of the commands seem to route to the last one that gets generated:
$ ./test_click.py foo
I am the 'baz' command
$ ./test_click.py bar
I am the 'baz' command
$ ./test_click.py baz
I am the 'baz' command
Is what I'm attempting actually possible?
The problem is with functional scope of python. When you create the function _f
it is using c
which has a higher scope than the function. So it will not be retained when you invoke the function. (_f
is invoked when you call main)
By the time you invoke main
the loop is complete and c will always hold the last value of the loop - which is baz
The solution is to attach the value c
to function using its own scope.
import click
@click.group()
def main():
pass
commands = ['foo', 'bar', 'baz']
def bind_function(name, c):
def func():
print("I am the '{}' command".format(c))
func.__name__ = name
return func
for c in commands:
f = bind_function('_f', c)
_f = main.command(name=c)(f)
if __name__ == '__main__':
main()
The bind_function
also allows you to name your functions differently if required.
You should use function that creates another function for your command, here the example
import click
commands = ['foo', 'bar', 'baz']
@click.group()
def main():
pass
def create_func(val):
def f():
print("I am the '{}' command".format(val))
return f
for c in commands:
main.command(name=c)(create_func(c))
if __name__ == '__main__':
main()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With