From: John Runyon Date: Mon, 23 Oct 2023 13:52:15 +0000 (-0600) Subject: core - add exception hooks X-Git-Url: https://jfr.im/git/erebus.git/commitdiff_plain/e8885384462a99c9fdbc6ef6ce7390b51ca3dbbf core - add exception hooks --- diff --git a/bot.py b/bot.py index c727d72..3ce3947 100644 --- a/bot.py +++ b/bot.py @@ -4,7 +4,7 @@ # Erebus IRC bot - Author: John Runyon # "Bot" and "BotConnection" classes (handling a specific "arm") -import socket, sys, time, threading, os, random, struct +import os, random, socket, struct, sys, threading, time, traceback from collections import deque if sys.version_info.major < 3: @@ -134,7 +134,7 @@ class Bot(object): try: callback(self, line) except Exception: - self.__debug_cbexception("numhook", line) + self._cbexception("numhook", line) if numeric in dispatch: dispatch[numeric](pieces) @@ -299,12 +299,19 @@ class Bot(object): else: pass # don't care about other modes - def __debug_cbexception(self, source, *args, **kwargs): + def _cbexception(self, source, *args, chained=False, **kwargs): + if not chained: # skip hooks if we were caused by a hook + exc = sys.exception() + if self.parent.hasexceptionhook(exc): + for callback in self.parent.getexceptionhook(exc): + try: + callback(self, exc, source, *args, **kwargs) + except Exception: + self._cbexception('exceptionhook', chained=True, module=callback.__module__, function=callback.__name__, underlying=(source, args, kwargs)) if self.parent.cfg.getboolean('debug', 'cbexc'): self.conn.send("PRIVMSG %s :%09.3f 4!!! CBEXC %s" % (self.parent.cfg.get('debug', 'owner'), time.time() % 100000, source)) - __import__('traceback').print_exc() + traceback.print_exc(chain=not chained) self.log('!', "CBEXC %s %r %r" % (source, args, kwargs)) -# print "%09.3f %s [!] CBEXC %s %r %r" % (time.time() % 100000, self.nick, source, args, kwargs) def parsemsg(self, user, target, msg): @@ -355,7 +362,7 @@ class Bot(object): self.reply(target, user, cbret) except: self.msg(user, "Command failed. Code: CBEXC%09.3f" % (time.time() % 100000)) - self.__debug_cbexception("chanhook", user=user, target=target, msg=msg) + self._cbexception("chanhook", user=user, target=target, msg=msg) return # not to bot, don't process! cmd = pieces[0].lower() @@ -376,7 +383,7 @@ class Bot(object): self.reply(target, user, cbret) except Exception: self.msg(user, "Command failed. Code: CBEXC%09.3f" % (time.time() % 100000)) - self.__debug_cbexception("hook", user=user, target=target, msg=msg) + self._cbexception("hook", user=user, target=target, msg=msg) except SystemExit as e: self.parent.mustquit = e try: @@ -395,7 +402,7 @@ class Bot(object): self.conn.send("PRIVMSG %s :%09.3f 4!!! NOMSG %r, %r" % (self.parent.cfg.get('debug', 'owner'), time.time() % 100000, target, msg)) self.log('!', "!!! NOMSG") # print "%09.3f %s [!] %s" % (time.time() % 100000, self.nick, "!!! NOMSG") - __import__('traceback').print_stack() + traceback.print_stack() def reply(self, chan, user, msg): diff --git a/erebus.py b/erebus.py index 42a2f41..91e01bf 100644 --- a/erebus.py +++ b/erebus.py @@ -18,6 +18,7 @@ class Erebus(object): #singleton to pass around numhandlers = {} msghandlers = {} chanhandlers = {} + exceptionhandlers = [] # list of (Exception_class, handler_function) tuples users = {} chans = {} @@ -315,6 +316,15 @@ class Erebus(object): #singleton to pass around def getchanhook(self, chan): return self.chanhandlers[chan] + def hookexception(self, exc, handler): + self.exceptionhandlers.append((exc, handler)) + def unhookexception(self, exc, handler): + self.exceptionhandlers.remove((exc, handler)) + def hasexceptionhook(self, exc): + return any((True for x,h in self.exceptionhandlers if isinstance(exc, x))) + def getexceptionhook(self, exc): + return (h for x,h in self.exceptionhandlers if isinstance(exc, x)) + def dbsetup(): main.db = None diff --git a/modlib.py b/modlib.py index ae19fb5..b09272e 100644 --- a/modlib.py +++ b/modlib.py @@ -62,6 +62,7 @@ class modlib(object): self.hooks = {} self.numhooks = {} self.chanhooks = {} + self.exceptionhooks = [] self.helps = [] self.parent = None @@ -77,12 +78,14 @@ class modlib(object): #"specified" values will be printed. unspecified values will result in "OK" or "failed" self.parent = parent for cmd, func in self.hooks.items(): - self.parent.hook(cmd, func) - self.parent.hook("%s.%s" % (self.name, cmd), func) + parent.hook(cmd, func) + parent.hook("%s.%s" % (self.name, cmd), func) for num, func in self.numhooks.items(): - self.parent.hooknum(num, func) + parent.hooknum(num, func) for chan, func in self.chanhooks.items(): - self.parent.hookchan(chan, func) + parent.hookchan(chan, func) + for exc, func in self.exceptionhooks: + parent.hookexception(exc, func) for func, args, kwargs in self.helps: try: @@ -98,6 +101,8 @@ class modlib(object): parent.unhooknum(num, func) for chan, func in self.chanhooks.items(): parent.unhookchan(chan, func) + for exc, func in self.exceptionhooks: + parent.unhookexception(exc, func) for func, args, kwargs in self.helps: try: @@ -106,6 +111,14 @@ class modlib(object): pass return True + def hookexception(self, exc): + def realhook(func): + self.exceptionhooks.append((exc, func)) + if self.parent is not None: + self.parent.hookexception(exc, func) + return func + return realhook + def hooknum(self, num): def realhook(func): self.numhooks[str(num)] = func