如何在 Python 中有效地提供 Web、CLI & API 界面?

How to efficiently provide a Web, CLI & API interface in Python?

我的问题如下。我的库中有一些函数:

def some_func(arg1=1):
    pass

我想以三种方式使用它:

在其他 python 脚本中使用导入语句:

import my_library
my_library.some_func()

通过单击使用 CLI 界面公开。

@click.group()
@click.option('--arg1', default=1)
def some_func(arg1):
    pass

使用带有 Flask 的 Web 界面公开。

@app.route('/endpoint', defaults={'arg1': 1})
def some_func(arg1):
    pass

但是我怎样才能在不重复太多代码的情况下有效地构建它呢?

是否可以将所有三个合并?我尝试了以下(变化),但失败了:

@click.group()
@click.option('--arg1', default=1)
@app.route('/endpoint', defaults={'arg1': 1})
def some_func(arg1=1):
    pass

或者我真的需要上面定义的 3 种不同的功能吗?

如果是这样,我应该如何设置默认值?

在所有地方访问全局变量是最好的方法吗?

由于装饰器语法只是函数应用的快捷方式,因此您可以从在库中定义函数开始。然后你的 click 例子变成:

import my_library

click.group()(click.option('--arg1', default=1)(my_library.some_func))

并且您的 Flask 示例变为

import library

app.route('/endpoint', defaults={'arg1': 1})(my_library.some_func)

(我知道对于 Flask,装饰器的 return 值并不重要;我假设 click 也是如此。)

这假设您没有尝试将同一个脚本用作命令行工具和 Flask 应用程序;在我看来,这没有多大意义。


就简化默认值而言,没有什么好的想法。 Click、Flask 和您的函数具有三种不同的指示默认值的方式;唯一的共同点是该默认值的实际 value。你可能会做这样的事情。首先,在 my_library.py:

some_func_default = 1

def some_func(arg=None):  # Or some other sentinel
    if arg is None:
        arg = some_func_default

然后在你的另外两个脚本中:

click.group()(click.option('--arg1', default=my_library.some_func_default)(my_library.some_func))

app.route('/endpoint', defaults={'arg1': my_library.some_func_default})(my_library.some_func)

当然,您可以使用 inspect 模块从 some_func 的原始定义中提取默认值,但这无助于 Click 和 Flask 设置默认值的方式不同他们的切入点。

您可以使用 hug 库,它正是为这个用例而制作的。

示例:


"""An example of writing an API to scrape hacker news once, and then enabling usage everywhere"""
import hug
import requests


@hug.local()
@hug.cli()
@hug.get()
def top_post(section: hug.types.one_of(('news', 'newest', 'show'))='news'):
    """Returns the top post from the provided section"""
    content = requests.get('https://news.ycombinator.com/{0}'.format(section)).content
    text = content.decode('utf-8')
    return text.split('<tr class=\'athing\'>')[1].split("<a href")[1].split(">")[1].split("<")[0]