使用 Python 单击命令调用带有可变参数的 class 方法
Use Python click command to invoke a class method with variadic arguments
我有一个 class,它使用以前未知数量的参数进行初始化,我希望使用 Python 的 click
包在 CLI 上完成它。我的问题是我无法初始化它并且 运行 一个 click
命令:
$ python mycode.py arg1 arg2 ... argN click_command
设置定义数量的参数,如 nargs=5
,解决了缺少命令的问题,但我必须在命令之前输入 5 个参数。对于像 nargs=-1
这样的可变参数,click
无法将 click_command
识别为命令。
如何输入 n 个参数,然后 运行 使用 click
命令?
import click
class Foo(object):
def __init__(self, *args):
self.args = args
def log(self):
print('self.args:', self.args)
pass_foo = click.make_pass_decorator(Foo)
@click.group()
@click.argument('myargs', nargs=-1)
@click.pass_context
def main(ctx, myargs):
ctx.obj = Foo(myargs)
print("arguments: ", myargs)
@main.command()
@pass_foo
def log(foo):
foo.log()
main()
我希望在将 n-many args 传递给我的 Foo()
class 后能够 运行 一个 click
命令,这样我就可以初始化它并且 运行 其 log()
方法作为 CLI 命令,但输出为:
Error: Missing command
我不完全确定您尝试做的是解决此问题的最佳方法。我认为将可变参数放在命令之后会更合乎逻辑,并且肯定会更符合点击的工作方式。但是,你可以用这个做你想做的事:
自定义 Class:
class CommandAfterArgs(click.Group):
def parse_args(self, ctx, args):
parsed_args = super(CommandAfterArgs, self).parse_args(ctx, args)
possible_command = ctx.params['myargs'][-1]
if possible_command in self.commands:
ctx.protected_args = [possible_command]
ctx.params['myargs'] = ctx.params['myargs'][:-1]
elif possible_command in ('-h', '--help'):
if len(ctx.params['myargs']) > 1 and \
ctx.params['myargs'][-2] in self.commands:
ctx.protected_args = [ctx.params['myargs'][-2]]
parsed_args = ['--help']
ctx.params['myargs'] = ctx.params['myargs'][:-2]
ctx.args = [possible_command]
return parsed_args
使用自定义 Class:
然后使用自定义 class,将其作为 cls
参数传递给组装饰器,如:
@click.group(cls=CommandAfterArgs)
@click.argument('myargs', nargs=-1)
def main(myargs):
...
测试代码:
import click
class Foo(object):
def __init__(self, *args):
self.args = args
def log(self):
print('self.args:', self.args)
pass_foo = click.make_pass_decorator(Foo)
@click.group(cls=CommandAfterArgs)
@click.argument('myargs', nargs=-1)
@click.pass_context
def main(ctx, myargs):
ctx.obj = Foo(*myargs)
print("arguments: ", myargs)
@main.command()
@pass_foo
def log(foo):
foo.log()
if __name__ == "__main__":
commands = (
'arg1 arg2 log',
'log --help',
'--help',
)
import sys, time
time.sleep(1)
print('Click Version: {}'.format(click.__version__))
print('Python Version: {}'.format(sys.version))
for cmd in commands:
try:
time.sleep(0.1)
print('-----------')
print('> ' + cmd)
time.sleep(0.1)
main(cmd.split())
except BaseException as exc:
if str(exc) != '0' and \
not isinstance(exc, (click.ClickException, SystemExit)):
raise
结果:
Click Version: 6.7
Python Version: 3.6.3 (v3.6.3:2c5fed8, Oct 3 2017, 18:11:49) [MSC v.1900 64 bit (AMD64)]
-----------
> arg1 arg2 log
arguments: ('arg1', 'arg2')
self.args: ('arg1', 'arg2')
-----------
> log --help
arguments: ()
Usage: test.py log [OPTIONS]
Options:
--help Show this message and exit.
-----------
> --help
Usage: test.py [OPTIONS] [MYARGS]... COMMAND [ARGS]...
Options:
--help Show this message and exit.
Commands:
log
我有一个 class,它使用以前未知数量的参数进行初始化,我希望使用 Python 的 click
包在 CLI 上完成它。我的问题是我无法初始化它并且 运行 一个 click
命令:
$ python mycode.py arg1 arg2 ... argN click_command
设置定义数量的参数,如 nargs=5
,解决了缺少命令的问题,但我必须在命令之前输入 5 个参数。对于像 nargs=-1
这样的可变参数,click
无法将 click_command
识别为命令。
如何输入 n 个参数,然后 运行 使用 click
命令?
import click
class Foo(object):
def __init__(self, *args):
self.args = args
def log(self):
print('self.args:', self.args)
pass_foo = click.make_pass_decorator(Foo)
@click.group()
@click.argument('myargs', nargs=-1)
@click.pass_context
def main(ctx, myargs):
ctx.obj = Foo(myargs)
print("arguments: ", myargs)
@main.command()
@pass_foo
def log(foo):
foo.log()
main()
我希望在将 n-many args 传递给我的 Foo()
class 后能够 运行 一个 click
命令,这样我就可以初始化它并且 运行 其 log()
方法作为 CLI 命令,但输出为:
Error: Missing command
我不完全确定您尝试做的是解决此问题的最佳方法。我认为将可变参数放在命令之后会更合乎逻辑,并且肯定会更符合点击的工作方式。但是,你可以用这个做你想做的事:
自定义 Class:
class CommandAfterArgs(click.Group):
def parse_args(self, ctx, args):
parsed_args = super(CommandAfterArgs, self).parse_args(ctx, args)
possible_command = ctx.params['myargs'][-1]
if possible_command in self.commands:
ctx.protected_args = [possible_command]
ctx.params['myargs'] = ctx.params['myargs'][:-1]
elif possible_command in ('-h', '--help'):
if len(ctx.params['myargs']) > 1 and \
ctx.params['myargs'][-2] in self.commands:
ctx.protected_args = [ctx.params['myargs'][-2]]
parsed_args = ['--help']
ctx.params['myargs'] = ctx.params['myargs'][:-2]
ctx.args = [possible_command]
return parsed_args
使用自定义 Class:
然后使用自定义 class,将其作为 cls
参数传递给组装饰器,如:
@click.group(cls=CommandAfterArgs)
@click.argument('myargs', nargs=-1)
def main(myargs):
...
测试代码:
import click
class Foo(object):
def __init__(self, *args):
self.args = args
def log(self):
print('self.args:', self.args)
pass_foo = click.make_pass_decorator(Foo)
@click.group(cls=CommandAfterArgs)
@click.argument('myargs', nargs=-1)
@click.pass_context
def main(ctx, myargs):
ctx.obj = Foo(*myargs)
print("arguments: ", myargs)
@main.command()
@pass_foo
def log(foo):
foo.log()
if __name__ == "__main__":
commands = (
'arg1 arg2 log',
'log --help',
'--help',
)
import sys, time
time.sleep(1)
print('Click Version: {}'.format(click.__version__))
print('Python Version: {}'.format(sys.version))
for cmd in commands:
try:
time.sleep(0.1)
print('-----------')
print('> ' + cmd)
time.sleep(0.1)
main(cmd.split())
except BaseException as exc:
if str(exc) != '0' and \
not isinstance(exc, (click.ClickException, SystemExit)):
raise
结果:
Click Version: 6.7
Python Version: 3.6.3 (v3.6.3:2c5fed8, Oct 3 2017, 18:11:49) [MSC v.1900 64 bit (AMD64)]
-----------
> arg1 arg2 log
arguments: ('arg1', 'arg2')
self.args: ('arg1', 'arg2')
-----------
> log --help
arguments: ()
Usage: test.py log [OPTIONS]
Options:
--help Show this message and exit.
-----------
> --help
Usage: test.py [OPTIONS] [MYARGS]... COMMAND [ARGS]...
Options:
--help Show this message and exit.
Commands:
log