我正在try 创建一个简单的可插入FastAPI应用程序,插件可以在其中添加或不添加API端点

这是我的文件夹 struct :

folder structure

服务器py

import importlib
import pkgutil
from pathlib import Path

import uvicorn
from fastapi import FastAPI

PLUGINS_PATH = Path(__file__).parent.joinpath("plugins")
app = FastAPI()


def import_module(module_name):
    """Imports a module by it's name from plugins folder."""
    module = f"plugins.{module_name}"
    return importlib.import_module(module, ".")


def load_plugins() -> list:
    """Import plugins from plugins folder."""
    loaded_apps = []
    for _, application, _ in pkgutil.iter_modules([str(PLUGINS_PATH)]):
        module = import_module(application)
        print(
            f"Loaded app: {module.__meta__['plugin_name']} -- version: {module.__meta__['version']}"
        )
        loaded_apps.append(module)
    return loaded_apps


@app.get("/")
def main():
    return "Hello World!"


if __name__ == "__main__":
    plugins = load_plugins()

    for plugin in plugins:
        """Register the plugins router."""
        if "router" in plugin.__dir__():
            app_router = plugin.router
            app.include_router(app_router)

    uvicorn.run("server:app", host="localhost", port=8000, reload=True)

在我的插件文件夹中,我有:

plugins/non_api_plugin/__init__.py人:

__meta__ = {"plugin_name": "NON API plugin", "version": "0.0.1"}

plugins/<v1|v2>/__init__.py

from .routes import routes as router

__meta__ = {"plugin_name": "API <v1|v2>", "version": "0.0.1"}

还有路由.py文件:

from fastapi import APIRouter

routes = APIRouter(prefix="/<v1|v2>")


@routes.get("/")
def novels():
    return "Hello World from <v1|v2>"

当我运行服务器时,会加载插件并记录它们的信息,但不会加载API端点.

我错过了什么?我最好的猜测是,我的插件加载系统在某个点上是错误的.

推荐答案

您正在代码路径中运行插件注册代码,该代码路径仅在脚本具有主上下文时运行:

if __name__ == "__main__":
    # plugin registration

由于您使用此部分来调用uvicorn,因此uvicorn会自动启动并导入您提供给它的模块.uvicorn启动时,它会导入应用程序并确定哪些端点可用——但现在您自己的脚本不再是应用程序的主上下文,因此__name__ == "__main__"块内的任何内容都不会运行.

一旦将插件注册块移出该范围,您就会看到预期的行为:

plugins = load_plugins()

for plugin in plugins:
    """Register the plugins router."""
    if "router" in plugin.__dir__():
        app_router = plugin.router
        app.include_router(app_router, prefix='/foo')  # I'd let the plugin name be the prefix here to avoid plugins using the same prefix

if __name__ == "__main__":
    uvicorn.run("server:app", host="localhost", port=8000, reload=True)

Python相关问答推荐

如何访问所有文件,例如环境变量

如何在虚拟Python环境中运行Python程序?

如何获取numpy数组的特定索引值?

如何根据一列的值有条件地 Select 前N个组,然后按两列分组?

我的字符串搜索算法的平均时间复杂度和最坏时间复杂度是多少?

使用BeautifulSoup抓取所有链接

交替字符串位置的正则表达式

为什么Python内存中的列表大小与文档不匹配?

如何在Gekko中使用分层条件约束

使用tqdm的进度条

使用嵌套对象字段的Qdrant过滤

当HTTP 201响应包含 Big Data 的POST请求时,应该是什么?  

如何在PythonPandas 中对同一个浮动列进行逐行划分?

使用xlsxWriter在EXCEL中为数据帧的各行上色

为什么按下按钮后屏幕的 colored颜色 保持不变?

使用pythonminidom过滤XML文件

如何仅使用数据帧操作获得特定的唯一数据帧组合?

设计添加和搜索词的数据 struct :Leetcode211

如何从多个词典中制作Pandas 数据帧?

Pandas:根据系列词典中的值筛选行