从代码中调用点击命令 [英] Call a click command from code

查看:61
本文介绍了从代码中调用点击命令的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个使用点击作为命令包装的功能.所以看起来像这样:

I have a function which is wrapped as a command using click. So it looks like this:

@click.command()
@click.option('-w', '--width', type=int, help="Some helping message", default=0)
[... some other options ...]
def app(width, [... some other option arguments...]):
    [... function code...]

对于此功能,我有不同的用例.有时候,可以通过命令行调用它,但是有时候我也想直接调用该函数

I have different use cases for this function. Sometimes, calling it through the command line is fine, but sometime I would also like to call directly the function

from file_name import app
width = 45
app(45, [... other arguments ...]) 

我们该怎么做?我们如何使用click调用已包装为命令的函数?我发现了此相关帖子,但目前尚不清楚我将如何适应我的情况(即,从头开始构建Context类,并在click命令功能之外使用它).

How can we do that? How can we call a function that has been wrapped as a command using click? I found this related post, but it is not clear to me how to adapt it to my case (i.e., build a Context class from scratch and use it outside of a click command function).

编辑:我应该提到:我不能(轻松地)修改包含要调用的函数的程序包.因此,我正在寻找的解决方案是如何从调用方方面解决问题.

I should have mentioned: I cannot (easily) modify the package that contains the function to call. So the solution I am looking for is how to deal with it from the caller side.

推荐答案

通过从参数重建命令行,可以从常规代码调用click命令功能.使用您的示例,它看起来可能像这样:

You can call a click command function from regular code by reconstructing the command line from parameters. Using your example it could look somthing like this:

call_click_command(app, width, [... other arguments ...])

代码:

def call_click_command(cmd, *args, **kwargs):
    """ Wrapper to call a click command

    :param cmd: click cli command function to call 
    :param args: arguments to pass to the function 
    :param kwargs: keywrod arguments to pass to the function 
    :return: None 
    """

    # Get positional arguments from args
    arg_values = {c.name: a for a, c in zip(args, cmd.params)}
    args_needed = {c.name: c for c in cmd.params
                   if c.name not in arg_values}

    # build and check opts list from kwargs
    opts = {a.name: a for a in cmd.params if isinstance(a, click.Option)}
    for name in kwargs:
        if name in opts:
            arg_values[name] = kwargs[name]
        else:
            if name in args_needed:
                arg_values[name] = kwargs[name]
                del args_needed[name]
            else:
                raise click.BadParameter(
                    "Unknown keyword argument '{}'".format(name))


    # check positional arguments list
    for arg in (a for a in cmd.params if isinstance(a, click.Argument)):
        if arg.name not in arg_values:
            raise click.BadParameter("Missing required positional"
                                     "parameter '{}'".format(arg.name))

    # build parameter lists
    opts_list = sum(
        [[o.opts[0], str(arg_values[n])] for n, o in opts.items()], [])
    args_list = [str(v) for n, v in arg_values.items() if n not in opts]

    # call the command
    cmd(opts_list + args_list)

这是如何工作的?

之所以有用,是因为click是一个设计良好的OO框架.可以对@click.Command对象进行自省,以确定其期望的参数.然后,可以构造一个命令行,使其看起来像单击所期望的命令行.

How does this work?

This works because click is a well designed OO framework. The @click.Command object can be introspected to determine what parameters it is expecting. Then a command line can be constructed that will look like the command line that click is expecting.

import click

@click.command()
@click.option('-w', '--width', type=int, default=0)
@click.option('--option2')
@click.argument('argument')
def app(width, option2, argument):
    click.echo("params: {} {} {}".format(width, option2, argument))
    assert width == 3
    assert option2 == '4'
    assert argument == 'arg'


width = 3
option2 = 4
argument = 'arg'

if __name__ == "__main__":
    commands = (
        (width, option2, argument, {}),
        (width, option2, dict(argument=argument)),
        (width, dict(option2=option2, argument=argument)),
        (dict(width=width, option2=option2, argument=argument),),
    )

    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('> {}'.format(cmd))
            time.sleep(0.1)
            call_click_command(app, *cmd[:-1], **cmd[-1])

        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)]
-----------
> (3, 4, 'arg', {})
params: 3 4 arg
-----------
> (3, 4, {'argument': 'arg'})
params: 3 4 arg
-----------
> (3, {'option2': 4, 'argument': 'arg'})
params: 3 4 arg
-----------
> ({'width': 3, 'option2': 4, 'argument': 'arg'},)
params: 3 4 arg

这篇关于从代码中调用点击命令的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