From b7f7c9342b7b23b5c04bc9974403e6e96ce8bec2 Mon Sep 17 00:00:00 2001 From: jesopo Date: Mon, 24 Sep 2018 13:10:39 +0100 Subject: [PATCH] Better communicate issues with load/unload/reload of modules --- ModuleManager.py | 61 +++++++++++++++++++++++++++++++++------------- modules/modules.py | 46 ++++++++++++++++------------------ 2 files changed, 65 insertions(+), 42 deletions(-) diff --git a/ModuleManager.py b/ModuleManager.py index 1ecb1f7a..a9d9f7d4 100644 --- a/ModuleManager.py +++ b/ModuleManager.py @@ -2,6 +2,23 @@ import glob, imp, inspect, os, sys, uuid BITBOT_HOOKS_MAGIC = "__bitbot_hooks" +class ModuleException(Exception): + pass +class ModuleWarning(Exception): + pass + +class ModuleNotFoundException(ModuleException): + pass +class ModuleNameCollisionException(ModuleException): + pass +class ModuleLoadException(ModuleException): + pass +class ModuleUnloadException(ModuleException): + pass + +class ModuleNotLoadedWarning(ModuleWarning): + pass + class BaseModule(object): def __init__(self, bot, events, exports): pass @@ -33,28 +50,32 @@ class ModuleManager(object): # this is a hashflag if line == "#--ignore": # nope, ignore this module. - return None + raise ModuleNotLoadedWarning("module ignored") elif line_split[0] == "#--require-config" and len( line_split) > 1: if not line_split[1].lower() in self.bot.config or not self.bot.config[ line_split[1].lower()]: # nope, required config option not present. - return None + raise ModuleNotLoadedWarning( + "required config not present") elif line_split[0] == "#--require-module" and len( line_split) > 1: if not "bitbot_%s" % line_split[1].lower() in sys.modules: if not line_split[1].lower() in self.waiting_requirement: self.waiting_requirement[line_split[1].lower()] = set([]) self.waiting_requirement[line_split[1].lower()].add(path) - return None + raise ModuleNotLoadedWarning( + "waiting for requirement") else: break module = imp.load_source(name, path) if not hasattr(module, "Module"): - raise ImportError("module '%s' doesn't have a Module class.") + raise ModuleLoadException("module '%s' doesn't have a " + "'Module' class.") if not inspect.isclass(module.Module): - raise ImportError("module '%s' has a Module attribute but it is not a class.") + raise ModuleLoadException("module '%s' has a 'Module' attribute " + "but it is not a class.") context = str(uuid.uuid4()) context_events = self.events.new_context(context) @@ -83,26 +104,32 @@ class ModuleManager(object): def load_module(self, name): try: module = self._load_module(name) - except ImportError as e: - self.bot.log.error("failed to load module \"%s\": %s", - [name, e.msg]) - return - if module: - self.modules[module._import_name] = module - if name in self.waiting_requirement: - for requirement_name in self.waiting_requirement: - self.load_module(requirement_name) - self.bot.log.info("Module '%s' loaded", [name]) - else: + except ModuleWarning as warning: self.bot.log.error("Module '%s' not loaded", [name]) + raise + except Exception as e: + self.bot.log.error("Failed to load module \"%s\": %s", + [name, e.msg]) + raise + + self.modules[module._import_name] = module + if name in self.waiting_requirement: + for requirement_name in self.waiting_requirement: + self.load_module(requirement_name) + self.bot.log.info("Module '%s' loaded", [name]) def load_modules(self, whitelist=[], blacklist=[]): for path in self.list_modules(): name = self._module_name(path) if name in whitelist or (not whitelist and not name in blacklist): - self.load_module(name) + try: + self.load_module(name) + except ModuleWarning: + pass def unload_module(self, name): + if not name in self.modules: + raise ModuleNotFoundException() module = self.modules[name] del self.modules[name] diff --git a/modules/modules.py b/modules/modules.py index 109baeff..154a1765 100644 --- a/modules/modules.py +++ b/modules/modules.py @@ -1,10 +1,8 @@ - +import ModuleManager class Module(object): def __init__(self, bot, events, exports): self.bot = bot - self.module_name = False - self.silent = False events.on("received.command.loadmodule").hook(self.load, min_args=1, permission="load-module", help="Load a module", @@ -43,37 +41,35 @@ class Module(object): self.bot.modules.unload_module(name) event["stdout"].write("Unloaded '%s'" % name) - def reload(self, event): - name = self.module_name if self.module_name != False else event[ - "args_split"][0].lower() - if not name in self.bot.modules.modules: - if self.silent == False: - event["stderr"].write("Module '%s' isn't loaded" % name) - return + def _reload(self, name): self.bot.modules.unload_module(name) self.bot.modules.load_module(name) - if self.silent == False: - event["stdout"].write("Reloaded '%s'" % name) + def reload(self, event): + name = event["args_split"][0].lower() + try: + self._reload(name) + except ModuleManager.ModuleNotFoundException: + event["stderr"].write("Module '%s' isn't loaded" % name) + return + event["stdout"].write("Reloaded '%s'" % name) def reload_all(self, event): - modules_reloaded = [] - self.silent = True - - for name, value in self.bot.modules.modules.items(): - if name in modules_reloaded: - pass - - self.module_name = name - self.reload(event) - modules_reloaded.append(name) + reloaded = [] + failed = [] + for name in self.bot.modules.modules.keys(): + try: + self._reload(name) + except ModuleManager.ModuleNotFoundException: + failed.append + if not self._reload(event): + failed.append(name) + else: + reloaded.append(name) event["stdout"].write("Reloaded modules: %s" % \ " ".join(modules_reloaded)) - self.silent = False - self.module_name = False - def enable(self, event): name = event["args_split"][0].lower() blacklist = self.bot.get_setting("module-blacklist", [])