我想使用一些有用的函数作为命令。为此,我正在测试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
这样我就可以使用add_name
文件和add_surname
生成三个命令add_name_and_surname
,setup.py
和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_name
和add_surname
不是作为命令而是作为函数,否则它会违背我最初的使用目的既可以作为库函数也可以作为命令。
答案 0 :(得分:21)
当您直接从另一个函数调用add_name()
和add_surname()
时,实际上会调用它们的修饰版本,因此预期的参数可能与您定义的参数不同(请参阅{{3}的答案)关于为什么的一些细节)。
我建议修改您的实现,以便保持原始功能未修饰并为它们创建精简的特定于命令的包装器,例如:
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包装器脚本调用它们。
这似乎是多余的,但实际上可能是正确的方法:一个函数代表你的业务逻辑,另一个函数(click命令)是一个“控制器”,通过命令行公开这个逻辑(可能是,for例如,也是一个通过Web服务公开相同逻辑的函数。
事实上,我甚至建议将它们放在单独的Python模块中 - 您的“核心”逻辑和特定于点击的实现,如果需要,可以替换为任何其他接口。
答案 1 :(得分:15)
由于点击装饰器,只能通过指定参数来调用函数。 Context课程在这里是你的朋友,具体来说是:
因此,add_name_and_surname的代码应如下所示:
var myGamePiece;
function startGame() {
myGameArea.start();
myGamePiece = new component(30, 30, "red", 10, 120);
}
var myGameArea = {
canvas : document.createElement("canvas"),
start : function() {
this.canvas.width = 480;
this.canvas.height = 270;
this.context = this.canvas.getContext("2d");
document.body.insertBefore(this.canvas, document.body.childNodes[0]);
}
}
function component(width, height, color, x, y) {
this.width = width;
this.height = height;
this.x = x;
this.y = y;
ctx = myGameArea.context;
ctx.fillStyle = color;
ctx.fillRect(this.x, this.y, this.width, this.height);
}
参考: http://click.pocoo.org/6/advanced/#invoking-other-commands
答案 2 :(得分:1)
我发现这些解决方案更加复杂。我希望从另一个程序包的另一个位置调用以下函数:
@click.command(help='Clean up')
@click.argument('path', nargs=1, default='def')
@click.option('--info', '-i', is_flag=True,
help='some info1')
@click.option('--total', '-t', is_flag=True,
help='some info2')
def clean(path, info, total):
#some definition, some actions
#this function will help us
def get_function(function_name):
if function_name == 'clean':
return clean
我还有另一个软件包,所以,我想单击此软件包中上面的命令
import somepackage1 #here is clean up click command
from click.testing import CliRunner
@check.command(context_settings=dict(
ignore_unknown_options=True,
))
@click.argument('args', nargs=-1)
@click.pass_context
def check(ctx, args):
runner = CliRunner()
if len(args[0]) == 0:
logger.error('Put name of a command')
if len(args) > 0:
result = runner.invoke(somepackage1.get_function(args[0]), args[1:])
logger.print(result.output)
else:
result = runner.invoke(somepackage1.get_function(args[0]))
logger.print(result.output)
所以,它有效。
python somepackage2 check clean params1 --info