带有 APIRouter 插件系统的 FastAPI 不工作

FastAPI with APIRouter plugin system not working

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

这是我的文件夹结构:

server.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"}

和routes.py个文件:

from fastapi import APIRouter

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


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

当我 运行 服务器时,插件被加载并记录它们的信息,但是 API 端点没有被加载。

我在这里缺少什么?我最好的猜测是我的插件加载系统在某些时候是错误的。

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

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)