X-Git-Url: https://jfr.im/git/erebus.git/blobdiff_plain/db75daab8e080fef8e9dbe7796492b24945e8707..HEAD:/ctlmod.py diff --git a/ctlmod.py b/ctlmod.py index 24dd236..3e30892 100644 --- a/ctlmod.py +++ b/ctlmod.py @@ -1,41 +1,112 @@ # Erebus IRC bot - Author: John Runyon +# vim: fileencoding=utf-8 # module loading/unloading/tracking code -import sys +from __future__ import print_function + +import sys, time, importlib, traceback import modlib +if sys.version_info.major >= 3: + from importlib import reload # reload is only available as a global in Py2, only in importlib in Py3 +else: + importlib.invalidate_caches = lambda: None # invalidate_caches doesn't exist in Py2 + modules = {} dependents = {} +#dependents[modname] = [list of modules which depend on modname] def isloaded(modname): return modname in modules -def modhas(modname, attname): return getattr(self.modules[modname], attname, None) is not None +def modhas(modname, attname): return getattr(modules[modname], attname, None) is not None -def load(parent, modname): +def load(parent, modname, dependent=False): + """Wrapper to call _load and print the return value.""" + if dependent: + print("(Loading dependency %s..." % (modname), end=' ') + else: + print("%09.3f [MOD] [?] Loading %s..." % (time.time() % 100000, modname), end=' ') + modstatus = _load(parent, modname, dependent) + if not modstatus: + if dependent: + print("failed: %s)" % (modstatus), end=' ') + else: + print("failed: %s." % (modstatus)) + if isinstance(modstatus, modlib.error) and isinstance(modstatus.errormsg, BaseException): + traceback.print_exception(modstatus.errormsg) + elif modstatus == True: + if dependent: + print("OK)", end=' ') + else: + print("OK.") + else: + if dependent: + print("OK: %s)" % (modstatus), end=' ') + else: + print("OK: %s." % (modstatus)) + return modstatus + +def _load(parent, modname, dependent=False): + """Load and return the new status of the module.""" + successstatus = [] if not isloaded(modname): - mod = __import__(modname) - reload(mod) + importlib.invalidate_caches() + try: + mod = importlib.import_module('modules.'+modname) + reload(mod) #in case it's been previously loaded. + except Exception as e: + return modlib.error(e) - if 1 not in mod.modinfo['compatible']: + + if not hasattr(mod, 'modinfo'): + return modlib.error('no modinfo') + + if parent.APIVERSION not in mod.modinfo['compatible']: return modlib.error('API-incompatible') modules[modname] = mod dependents[modname] = [] for dep in mod.modinfo['depends']: - if dep not in modules: - depret = load(parent, dep) - if not depret: - return + if bool(int(parent.cfg.get('autoloads', dep, default=1))): + if dep not in modules: + depret = load(parent, dep, dependent=True) + if depret is not None and not depret: + return depret + else: + return modlib.error("dependent %s disabled" % (dep)) dependents[dep].append(modname) - - ret = mod.modstart(parent) - if ret is not None and not ret: + for dep in mod.modinfo['softdeps']: + if bool(int(parent.cfg.get('autoloads', dep, default=1))): + if dep not in modules: + depret = load(parent, dep, dependent=True) + if depret is not None and not depret: + successstatus.append("softdep %s failed" % (dep)) + else: + successstatus.append("softdep %s disabled" % (dep)) + #swallow errors loading - softdeps are preferred, not required + + + try: + ret = mod.modstart(parent) + except Exception as e: + return modlib.error(e) + if ret is None: + ret = True + if not ret: del modules[modname] del dependents[modname] for dep in mod.modinfo['depends']: dependents[dep].remove(modname) - return ret + + successstatus = ';'.join(successstatus) + if len(successstatus) > 0 and ret: + if ret == True: + return successstatus + else: + return "%s (%s)" % (ret, successstatus) + else: + return ret else: #if not isloaded...else: return modlib.error('already loaded') @@ -43,24 +114,31 @@ def unload(parent, modname): if isloaded(modname): for dependent in dependents[modname]: unload(parent, dependent) - for dep in dependents[modname]: + for dep in modules[modname].modinfo['depends']: dependents[dep].remove(modname) - self.modules[modname].modstop(parent) + ret = modules[modname].modstop(parent) + del modules[modname] + return ret else: return modlib.error('already unloaded') def reloadmod(parent, modname): if isloaded(modname): - if modhas(modname, 'modrestart'): self.modules[modname].modrestart(parent) - else: self.modules[modname].modstop(parent) + if modhas(modname, 'modrestart'): modules[modname].modrestart(parent) + else: modules[modname].modstop(parent) - reload(self.modules[modname]) + try: + reload(modules[modname]) + except BaseException as e: + return modlib.error(e) - if modhas(modname, 'modrestarted'): self.modules[modname].modrestarted(parent) - else: self.modules[modname].modstart(parent) + if modhas(modname, 'modrestarted'): ret = modules[modname].modrestarted(parent) + else: ret = modules[modname].modstart(parent) + return ret else: - load(parent, modname) + return load(parent, modname) + def loadall(parent, modlist): for m in modlist: load(parent, m) @@ -68,5 +146,3 @@ def unloadall(parent, modlist): for m in modlist: unload(parent, m) def reloadall(parent, modlist): for m in modlist: reloadmod(parent, m) - -sys.path.append('modules')