From 8b73a74609a8677cd1ced5aedd6b39f67019ecf5 Mon Sep 17 00:00:00 2001 From: lanvent Date: Thu, 6 Apr 2023 21:03:17 +0800 Subject: [PATCH] fix: bug when reinstall plugin --- channel/wechatmp/wechatmp_channel.py | 2 +- common/package_manager.py | 18 ++++++++-- plugins/godcmd/godcmd.py | 6 +--- plugins/plugin_manager.py | 54 ++++++++++++++++++++-------- 4 files changed, 56 insertions(+), 24 deletions(-) diff --git a/channel/wechatmp/wechatmp_channel.py b/channel/wechatmp/wechatmp_channel.py index c7f7142..1967245 100644 --- a/channel/wechatmp/wechatmp_channel.py +++ b/channel/wechatmp/wechatmp_channel.py @@ -41,7 +41,7 @@ class WechatMPChannel(ChatChannel): urls = ( '/wx', 'SubsribeAccountQuery', ) - app = web.application(urls, globals()) + app = web.application(urls, globals(), autoreload=False) port = conf().get('wechatmp_port', 8080) web.httpserver.runsimple(app.wsgifunc(), ('0.0.0.0', port)) diff --git a/common/package_manager.py b/common/package_manager.py index 6430934..da9b982 100644 --- a/common/package_manager.py +++ b/common/package_manager.py @@ -1,3 +1,4 @@ +import time import pip def install(package): @@ -7,9 +8,20 @@ def install_requirements(file): pip.main(['install', '-r', file, "--upgrade"]) def check_dulwich(): + needwait = False + for i in range(2): + if needwait: + time.sleep(3) + needwait = False + try: + import dulwich + return + except ImportError: + try: + install('dulwich') + except: + needwait = True try: import dulwich - return except ImportError: - install('dulwich') - raise ImportError("Unable to import dulwich") \ No newline at end of file + raise ImportError("Unable to import dulwich") \ No newline at end of file diff --git a/plugins/godcmd/godcmd.py b/plugins/godcmd/godcmd.py index 25a26d6..d294fb3 100644 --- a/plugins/godcmd/godcmd.py +++ b/plugins/godcmd/godcmd.py @@ -310,11 +310,7 @@ class Godcmd(Plugin): if len(args) != 1: ok, result = False, "请提供插件名" else: - ok = PluginManager().enable_plugin(args[0]) - if ok: - result = "插件已启用" - else: - result = "插件不存在" + ok, result = PluginManager().enable_plugin(args[0]) elif cmd == "disablep": if len(args) != 1: ok, result = False, "请提供插件名" diff --git a/plugins/plugin_manager.py b/plugins/plugin_manager.py index d30a02e..a2b2bc8 100644 --- a/plugins/plugin_manager.py +++ b/plugins/plugin_manager.py @@ -1,8 +1,10 @@ # encoding:utf-8 import importlib +import importlib.util import json import os +import sys from common.singleton import singleton from common.sorted_dict import SortedDict from .event import * @@ -18,6 +20,7 @@ class PluginManager: self.instances = {} self.pconf = {} self.current_plugin_path = None + self.loaded = {} def register(self, name: str, desire_priority: int = 0, **kwargs): def wrapper(plugincls): @@ -30,9 +33,10 @@ class PluginManager: plugincls.namecn = kwargs.get('namecn') if kwargs.get('namecn') != None else name plugincls.hidden = kwargs.get('hidden') if kwargs.get('hidden') != None else False plugincls.enabled = True + if self.current_plugin_path == None: + raise Exception("Plugin path not set") self.plugins[name.upper()] = plugincls logger.info("Plugin %s_v%s registered, path=%s" % (name, plugincls.version, plugincls.path)) - return plugincls return wrapper def save_config(self): @@ -58,6 +62,7 @@ class PluginManager: def scan_plugins(self): logger.info("Scaning plugins ...") plugins_dir = "./plugins" + raws = [self.plugins[name] for name in self.plugins] for plugin_name in os.listdir(plugins_dir): plugin_path = os.path.join(plugins_dir, plugin_name) if os.path.isdir(plugin_path): @@ -68,17 +73,27 @@ class PluginManager: import_path = "plugins.{}".format(plugin_name) try: self.current_plugin_path = plugin_path - main_module = importlib.import_module(import_path) + if plugin_path in self.loaded: + if self.loaded[plugin_path] == None: + logger.info("reload module %s" % plugin_name) + self.loaded[plugin_path] = importlib.reload(sys.modules[import_path]) + dependent_module_names = [name for name in sys.modules.keys() if name.startswith( import_path+ '.')] + for name in dependent_module_names: + logger.info("reload module %s" % name) + importlib.reload(sys.modules[name]) + else: + self.loaded[plugin_path] = importlib.import_module(import_path) + self.current_plugin_path = None except Exception as e: - logger.warn("Failed to import plugin %s: %s" % (plugin_name, e)) + logger.exception("Failed to import plugin %s: %s" % (plugin_name, e)) continue pconf = self.pconf - new_plugins = [] + news = [self.plugins[name] for name in self.plugins] + new_plugins = list(set(news) - set(raws)) modified = False for name, plugincls in self.plugins.items(): rawname = plugincls.name if rawname not in pconf["plugins"]: - new_plugins.append(plugincls) modified = True logger.info("Plugin %s not found in pconfig, adding to pconfig..." % name) pconf["plugins"][rawname] = {"enabled": plugincls.enabled, "priority": plugincls.priority} @@ -95,14 +110,16 @@ class PluginManager: self.listening_plugins[event].sort(key=lambda name: self.plugins[name].priority, reverse=True) def activate_plugins(self): # 生成新开启的插件实例 + failed_plugins = [] for name, plugincls in self.plugins.items(): if plugincls.enabled: if name not in self.instances: try: instance = plugincls() except Exception as e: - logger.warn("Failed to create init %s, diabled. %s" % (name, e)) + logger.warn("Failed to init %s, diabled. %s" % (name, e)) self.disable_plugin(name) + failed_plugins.append(name) continue self.instances[name] = instance for event in instance.handlers: @@ -110,6 +127,7 @@ class PluginManager: self.listening_plugins[event] = [] self.listening_plugins[event].append(name) self.refresh_order() + return failed_plugins def reload_plugin(self, name:str): name = name.upper() @@ -159,15 +177,17 @@ class PluginManager: def enable_plugin(self, name:str): name = name.upper() if name not in self.plugins: - return False + return False, "插件不存在" if not self.plugins[name].enabled : self.plugins[name].enabled = True rawname = self.plugins[name].name self.pconf["plugins"][rawname]["enabled"] = True self.save_config() - self.activate_plugins() - return True - return True + failed_plugins = self.activate_plugins() + if name in failed_plugins: + return False, "插件开启失败" + return True, "插件已开启" + return True, "插件已开启" def disable_plugin(self, name:str): name = name.upper() @@ -206,12 +226,12 @@ class PluginManager: repo = source["repo"][repo]["url"] match = re.match(r"^(https?:\/\/|git@)([^\/:]+)[\/:]([^\/:]+)\/(.+).git$", repo) if not match: - return False, "source中的仓库地址不合法" + return False, "安装插件失败,source中的仓库地址不合法" else: - return False, "仓库地址不合法" + return False, "安装插件失败,仓库地址不合法" except Exception as e: logger.error("Failed to install plugin, {}".format(e)) - return False, "安装插件失败" + return False, "安装插件失败,请检查仓库地址是否正确" dirname = os.path.join("./plugins",match.group(4)) try: repo = porcelain.clone(repo, dirname, checkout=True) @@ -221,7 +241,7 @@ class PluginManager: return True, "安装插件成功,请扫描插件或重启程序" except Exception as e: logger.error("Failed to install plugin, {}".format(e)) - return False, "安装插件失败" + return False, "安装插件失败,"+str(e) def uninstall_plugin(self, name:str): name = name.upper() @@ -233,8 +253,12 @@ class PluginManager: try: import shutil shutil.rmtree(dirname) + rawname = self.plugins[name].name del self.plugins[name] + del self.pconf["plugins"][rawname] + self.loaded[dirname] = None + self.save_config() return True, "卸载插件成功" except Exception as e: logger.error("Failed to uninstall plugin, {}".format(e)) - return False, "卸载插件失败" \ No newline at end of file + return False, "卸载插件失败,请手动删除文件夹完成卸载,"+str(e) \ No newline at end of file