diff --git a/applications/__init__.py b/applications/__init__.py index af5bb8e..d2b460b 100644 --- a/applications/__init__.py +++ b/applications/__init__.py @@ -8,6 +8,7 @@ from applications.view import init_bps def create_app(): app = Flask(os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) + # 引入配置 app.config.from_object(BaseConfig) diff --git a/applications/common/script/__init__.py b/applications/common/script/__init__.py index 81450ef..3801751 100644 --- a/applications/common/script/__init__.py +++ b/applications/common/script/__init__.py @@ -1,7 +1,9 @@ from flask import Flask from .admin import admin_cli +from applications.extensions.init_plugins import broadcast_execute def init_script(app: Flask): app.cli.add_command(admin_cli) + broadcast_execute(app, 'event_finish') diff --git a/applications/common/script/admin.py b/applications/common/script/admin.py index 826ddfa..11fc43b 100644 --- a/applications/common/script/admin.py +++ b/applications/common/script/admin.py @@ -572,4 +572,4 @@ def init_db(): print("用户角色数据存入") add_role_power() print("角色权限数据存入") - print("数据初始化完成,请使用run脚本运行") + print("数据初始化完成,请使用run脚本运行") diff --git a/applications/extensions/__init__.py b/applications/extensions/__init__.py index fe9762c..b1f9913 100644 --- a/applications/extensions/__init__.py +++ b/applications/extensions/__init__.py @@ -7,9 +7,15 @@ from .init_mail import init_mail, mail as flask_mail from .init_upload import init_upload from .init_migrate import init_migrate from .init_session import init_session +from .init_plugins import register_plugin, broadcast_execute def init_plugs(app: Flask) -> None: + # 注册插件 + register_plugin(app) + broadcast_execute(app, 'event_begin') + + # 注册 Flask 功能 init_login_manager(app) init_databases(app) init_template_directives(app) @@ -17,4 +23,4 @@ def init_plugs(app: Flask) -> None: init_mail(app) init_upload(app) init_migrate(app) - init_session(app) \ No newline at end of file + init_session(app) diff --git a/applications/extensions/init_plugins.py b/applications/extensions/init_plugins.py new file mode 100644 index 0000000..80d1264 --- /dev/null +++ b/applications/extensions/init_plugins.py @@ -0,0 +1,61 @@ +import os + +from flask import Flask +from flask import Blueprint + +import json +import importlib + +plugin_bp = Blueprint('plugin', __name__, url_prefix='/plugin') + +PLUGIN_ENABLE_FOLDERS = [] +PLUGIN_IMPORTLIB = [] + + +def register_plugin(app: Flask): + """ + 获取所有插件并加载 + """ + global PLUGIN_ENABLE_FOLDERS + app.register_blueprint(plugin_bp) + # 载入插件过程 + # plugin_folder 配置的是插件的文件夹名 + PLUGIN_ENABLE_FOLDERS = app.config['PLUGIN_ENABLE_FOLDERS'] + + for plugin_folder in PLUGIN_ENABLE_FOLDERS: + + plugin_info = { + 'plugin_name': plugin_folder + } + + try: + if os.path.exists("plugins/" + plugin_folder + "/__init__.json"): + with open("plugins/" + plugin_folder + "/__init__.json", "r", encoding='utf-8') as f: + plugin_info = json.loads(f.read()) + + # 将插件全部载入 + PLUGIN_IMPORTLIB.append(importlib.import_module('plugins.' + plugin_folder)) + + print(f" * Plugin: Loaded plugin: {plugin_info['plugin_name']} .") + except BaseException as e: + info = f" * Plugin: Crash a error when loading {plugin_info['plugin_name'] if len(plugin_info) != 0 else 'plugin'} :" + "\n" + app.logger.error(info) + app.logger.exception(e) + + +def broadcast_execute(app: Flask, function_name): + for plugin in PLUGIN_IMPORTLIB: + + try: + # 初始化完成事件 + try: + getattr(plugin, function_name)(app) + except AttributeError: # 没有插件启用事件就不调用 + pass + + except BaseException as e: + app.logger.exception(e) + + if function_name == 'event_finish': + with app.app_context(): + broadcast_execute(app, 'event_context') \ No newline at end of file diff --git a/applications/view/__init__.py b/applications/view/__init__.py index e1fb713..3e71f71 100644 --- a/applications/view/__init__.py +++ b/applications/view/__init__.py @@ -1,7 +1,9 @@ from applications.view.system import register_system_bps -from applications.view.plugin import register_plugin_views +from applications.extensions.init_plugins import broadcast_execute def init_bps(app): register_system_bps(app) - register_plugin_views(app) + + # 插件初始化函数 + broadcast_execute(app, 'event_init') diff --git a/applications/view/plugin/__init__.py b/applications/view/plugin/__init__.py deleted file mode 100644 index 7d16edd..0000000 --- a/applications/view/plugin/__init__.py +++ /dev/null @@ -1,34 +0,0 @@ -from flask import Flask -from flask import Blueprint -import json -import traceback -import importlib -plugin_bp = Blueprint('plugin', __name__, url_prefix='/plugin') -PLUGIN_ENABLE_FOLDERS = [] - - -def register_plugin_views(app: Flask): - global PLUGIN_ENABLE_FOLDERS - app.register_blueprint(plugin_bp) - # 载入插件过程 - # plugin_folder 配置的是插件的文件夹名 - PLUGIN_ENABLE_FOLDERS = app.config['PLUGIN_ENABLE_FOLDERS'] - for plugin_folder in PLUGIN_ENABLE_FOLDERS: - plugin_info = {} - try: - with open("plugins/" + plugin_folder + "/__init__.json", "r", encoding='utf-8') as f: - plugin_info = json.loads(f.read()) - # 初始化完成事件 - try: - getattr(importlib.import_module('plugins.' + plugin_folder), "event_init")(app) - except AttributeError: # 没有插件启用事件就不调用 - pass - - print(f" * Plugin: Loaded plugin: {plugin_info['plugin_name']} .") - except BaseException as e: - info = f" * Plugin: Crash a error when loading {plugin_info['plugin_name'] if len(plugin_info) != 0 else 'plugin'} :" + "\n" - info += 'str(Exception):\t' + str(Exception) + "\n" - info += 'str(e):\t\t' + str(e) + "\n" - info += 'repr(e):\t' + repr(e) + "\n" - info += 'traceback.format_exc():\n%s' + traceback.format_exc() - print(info) diff --git a/docs/source/practices/plugin.rst b/docs/source/practices/plugin.rst index 8716732..321bfb8 100644 --- a/docs/source/practices/plugin.rst +++ b/docs/source/practices/plugin.rst @@ -71,10 +71,10 @@ 这个 JSON 文件中,记录了基本的插件名称与插件版本,以及插件的介绍。**在更新之后,此文件可以不存在,插件的名称默认为文件夹名。** -编写插件入口 +编写插件事件 ------------------- -插件入口位于 `__init__.py` 中,请确保 `__init__.py` 文件一定包含 `event_init(app: Flask)` 函数,如下: +插件入口位于 `__init__.py` 中,一般来说请确保 `__init__.py` 文件包含 `event_init(app: Flask)` 函数,如下: .. code-block:: python @@ -122,9 +122,22 @@ 这样可以使目录架构更加清晰。 +另外,插件还有其他三个事件: + +.. code-block:: python + + def event_begin(app: Flask): # 在项目所有功能注册之前调用 + pass + + def event_finish(app: Flask): # 在项目所有功能注册之后调用(插件已经加载完毕) + pass + + def event_context(app: Flask): # Flask 初始化完成,等待第一个请求之前,等同于 with app.app_context(): + pass + .. important:: - 注意不要直接在 `__init__.py` 的 `event_init` 函数外直接写存在阻塞的代码,不然项目 Flask 将不能初始化完成。 + 注意不要直接在 `__init__.py` 的函数外直接写存在阻塞的代码,不然项目 Flask 将不能初始化完成。 .. note:: diff --git a/plugins/helloworld/__init__.json b/plugins/helloworld/__init__.json new file mode 100644 index 0000000..1373e2d --- /dev/null +++ b/plugins/helloworld/__init__.json @@ -0,0 +1,5 @@ +{ + "plugin_name": "Hello World", + "plugin_version": "1.0.0.1", + "plugin_description": "一个测试的插件。" +} \ No newline at end of file diff --git a/plugins/helloworld/__init__.py b/plugins/helloworld/__init__.py index a875cce..c6c6fc1 100644 --- a/plugins/helloworld/__init__.py +++ b/plugins/helloworld/__init__.py @@ -6,11 +6,33 @@ import os from flask import Flask from .main import helloworld_blueprint +from applications.models import Dept +from applications.common import curd +from applications.schemas import DeptSchema + # 获取插件所在的目录(结尾没有分割符号) dir_path = os.path.dirname(__file__).replace("\\", "/") folder_name = dir_path[dir_path.rfind("/") + 1:] # 插件文件夹名称 +def event_begin(app: Flask): # 在项目所有功能注册之前调用 + print("所有功能初始化之前加载") + + def event_init(app: Flask): """初始化完成时会调用这里""" + print("初始插件初始化视图") app.register_blueprint(helloworld_blueprint) + + +def event_finish(app: Flask): # 在项目所有功能注册之后调用(插件已经加载完毕) + print("所有初始化完毕") + + +def event_context(app: Flask): # Flask 初始化完成,等待第一个请求之前,等同于 with app.app_context(): + print("第一个请求来之前加载") + + dept = Dept.query.order_by(Dept.sort).all() + power_data = curd.model_to_dicts(schema=DeptSchema, data=dept) + + print(power_data)