是否可以在 Python Click 中动态生成命令

Is it possible to dynamically generate commands in Python Click

我正在尝试从配置文件生成 click 命令。本质上,这种模式:

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

--help 文字看起来不错:

Usage: test_click.py [OPTIONS] COMMAND [ARGS]...

Options:
  --help  Show this message and exit.

Commands:
  bar
  baz
  foo

然而,所有命令似乎都路由到生成的最后一个命令:

$ ./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

我的尝试真的可行吗?

你应该使用为你的命令创建另一个函数的函数,这里是例子

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

问题出在 python 的功能范围内。当您创建函数 _f 时,它使用的是 c ,它的范围比函数高。所以当你调用函数时它不会被保留。 (_f 在调用 main 时被调用)

当您调用 main 时,循环已完成,c 将始终保存循环的最后一个值 - 即 baz

解决方案是将值 c 附加到使用其自身作用域的函数。

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

bind_function 还允许您根据需要为函数命名。