7

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?

0

2 Answers 2

7

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.

Sign up to request clarification or add additional context in comments.

Comments

2

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()

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.