基本命令、组、上下文

命令和组是Click应用程序的构建模块。 Command 包装一个函数使其成为一个卸载命令。 Group 包装命令和组以将其制作成应用程序。 Context 是小组和命令的沟通方式。

命令

基本命令示例

简单的命令装饰器不接受参数。

@click.command()
@click.option('--count', default=1)
def hello(count):
    for x in range(count):
        click.echo("Hello!")
$ hello --count 2
Hello!
Hello!

重命名命令

默认情况下,该命令是函数名,其中下划线被破折号取代。要更改此参数,请将所需的名称传递到第一个位置参数。

@click.command('say-hello')
@click.option('--count', default=1)
def hello(count):
    for x in range(count):
        click.echo("Hello!")
$ say-hello --count 2
Hello!
Hello!

废弃命令

将命令标记为不推荐的传递 deprecated=True

@click.command('say-hello', deprecated=True)
@click.option('--count', default=1)
def hello(count):
    for x in range(count):
        click.echo("Hello!")
$ say-hello --count 2
DeprecationWarning: The command 'say-hello' is deprecated.
Hello!
Hello!

基本组示例

一个组包装一个或多个命令。包装后,命令将嵌套在该组下。您可以在帮助页面和执行中看到这一点。默认情况下,在不使用命令的情况下调用组将显示帮助页面。

@click.group()
def greeting():
    click.echo('Starting greeting ...')

@greeting.command('say-hello')
@click.option('--count', default=1)
def hello(count):
    for x in range(count):
        click.echo("Hello!")

在顶级:

$ greeting
Usage: greeting [OPTIONS] COMMAND [ARGS]...

Options:
  --help  Show this message and exit.

Commands:
  say-hello

在命令级别:

$ greeting say-hello
Starting greeting ...
Hello!
$ greeting say-hello --help
Starting greeting ...
Usage: greeting say-hello [OPTIONS]

Options:
  --count INTEGER
  --help           Show this message and exit.

正如您从上面的例子中看到的那样,由组装饰器包装的函数会执行,除非它被中断(例如通过调用帮助)。

重命名群组

要使用修饰函数名称以外的名称作为组名称,请将其作为第一个位置参数传递。

@click.group('greet-someone')
def greeting():
    click.echo('Starting greeting ...')

@greeting.command('say-hello')
@click.option('--count', default=1)
def hello(count):
    for x in range(count):
        click.echo("Hello!")
$ greet-someone say-hello
Starting greeting ...
Hello!

没有命令的团体召唤

默认情况下,如果在没有命令的情况下传递组,则不会调用该组并自动传递命令 --help .要改变这一点,请通过 invoke_without_command=True 给大家上下文对象还包括有关组调用是否会转到其下嵌套的命令的信息。

@click.group(invoke_without_command=True)
@click.pass_context
def cli(ctx):
    if ctx.invoked_subcommand is None:
        click.echo('I was invoked without subcommand')
    else:
        click.echo(f"I am about to invoke {ctx.invoked_subcommand}")

@cli.command()
def sync():
    click.echo('The subcommand')
$ tool
I was invoked without subcommand
$ tool sync
I am about to invoke sync
The subcommand

组分离

命令 参数 附加于命令的命令只属于该命令。

@click.group()
def greeting():
    pass

@greeting.command()
@click.option('--count', default=1)
def hello(count):
    for x in range(count):
        click.echo("Hello!")

@greeting.command()
@click.option('--count', default=1)
def goodbye(count):
    for x in range(count):
        click.echo("Goodbye!")
$ greeting hello --count 2
Hello!
Hello!
$ greeting goodbye --count 2
Goodbye!
Goodbye!
$ greeting
Usage: greeting [OPTIONS] COMMAND [ARGS]...

Options:
  --help  Show this message and exit.

Commands:
  goodbye
  hello

此外,给定组的参数仅属于该组,而不属于该组下的命令。这意味着必须指定特定命令的选项和参数 after 命令名称本身,但是 before 任何其他命令名称。

这种行为可以通过 --help 选项.假设我们有一个名为 tool 包含一个名为 sub .

  • tool --help 返回整个程序的帮助(列出子命令)。

  • tool sub --help 返回帮助 sub 子命令。

  • tool --help sub 对待 --help 作为主程序的论据。单击然后调用回调 --help ,它会在单击可以处理子命令之前打印帮助并中止程序。

