如何使用 Python 的 Click 包 return 来自装饰器的参数值?
How do I return a parameter value from a decorator using Python's Click package?
在我的 CLI 应用程序中,我有一个配置文件,其中包含有关如何连接到 API 的信息。可以为多个环境设置此配置:(test/prod/etc)。
我想确保传递的环境存在于配置文件中,所以我创建了一个装饰器并将其建模为 confirmation_option
。
这是问题的一个小例子:
# -*- coding: utf-8 -*-
import sys
import click
TEST_DICT = {
'production': 'ProdKey'
}
def environment_option(*param_decls, **attrs):
"""
Check that the passed environment exists
"""
def decorator(f):
def callback(ctx, param, value):
if not value:
ctx.abort()
try:
TEST_DICT[value]
except KeyError:
click.secho("Bad environment provided: {}".format(value), fg='red')
sys.exit(1)
attrs.setdefault('callback', callback)
return click.option(*(param_decls or ('--environment',)), **attrs)(f)
return decorator
@click.group()
@click.pass_context
def cli(ctx):
"""My CLI"""
@cli.command()
@environment_option('-e', '--environment', help="Environment to associate this key with", required=True)
@click.pass_context
def show_keys(ctx, environment):
"""
List the available keys for the selected environment
"""
click.echo("Environment:", type(environment))
if __name__ == "__main__":
sys.exit(cli()) # pragma: no cover
如果我传入无效环境,它会按预期工作。
$ python cli show-keys -e notreal
Bad environment provided: notreal
我遇到的问题是,一个有效的环境——不会在装饰器中触发错误的环境——在返回到 show_keys
函数时没有值。是 NoneType
:
$ python cli show-keys -e production
Traceback (most recent call last):
File "/home/devuser/.virtualenvs/mycli/bin/mycli", line 11, in <module>
load_entry_point('mycli', 'console_scripts', 'mycli')()
File "/home/devuser/.virtualenvs/mycli/lib/python3.6/site-packages/click/core.py", line 764, in __call__
return self.main(*args, **kwargs)
File "/home/devuser/.virtualenvs/mycli/lib/python3.6/site-packages/click/core.py", line 717, in main
rv = self.invoke(ctx)
File "/home/devuser/.virtualenvs/mycli/lib/python3.6/site-packages/click/core.py", line 1137, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
File "/home/devuser/.virtualenvs/mycli/lib/python3.6/site-packages/click/core.py", line 1137, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
File "/home/devuser/.virtualenvs/mycli/lib/python3.6/site-packages/click/core.py", line 956, in invoke
return ctx.invoke(self.callback, **ctx.params)
File "/home/devuser/.virtualenvs/mycli/lib/python3.6/site-packages/click/core.py", line 555, in invoke
return callback(*args, **kwargs)
File "/home/devuser/.virtualenvs/mycli/lib/python3.6/site-packages/click/decorators.py", line 17, in new_func
return f(get_current_context(), *args, **kwargs)
File "/home/devuser/repositories/mycli/mycli/mycli/commands/config_cmds.py", line 60, in show_keys
click.echo("Environment:", type(environment))
File "/home/devuser/.virtualenvs/mycli/lib/python3.6/site-packages/click/utils.py", line 260, in echo
file.write(message)
AttributeError: type object 'NoneType' has no attribute 'write'
如何从这个装饰器而不是 NoneType
取回我的 environment
的值?
Python 函数没有 return
语句,return None
。所以在回调中,需要return
的值为TEST_DICT[value]
。类似于:
def callback(ctx, param, value):
if not value:
ctx.abort()
try:
return TEST_DICT[value]
except KeyError:
....
测试代码:
import sys
import click
TEST_DICT = {
'production': 'ProdKey'
}
def environment_option(*param_decls, **attrs):
"""
Check that the passed environment exists
"""
def decorator(f):
def callback(ctx, param, value):
if not value:
ctx.abort()
try:
return TEST_DICT[value]
except KeyError:
click.secho("Bad environment provided: {}".format(value),
fg='red')
sys.exit(1)
attrs.setdefault('callback', callback)
return click.option(*(param_decls or ('--environment',)), **attrs)(f)
return decorator
@click.group()
@click.pass_context
def cli(ctx):
"""My CLI"""
@cli.command('show-keys')
@environment_option('-e', '--environment',
help="Environment to associate this key with",
required=True)
@click.pass_context
def show_keys(ctx, environment):
"""
List the available keys for the selected environment
"""
click.echo("Environment: {}".format(environment))
if __name__ == "__main__":
commands = (
'show-keys -e production',
'show-keys -e notreal',
'show-keys --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)
cli(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)]
-----------
> show-keys -e production
Environment: ProdKey
-----------
> show-keys -e notreal
Bad environment provided: notreal
-----------
> show-keys --help
Usage: test.py show-keys [OPTIONS]
List the available keys for the selected environment
Options:
-e, --environment TEXT Environment to associate this key with [required]
--help Show this message and exit.
-----------
> --help
Usage: test.py [OPTIONS] COMMAND [ARGS]...
My CLI
Options:
--help Show this message and exit.
Commands:
show-keys List the available keys for the selected...
-----------
>
Usage: test.py [OPTIONS] COMMAND [ARGS]...
My CLI
Options:
--help Show this message and exit.
Commands:
show-keys List the available keys for the selected...
在我的 CLI 应用程序中,我有一个配置文件,其中包含有关如何连接到 API 的信息。可以为多个环境设置此配置:(test/prod/etc)。
我想确保传递的环境存在于配置文件中,所以我创建了一个装饰器并将其建模为 confirmation_option
。
这是问题的一个小例子:
# -*- coding: utf-8 -*-
import sys
import click
TEST_DICT = {
'production': 'ProdKey'
}
def environment_option(*param_decls, **attrs):
"""
Check that the passed environment exists
"""
def decorator(f):
def callback(ctx, param, value):
if not value:
ctx.abort()
try:
TEST_DICT[value]
except KeyError:
click.secho("Bad environment provided: {}".format(value), fg='red')
sys.exit(1)
attrs.setdefault('callback', callback)
return click.option(*(param_decls or ('--environment',)), **attrs)(f)
return decorator
@click.group()
@click.pass_context
def cli(ctx):
"""My CLI"""
@cli.command()
@environment_option('-e', '--environment', help="Environment to associate this key with", required=True)
@click.pass_context
def show_keys(ctx, environment):
"""
List the available keys for the selected environment
"""
click.echo("Environment:", type(environment))
if __name__ == "__main__":
sys.exit(cli()) # pragma: no cover
如果我传入无效环境,它会按预期工作。
$ python cli show-keys -e notreal
Bad environment provided: notreal
我遇到的问题是,一个有效的环境——不会在装饰器中触发错误的环境——在返回到 show_keys
函数时没有值。是 NoneType
:
$ python cli show-keys -e production
Traceback (most recent call last):
File "/home/devuser/.virtualenvs/mycli/bin/mycli", line 11, in <module>
load_entry_point('mycli', 'console_scripts', 'mycli')()
File "/home/devuser/.virtualenvs/mycli/lib/python3.6/site-packages/click/core.py", line 764, in __call__
return self.main(*args, **kwargs)
File "/home/devuser/.virtualenvs/mycli/lib/python3.6/site-packages/click/core.py", line 717, in main
rv = self.invoke(ctx)
File "/home/devuser/.virtualenvs/mycli/lib/python3.6/site-packages/click/core.py", line 1137, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
File "/home/devuser/.virtualenvs/mycli/lib/python3.6/site-packages/click/core.py", line 1137, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
File "/home/devuser/.virtualenvs/mycli/lib/python3.6/site-packages/click/core.py", line 956, in invoke
return ctx.invoke(self.callback, **ctx.params)
File "/home/devuser/.virtualenvs/mycli/lib/python3.6/site-packages/click/core.py", line 555, in invoke
return callback(*args, **kwargs)
File "/home/devuser/.virtualenvs/mycli/lib/python3.6/site-packages/click/decorators.py", line 17, in new_func
return f(get_current_context(), *args, **kwargs)
File "/home/devuser/repositories/mycli/mycli/mycli/commands/config_cmds.py", line 60, in show_keys
click.echo("Environment:", type(environment))
File "/home/devuser/.virtualenvs/mycli/lib/python3.6/site-packages/click/utils.py", line 260, in echo
file.write(message)
AttributeError: type object 'NoneType' has no attribute 'write'
如何从这个装饰器而不是 NoneType
取回我的 environment
的值?
Python 函数没有 return
语句,return None
。所以在回调中,需要return
的值为TEST_DICT[value]
。类似于:
def callback(ctx, param, value):
if not value:
ctx.abort()
try:
return TEST_DICT[value]
except KeyError:
....
测试代码:
import sys
import click
TEST_DICT = {
'production': 'ProdKey'
}
def environment_option(*param_decls, **attrs):
"""
Check that the passed environment exists
"""
def decorator(f):
def callback(ctx, param, value):
if not value:
ctx.abort()
try:
return TEST_DICT[value]
except KeyError:
click.secho("Bad environment provided: {}".format(value),
fg='red')
sys.exit(1)
attrs.setdefault('callback', callback)
return click.option(*(param_decls or ('--environment',)), **attrs)(f)
return decorator
@click.group()
@click.pass_context
def cli(ctx):
"""My CLI"""
@cli.command('show-keys')
@environment_option('-e', '--environment',
help="Environment to associate this key with",
required=True)
@click.pass_context
def show_keys(ctx, environment):
"""
List the available keys for the selected environment
"""
click.echo("Environment: {}".format(environment))
if __name__ == "__main__":
commands = (
'show-keys -e production',
'show-keys -e notreal',
'show-keys --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)
cli(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)]
-----------
> show-keys -e production
Environment: ProdKey
-----------
> show-keys -e notreal
Bad environment provided: notreal
-----------
> show-keys --help
Usage: test.py show-keys [OPTIONS]
List the available keys for the selected environment
Options:
-e, --environment TEXT Environment to associate this key with [required]
--help Show this message and exit.
-----------
> --help
Usage: test.py [OPTIONS] COMMAND [ARGS]...
My CLI
Options:
--help Show this message and exit.
Commands:
show-keys List the available keys for the selected...
-----------
>
Usage: test.py [OPTIONS] COMMAND [ARGS]...
My CLI
Options:
--help Show this message and exit.
Commands:
show-keys List the available keys for the selected...