【问题标题】:How to efficiently provide a Web, CLI & API interface in Python?如何在 Python 中高效地提供 Web、CLI 和 API 接口?
【发布时间】:2019-05-06 15:27:07
【问题描述】:

我的问题如下。我的库中有一些功能:

def some_func(arg1=1):
    pass

我想以三种方式使用它:

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

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 个不同的功能吗?

如果是这样,我应该如何处理默认值?

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

【问题讨论】:

  • 你应该使用三个入口点,并且使用共享常量作为默认值并没有错。
  • 谢谢。但在我看来,使用常量作为默认值似乎很丑陋,但这也许是因为我个人还没有看到“野外”。因此,我尝试找出是否有更好的方法来创建此类应用程序。但我很难找到任何相关信息。

标签: python flask interface command-line-interface python-click


【解决方案1】:

由于装饰器语法只是函数应用程序的快捷方式,因此您首先要在库中定义函数。那么你的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,装饰器的返回值并不重要;我假设 click 也是如此。)

这假设您没有尝试将相同的脚本用作命令行工具和 Flask 应用程序; IMO 没有多大意义。


就简化默认值而言,没有什么好的想法。 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 为其入口点设置默认值的方式的差异。

【讨论】:

  • 感谢您的回答。您的假设是正确的,因为这些实际上是单独的脚本。但是对于默认值,什么是一个好的选择呢?使用来自my_library的全局变量?
  • @MuadDev 这看起来和其他的一样好。问题在于 Click 和 Flask 如何指定默认值,而不是如何发现默认值。
  • 感谢您的额外建议。但是你能解释一下为什么你会在“my_library”定义中使用一个标记值而不是直接使用全局变量,比如:some_func(arg=some_func_default)
  • 我很早就概括了,以防默认值是可变的。
【解决方案2】:

您可以使用专门为此用例制作的 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]

【讨论】:

  • 看起来很有趣,感谢您将我指向此资源!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-02
相关资源
最近更新 更多