任意嵌套

Commands 附接到 Group .多个组可以连接到另一个组。包含多个组的组可以附加到一个组,等等。要调用嵌套在多个组下的命令,必须调用该命令所嵌套的所有组。

@click.group()
def cli():
    pass

# Not @click so that the group is registered now.
@cli.group()
def session():
    click.echo('Starting session')

@session.command()
def initdb():
    click.echo('Initialized the database')

@session.command()
def dropdb():
    click.echo('Dropped the database')
$ cli session initdb
Starting session
Initialized the database

懒惰的附加命令

到目前为止,大多数示例都立即将命令附加到组,但命令可能会稍后注册。这可以用于将命令拆分为多个Python模块。无论如何附加,这些命令的调用都是相同的。

@click.group()
def cli():
    pass

@cli.command()
def initdb():
    click.echo('Initialized the database')

@click.command()
def dropdb():
    click.echo('Dropped the database')

cli.add_command(dropdb)
$ cli initdb
Initialized the database
$ cli dropdb
Dropped the database

上下文对象

Context 对象是命令和组的通信方式。

自动Envvar预设

仅支持选项自动构建的环境变量。要启用此功能, auto_envvar_prefix 参数需要传递给所调用的脚本。 然后将每个命令和参数添加为收件箱下分数分隔的变量。 如果您有一个名为 run 采取一种称为 reload 而前面是 WEB ,那么变量是 WEB_RUN_RELOAD .

示例使用:

@click.command()
@click.option('--username')
def greet(username):
    click.echo(f'Hello {username}!')

if __name__ == '__main__':
    greet(auto_envvar_prefix='GREETER')

从命令行:

$ export GREETER_USERNAME=john
$ greet
Hello john!

当使用 auto_envvar_prefix 对于命令组,命令名称需要包含在环境变量中,位于前置和参数名称之间, i.e. PREFIX_COMMAND_VARIABLE .如果您有一个名为 run-server 采取一种称为 host 而前面是 WEB ,那么变量是 WEB_RUN_SERVER_HOST .

@click.group()
@click.option('--debug/--no-debug')
def cli(debug):
    click.echo(f"Debug mode is {'on' if debug else 'off'}")

@cli.command()
@click.option('--username')
def greet(username):
    click.echo(f"Hello {username}!")

if __name__ == '__main__':
    cli(auto_envvar_prefix='GREETER')
$ export GREETER_DEBUG=false
$ export GREETER_GREET_USERNAME=John
$ cli greet
Debug mode is off
Hello John!

全球上下文访问

Changelog

Added in version 5.0.

从Click 5.0开始,可以通过使用 get_current_context() 返回它的函数。这主要用于访问上下文绑定对象以及存储在其中以自定义运行时行为的一些标志。 比如 echo() 函数这样做是为了推断 color

示例使用::

def get_current_command_name():
    return click.get_current_context().info_name

应该注意的是,这仅适用于当前线程。 如果您产生其他线程,那么这些线程将无法引用当前上下文。 如果您想赋予另一个线程引用此上下文的能力,则需要使用线程内的上下文作为上下文管理器::

def spawn_thread(ctx, func):
    def wrapper():
        with ctx:
            func()
    t = threading.Thread(target=wrapper)
    t.start()
    return t

现在线程函数可以像主线程一样访问上下文。 然而,如果您确实使用它来线程化,则需要非常小心,因为绝大多数上下文都不是线程安全的! 您仅允许阅读上下文,但不允许对其进行任何修改。

检测参数来源

在某些情况下,了解选项或参数是否来自命令行、环境、默认值或 Context.default_map .的 Context.get_parameter_source() 可以使用方法来找出这一点。它将返回 ParameterSource 枚举。

@click.command()
@click.argument('port', nargs=1, default=8080, envvar="PORT")
@click.pass_context
def cli(ctx, port):
    source = ctx.get_parameter_source("port")
    click.echo(f"Port came from {source.name}")
$ cli 8080
Port came from COMMANDLINE

$ export PORT=8080
$ cli
Port came from ENVIRONMENT

$ cli
Port came from DEFAULT