从单击命令调用另一个单击命令

新手上路,请多包涵

我想使用一些有用的函数作为命令。为此,我正在测试 click 库。我定义了我的三个原始函数,然后装饰为 click.command

 import click
import os, sys

@click.command()
@click.argument('content', required=False)
@click.option('--to_stdout', default=True)
def add_name(content, to_stdout=False):
    if not content:
        content = ''.join(sys.stdin.readlines())
    result = content + "\n\tadded name"
    if to_stdout is True:
        sys.stdout.writelines(result)
    return result

@click.command()
@click.argument('content', required=False)
@click.option('--to_stdout', default=True)
def add_surname(content, to_stdout=False):
    if not content:
        content = ''.join(sys.stdin.readlines())
    result = content + "\n\tadded surname"
    if to_stdout is True:
        sys.stdout.writelines(result)
    return result

@click.command()
@click.argument('content', required=False)
@click.option('--to_stdout', default=False)
def add_name_and_surname(content, to_stdout=False):
    result = add_surname(add_name(content))
    if to_stdout is True:
        sys.stdout.writelines(result)
    return result

This way I am able to generate the three commands add_name , add_surname and add_name_and_surname using a setup.py file and pip install --editable . 然后我就可以管道了:

 $ echo "original content" | add_name | add_surname
original content

    added name
    added surname

但是,在将不同的单击命令组合为函数时,我需要解决一个小问题:

 $echo "original content" | add_name_and_surname
Usage: add_name_and_surname [OPTIONS] [CONTENT]

Error: Got unexpected extra arguments (r i g i n a l   c o n t e n t
)

我不知道为什么它不起作用,我需要这个 add_name_and_surname 命令来调用 add_nameadd_surname 作为我的原始函数失败使用函数作为库函数和命令的目的。

原文由 kaligne 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 817
2 个回答

由于点击装饰器,函数不能再仅通过指定参数来调用。 Context 类是你的朋友,具体来说:

  1. Context.invoke() - 使用您提供的参数调用另一个命令
  2. Context.forward() - 填充当前命令的参数

所以你的 add_name_and_surname 代码应该是这样的:

 @click.command()
@click.argument('content', required=False)
@click.option('--to_stdout', default=False)
@click.pass_context
def add_name_and_surname(ctx, content, to_stdout=False):
    result = ctx.invoke(add_surname, content=ctx.forward(add_name))
    if to_stdout is True:
        sys.stdout.writelines(result)
    return result

参考: http ://click.pocoo.org/6/advanced/#invoking-other-commands

原文由 Vikas Tikoo 发布,翻译遵循 CC BY-SA 3.0 许可协议

当您直接从另一个函数调用 add_name()add_surname() 时,您实际上调用了它们的修饰版本,因此预期的参数可能与您定义的不同(请参阅 如何剥离 的答案 来自 python 函数的装饰器,以 获取有关原因的一些详细信息)。

我建议修改您的实现,以便您保留原始功能未修饰并为它们创建薄的特定于点击的包装器,例如:

 def add_name(content, to_stdout=False):
    if not content:
        content = ''.join(sys.stdin.readlines())
    result = content + "\n\tadded name"
    if to_stdout is True:
        sys.stdout.writelines(result)
    return result

@click.command()
@click.argument('content', required=False)
@click.option('--to_stdout', default=True)
def add_name_command(content, to_stdout=False):
    return add_name(content, to_stdout)

然后,您可以直接调用这些函数或通过 setup.py 创建的 CLI 包装器脚本调用它们。

这可能看起来多余,但实际上可能是正确的方法:一个函数代表您的业务逻辑,另一个(点击命令)是一个“控制器”,通过命令行公开这个逻辑(可能是为了例如,还有一个函数通过 Web 服务公开相同的逻辑)。

事实上,我什至建议将它们放在单独的 Python 模块中——您的“核心”逻辑和特定于点击的实现,如果需要,可以将其替换为任何其他界面。

原文由 shevron 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题