From ba065ad646748719aa4ca986193e3d40601fc91e Mon Sep 17 00:00:00 2001 From: Dan <294904+dngfx@users.noreply.github.com> Date: Sat, 1 Sep 2018 11:40:57 +0100 Subject: [PATCH] omg it's the future (#15) * Add Database.UserChannelSettings.find_all_by_setting * Turns out we didn't need find_all_by_setting * Actually, we do need find_all_by_setting * Table name typo * Add Utils.bold and Utils.underline * Added functionality to load, unload and reload modules from a command! --- EventManager.py | 15 +++++++++----- ModuleManager.py | 49 +++++++++++++++++++++++++++++----------------- modules/modules.py | 39 ++++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 23 deletions(-) create mode 100644 modules/modules.py diff --git a/EventManager.py b/EventManager.py index 4ca5837c..326192ad 100644 --- a/EventManager.py +++ b/EventManager.py @@ -179,7 +179,7 @@ class EventHook(object): returns.append(hook.call(event)) except Exception as e: traceback.print_exc() - self.bot.log.error("failed to call event \"%s", [ + self.bot.log.error("failed to call event \"%s\"", [ event_path], exc_info=True) called += 1 @@ -202,11 +202,9 @@ class EventHook(object): child_name_lower = child_name.lower() if child_name_lower in self._children: del self._children[child_name_lower] - def get_children(self): - return self._children.keys() def check_purge(self): - if len(self.get_hooks()) == 0 and len(self._children + if len(self.get_hooks()) == 0 and len(self.get_children() ) == 0 and not self.parent == None: self.parent.remove_child(self.name) self.parent.check_purge() @@ -218,9 +216,16 @@ class EventHook(object): def purge_context(self, context): if self.has_context(context): self.remove_context(context) - for child in self.get_children(): + for child_name in self.get_children()[:]: + child = self.get_child(child_name) child.purge_context(context) + if child.is_empty(): + self.remove_child(child_name) def get_hooks(self): return sorted(self._hooks + list(itertools.chain.from_iterable( self._context_hooks.values())), key=lambda e: e.priority) + def get_children(self): + return list(self._children.keys()) + def is_empty(self): + return len(self.get_hooks() + self.get_children()) == 0 diff --git a/ModuleManager.py b/ModuleManager.py index 812edb71..404502e8 100644 --- a/ModuleManager.py +++ b/ModuleManager.py @@ -10,13 +10,15 @@ class ModuleManager(object): def list_modules(self): return sorted(glob.glob(os.path.join(self.directory, "*.py"))) - def module_name(self, filename): - return os.path.basename(filename).rsplit(".py", 1)[0].lower() + def _module_name(self, path): + return os.path.basename(path).rsplit(".py", 1)[0].lower() + def _module_path(self, name): + return os.path.join(self.directory, "%s.py" % name) - def _load_module(self, filename): - name = self.module_name(filename) + def _load_module(self, name): + path = self._module_path(name) - with open(filename) as module_file: + with open(path) as module_file: while True: line = module_file.readline().strip() line_split = line.split(" ") @@ -36,11 +38,11 @@ class ModuleManager(object): 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(filename) + self.waiting_requirement[line_split[1].lower()].add(path) return None else: break - module = imp.load_source(name, filename) + module = imp.load_source(name, path) if not hasattr(module, "Module"): raise ImportError("module '%s' doesn't have a Module class.") @@ -60,23 +62,34 @@ class ModuleManager(object): "module name '%s' attempted to be used twice.") return module_object - def load_module(self, filename): - name = self.module_name(filename) + def load_module(self, name): try: - module = self._load_module(filename) + module = self._load_module(name) except ImportError as e: - sys.stderr.write("module '%s' not loaded: Could not resolve import.\n" % filename) + 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 filename in self.waiting_requirement: - self.load_module(filename) - sys.stderr.write("module '%s' loaded.\n" % filename) + for requirement_name in self.waiting_requirement: + self.load_module(requirement_name) + self.bot.log.info("Module '%s' loaded", [name]) else: - sys.stderr.write("module '%s' not loaded.\n" % filename) + self.bot.log.error("Module '%s' not loaded", [name]) def load_modules(self, whitelist=None): - for filename in self.list_modules(): - if whitelist == None or filename in whitelist: - self.load_module(filename) + for path in self.list_modules(): + name = self._module_name(path) + if whitelist == None or name in whitelist: + self.load_module(name) + + def unload_module(self, name): + module = self.modules[name] + del self.modules[name] + + event_context = module._event_context + self.events.purge_context(event_context) + + del sys.modules[name] + del module diff --git a/modules/modules.py b/modules/modules.py new file mode 100644 index 00000000..0138be1b --- /dev/null +++ b/modules/modules.py @@ -0,0 +1,39 @@ + + +class Module(object): + def __init__(self, bot, events): + self.bot = bot + events.on("received.command.loadmodule").hook(self.load, + min_args=1, permission="load-module", help="Load a module", + usage="") + events.on("received.command.unloadmodule").hook(self.unload, + min_args=1, permission="unload-module", help="Unload a module", + usage="") + events.on("received.command.reloadmodule").hook(self.reload, + min_args=1, permission="reload-module", help="Reload a module", + usage="") + + def load(self, event): + name = event["args_split"][0].lower() + if name in self.bot.modules.modules: + event["stderr"].write("Module '%s' is already loaded" % name) + return + self.bot.modules.load_module(name) + event["stdout"].write("Loaded '%s'" % name) + + def unload(self, event): + name = event["args_split"][0].lower() + if not name in self.bot.modules.modules: + event["stderr"].write("Module '%s' isn't loaded" % name) + return + self.bot.modules.unload_module(name) + event["stdout"].write("Unloaded '%s'" % name) + + def reload(self, event): + name = event["args_split"][0].lower() + if not name in self.bot.modules.modules: + event["stderr"].write("Module '%s' isn't loaded" % name) + return + self.bot.modules.unload_module(name) + self.bot.modules.load_module(name) + event["stdout"].write("Reloaded '%s'" % name)