From 47d81547aa51b9a340eeedec6e3af2142f00052d Mon Sep 17 00:00:00 2001 From: Jeremy Fincher Date: Fri, 6 Feb 2004 09:20:47 +0000 Subject: [PATCH] Added log.firewall and log.MetaFirewall, and converted several classes to use them. --- src/asyncoreDrivers.py | 6 +----- src/callbacks.py | 10 +++------- src/irclib.py | 45 +++++++++++++++++------------------------- src/log.py | 40 +++++++++++++++++++++++++++++++++++++ src/socketDrivers.py | 6 +----- src/twistedDrivers.py | 6 +----- 6 files changed, 64 insertions(+), 49 deletions(-) diff --git a/src/asyncoreDrivers.py b/src/asyncoreDrivers.py index d83e5a876..ff9d55da6 100644 --- a/src/asyncoreDrivers.py +++ b/src/asyncoreDrivers.py @@ -83,11 +83,7 @@ class AsyncoreDriver(asynchat.async_chat, object): def writable(self): while self.connected: - try: - m = self.irc.takeMsg() - except Exception, e: - log.exception('Uncaught exception in irclib.Irc.takeMsg:') - return + m = self.irc.takeMsg() if m: self.push(str(m)) else: diff --git a/src/callbacks.py b/src/callbacks.py index 7da8024fb..7eda4a692 100644 --- a/src/callbacks.py +++ b/src/callbacks.py @@ -434,13 +434,7 @@ class IrcObjectProxy(RichReplyMethods): log.debug('Finished calling invalidCommand: %s', cb.name()) return if hasattr(cb, 'invalidCommand'): - try: - cb.invalidCommand(self, self.msg, self.args) - except Exception, e: - cb.log.exception('Uncaught exception in invalidCommand:') - log.warning('Uncaught exception in %s.invalidCommand, ' - 'continuing to call other invalidCommands.' % - cb.name()) + cb.invalidCommand(self, self.msg, self.args) def _callCommand(self, name, command, cb): try: @@ -669,6 +663,8 @@ class ConfigIrcProxy(RichReplyMethods): class Privmsg(irclib.IrcCallback): """Base class for all Privmsg handlers.""" + __metaclass__ = log.MetaFirewall + __firewalled__ = {'invalidCommand': None} # Eventually callCommand. threaded = False public = True alwaysCall = () diff --git a/src/irclib.py b/src/irclib.py index ec2fc715f..0fc735496 100644 --- a/src/irclib.py +++ b/src/irclib.py @@ -75,6 +75,13 @@ class IrcCallback(IrcCommandDispatcher): # priority means the callback is called *earlier* on the inFilter chain, # *earlier* on the __call__ chain, and *later* on the outFilter chain. priority = 99 + __metaclass__ = log.MetaFirewall + __firewalled__ = {'die': None, + 'reset': None, + '__call__': None, + 'inFilter': lambda self, irc, msg: msg, + 'outFilter': lambda self, irc, msg: msg, + 'name': lambda self: self.__class__.__name__,} def name(self): """Returns the name of the callback.""" return self.__class__.__name__ @@ -99,11 +106,7 @@ class IrcCallback(IrcCommandDispatcher): """Used for handling each message.""" method = self.dispatchCommand(msg.command) if method is not None: - try: - method(irc, msg) - except Exception, e: - s = 'Exception caught in generic IrcCallback.__call__:' - log.exception(s) + method(irc, msg) def reset(self): """Resets the callback. Called when reconnecting to the server.""" @@ -263,6 +266,8 @@ class IrcState(IrcCommandDispatcher): """Maintains state of the Irc connection. Should also become smarter. """ __slots__ = ('history', 'nicksToHostmasks', 'channels') + __metaclass__ = log.MetaFirewall + __firewalled__ = {'addMsg': None} def __init__(self): self.history = RingBuffer(conf.supybot.maxHistoryLength()) self.reset() @@ -411,6 +416,10 @@ class Irc(IrcCommandDispatcher): Handles PING commands already. """ + __metaclass__ = log.MetaFirewall + __firewalled__ = {'die': None, + 'feedMsg': None, + 'takeMsg': None,} _nickSetters = sets.Set(['001', '002', '003', '004', '250', '251', '252', '254', '255', '265', '266', '372', '375', '376', '333', '353', '332', '366', '005']) @@ -520,31 +529,16 @@ class Irc(IrcCommandDispatcher): if msg: log.debug(repr(msg)) for callback in reviter(self.callbacks): - try: - outFilter = getattr(callback, 'outFilter') - except AttributeError, e: - continue - try: - msg = outFilter(self, msg) - except: - log.exception('Exception caught in outFilter:') - continue + msg = callback.outFilter(self, msg) if msg is None: - log.debug('%s.outFilter returned None' % callback.name()) + log.debug('%s.outFilter returned None.' % callback.name()) return self.takeMsg() if len(str(msg)) > 512: # Yes, this violates the contract, but at this point it doesn't # matter. That's why we gotta go munging in private attributes msg._str = msg._str[:500] + '\r\n' msg._len = len(str(msg)) - try: - self.state.addMsg(self, msg) - except Exception, e: - log.exception('Uncaught exception in IrcState.addMsg. This ' - 'could be a bug, but it could also be the ' - 'result of an invalid message being sent via ' - 'Owner.ircquote.') - return + self.state.addMsg(self, msg) log.debug('Outgoing message: ' + str(msg).rstrip('\r\n')) return msg else: @@ -668,10 +662,7 @@ class Irc(IrcCommandDispatcher): log.info('Irc object for %s dying.' % self.server) if self in world.ircs: for cb in self.callbacks: - try: - cb.die() - except Exception, e: - log.exception('Uncaught exception in %s.die:', cb.name()) + cb.die() world.ircs.remove(self) else: log.warning('Irc object killed twice.') diff --git a/src/log.py b/src/log.py index 66b005d41..fca245a89 100644 --- a/src/log.py +++ b/src/log.py @@ -42,6 +42,7 @@ import logging import ansi import conf +import utils import registry deadlyExceptions = [KeyboardInterrupt, SystemExit] @@ -166,6 +167,45 @@ def timestamp(when=None): format = conf.supybot.log.timestampFormat() return time.strftime(format, time.localtime(when)) +def firewall(f, errorHandler=None): + def logException(self, s=None): + if s is None: + s = 'Uncaught exception' + if hasattr(self, 'log'): + self.log.exception('%s:', s) + else: + exception('%s in %s.%s:', s, self.__class__.__name__, f.func_name) + def m(self, *args, **kwargs): + try: + return f(self, *args, **kwargs) + except Exception, e: + logException(self) + if errorHandler is not None: + try: + errorHandler(self, *args, **kwargs) + except Exception, e: + logException(self, 'Uncaught exception in errorHandler') + + m = utils.changeFunctionName(m, f.func_name, f.__doc__) + return m + +class MetaFirewall(type): + def __new__(cls, name, bases, dict): + firewalled = {} + for base in bases: + if hasattr(base, '__firewalled__'): + firewalled.update(base.__firewalled__) + if '__firewalled__' in dict: + firewalled.update(dict['__firewalled__']) + for attr in firewalled: + if attr in dict: + try: + errorHandler = firewalled[attr] + except: + errorHandler = None + dict[attr] = firewall(dict[attr], errorHandler) + return type.__new__(cls, name, bases, dict) + class LogLevel(registry.Value): def set(self, s): diff --git a/src/socketDrivers.py b/src/socketDrivers.py index c21343071..54a3d2753 100644 --- a/src/socketDrivers.py +++ b/src/socketDrivers.py @@ -75,11 +75,7 @@ class SocketDriver(drivers.IrcDriver): self.reconnect() def _sendIfMsgs(self): - try: - msgs = [self.irc.takeMsg()] - except Exception, e: - log.exception('Uncaught exception in irclib.Irc.takeMsg:') - return + msgs = [self.irc.takeMsg()] while msgs[-1] is not None: msgs.append(self.irc.takeMsg()) del msgs[-1] diff --git a/src/twistedDrivers.py b/src/twistedDrivers.py index 538f5ebfd..fef4973d0 100644 --- a/src/twistedDrivers.py +++ b/src/twistedDrivers.py @@ -69,11 +69,7 @@ class SupyIrcProtocol(LineReceiver): def checkIrcForMsgs(self): if self.connected: - try: - msg = self.irc.takeMsg() - except Exception, e: - log.exception('Uncaught exception in irclib.Irc.takeMsg:') - return + msg = self.irc.takeMsg() if msg: self.transport.write(str(msg)) self.mostRecentCall = reactor.callLater(1, self.checkIrcForMsgs)