同时单击包含选项和命令的组

tom*_*pek 5 python command-line-interface python-3.x python-click

我正在尝试创建具有以下行为的应用程序:

myapp- 将启动应用程序并执行操作 A

myapp "some argument"- 会做事情 B。事情 B 受到参数“某些参数”的影响。

myapp command- 将启动具有功能 C 的“命令”(由装饰器表示@cli.command)。这将受到 click 必须提供的所有内容的影响,例如@click.option

请注意,在我的应用程序中会有更多像 C 这样的命令。


我尝试使用以下代码来实现这一点:

import click

class GroupWithOption(click.Group):
    def list_commands(self, ctx):
        return ['command']

    def get_command(self, ctx, cmd_name):
        if cmd_name == 'command':
            return command
        else:
            return do_b


@click.group(cls=GroupWithOption, invoke_without_command=True)
def main():
    print("Does A")

@main.command()
def command():
    print("Does C")

@main.command()
def do_b():
    print("Does B")


if __name__ == '__main__':
    main()
Run Code Online (Sandbox Code Playgroud)

结果好坏参半。其一,我可以非常轻松地调用 3 种不同的行为(或更多),但我无法弄清楚如何将参数传递给 B 命令。我不喜欢这个解决方案。看起来并不是一件干净的事。为了正常运行,它需要使用全局变量和一些讨厌的黑客。

你们中有人知道更好的方法吗?如何实现这一目标?

Ste*_*uch 4

一种方法是将此处给出的两个答案结合起来:

关键要素:

@click.group(cls=DefaultCommandGroup, invoke_without_command=True)
@click.pass_context
def main(ctx):
    if not ctx.invoked_subcommand:
        click.echo("Does A")

@main.command(default_command=True)
@click.argument('args', nargs=-1)
def default_cmd_with_args(args):
    click.echo("Does B: {}".format(args))
Run Code Online (Sandbox Code Playgroud)

这是如何运作的?

if not ctx.invoked_subcommand:
Run Code Online (Sandbox Code Playgroud)

允许仅在“无命令”情况下调用组命令,并且

@click.group(cls=DefaultCommandGroup, invoke_without_command=True)
Run Code Online (Sandbox Code Playgroud)

结合:

@main.command(default_command=True)
Run Code Online (Sandbox Code Playgroud)

如果没有找到其他命令,则允许运行命令。

测试代码:

import click

@click.group(cls=DefaultCommandGroup, invoke_without_command=True)
@click.pass_context
def main(ctx):
    """My Great CLI"""
    if not ctx.invoked_subcommand:
        click.echo("Does A")

@main.command(default_command=True)
@click.argument('args', nargs=-1)
def default_cmd_with_args(args):
    """Command run without a command"""
    click.echo("Does B: {}".format(args))


@main.command()
def cmd_c1():
    """A c1 command"""
    click.echo("Does C1")


@main.command()
def cmd_c2():
    """A c2 command"""
    click.echo("Does C2")


if __name__ == "__main__":
    commands = (
        '',
        'random args',
        'cmd_c1',
        'cmd_c2',
        '--help',
    )

    import sys, time

    time.sleep(1)
    print('Click Version: {}'.format(click.__version__))
    print('Python Version: {}'.format(sys.version))
    for command in commands:
        try:
            time.sleep(0.1)
            print('-----------')
            print('> ' + command)
            time.sleep(0.1)
            main(command.split())

        except BaseException as exc:
            if str(exc) != '0' and \
                    not isinstance(exc, (click.ClickException, SystemExit)):
                raise
Run Code Online (Sandbox Code Playgroud)

结果:

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)]
-----------
> 
Does A
-----------
> random args
Does B: ('random', 'args')
-----------
> cmd_c1
Does C1
-----------
> cmd_c2
Does C2
-----------
> --help
Usage: test.py [OPTIONS] COMMAND [ARGS]...

  My Great CLI

Options:
  --help  Show this message and exit.

Commands:
  <>      Command run without a command
  cmd_c1  A c1 command
  cmd_c2  A c2 command
Run Code Online (Sandbox Code Playgroud)