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!
This commit is contained in:
Dan 2018-09-01 11:40:57 +01:00 committed by GitHub
parent 7db9a8ea2b
commit ba065ad646
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 80 additions and 23 deletions

View file

@ -179,7 +179,7 @@ class EventHook(object):
returns.append(hook.call(event)) returns.append(hook.call(event))
except Exception as e: except Exception as e:
traceback.print_exc() 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) event_path], exc_info=True)
called += 1 called += 1
@ -202,11 +202,9 @@ class EventHook(object):
child_name_lower = child_name.lower() child_name_lower = child_name.lower()
if child_name_lower in self._children: if child_name_lower in self._children:
del self._children[child_name_lower] del self._children[child_name_lower]
def get_children(self):
return self._children.keys()
def check_purge(self): 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: ) == 0 and not self.parent == None:
self.parent.remove_child(self.name) self.parent.remove_child(self.name)
self.parent.check_purge() self.parent.check_purge()
@ -218,9 +216,16 @@ class EventHook(object):
def purge_context(self, context): def purge_context(self, context):
if self.has_context(context): if self.has_context(context):
self.remove_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) child.purge_context(context)
if child.is_empty():
self.remove_child(child_name)
def get_hooks(self): def get_hooks(self):
return sorted(self._hooks + list(itertools.chain.from_iterable( return sorted(self._hooks + list(itertools.chain.from_iterable(
self._context_hooks.values())), key=lambda e: e.priority) 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

View file

@ -10,13 +10,15 @@ class ModuleManager(object):
def list_modules(self): def list_modules(self):
return sorted(glob.glob(os.path.join(self.directory, "*.py"))) return sorted(glob.glob(os.path.join(self.directory, "*.py")))
def module_name(self, filename): def _module_name(self, path):
return os.path.basename(filename).rsplit(".py", 1)[0].lower() 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): def _load_module(self, name):
name = self.module_name(filename) path = self._module_path(name)
with open(filename) as module_file: with open(path) as module_file:
while True: while True:
line = module_file.readline().strip() line = module_file.readline().strip()
line_split = line.split(" ") line_split = line.split(" ")
@ -36,11 +38,11 @@ class ModuleManager(object):
if not "bitbot_%s" % line_split[1].lower() in sys.modules: if not "bitbot_%s" % line_split[1].lower() in sys.modules:
if not line_split[1].lower() in self.waiting_requirement: 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()] = set([])
self.waiting_requirement[line_split[1].lower()].add(filename) self.waiting_requirement[line_split[1].lower()].add(path)
return None return None
else: else:
break break
module = imp.load_source(name, filename) module = imp.load_source(name, path)
if not hasattr(module, "Module"): if not hasattr(module, "Module"):
raise ImportError("module '%s' doesn't have a Module class.") 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.") "module name '%s' attempted to be used twice.")
return module_object return module_object
def load_module(self, filename): def load_module(self, name):
name = self.module_name(filename)
try: try:
module = self._load_module(filename) module = self._load_module(name)
except ImportError as e: 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 return
if module: if module:
self.modules[module._import_name] = module self.modules[module._import_name] = module
if name in self.waiting_requirement: if name in self.waiting_requirement:
for filename in self.waiting_requirement: for requirement_name in self.waiting_requirement:
self.load_module(filename) self.load_module(requirement_name)
sys.stderr.write("module '%s' loaded.\n" % filename) self.bot.log.info("Module '%s' loaded", [name])
else: 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): def load_modules(self, whitelist=None):
for filename in self.list_modules(): for path in self.list_modules():
if whitelist == None or filename in whitelist: name = self._module_name(path)
self.load_module(filename) 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

39
modules/modules.py Normal file
View file

@ -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="<module-name>")
events.on("received.command.unloadmodule").hook(self.unload,
min_args=1, permission="unload-module", help="Unload a module",
usage="<module-name>")
events.on("received.command.reloadmodule").hook(self.reload,
min_args=1, permission="reload-module", help="Reload a module",
usage="<module-name>")
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)