efi*_*ida 3 python plugins python-importlib fastapi
我正在尝试创建一个简单的可插入 FastAPI 应用程序,其中插件可以添加或不添加 API 端点
这是我的文件夹结构:
服务器.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)
Run Code Online (Sandbox Code Playgroud)
在我的插件文件夹中我有:
这plugins/non_api_plugin/__init__.py:
__meta__ = {"plugin_name": "NON API plugin", "version": "0.0.1"}
Run Code Online (Sandbox Code Playgroud)
这plugins/<v1|v2>/__init__.py
from .routes import routes as router
__meta__ = {"plugin_name": "API <v1|v2>", "version": "0.0.1"}
Run Code Online (Sandbox Code Playgroud)
和routes.py文件:
from fastapi import APIRouter
routes = APIRouter(prefix="/<v1|v2>")
@routes.get("/")
def novels():
return "Hello World from <v1|v2>"
Run Code Online (Sandbox Code Playgroud)
当我运行服务器时,会加载插件并记录其信息,但不会加载 API 端点。
我在这里缺少什么?我最好的猜测是我的插件加载系统在某些时候是错误的。
您正在代码路径中运行插件注册代码,该代码路径仅在脚本具有主上下文时运行:
if __name__ == "__main__":
# plugin registration
Run Code Online (Sandbox Code Playgroud)
由于您使用此部分来调用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)
Run Code Online (Sandbox Code Playgroud)