From c573ab59960c540fa8478b514b1b966dc93685cc Mon Sep 17 00:00:00 2001 From: Jeremy Fincher Date: Fri, 10 Sep 2004 06:30:21 +0000 Subject: [PATCH] New plugin prioritization method. --- src/Misc.py | 6 +++++- src/Owner.py | 20 +++++++++++++------- src/callbacks.py | 41 +++++++++++++++++++++++++++++++++++++++++ src/irclib.py | 13 ++++++++++++- 4 files changed, 71 insertions(+), 9 deletions(-) diff --git a/src/Misc.py b/src/Misc.py index a47a77177..d13a433cb 100755 --- a/src/Misc.py +++ b/src/Misc.py @@ -65,12 +65,16 @@ conf.registerGlobalValue(conf.supybot.plugins.Misc, 'listPrivatePlugins', are loaded.""")) class Misc(callbacks.Privmsg): - priority = sys.maxint def __init__(self): super(Misc, self).__init__() timeout = conf.supybot.abuse.flood.command.invalid self.invalidCommands = ircutils.FloodQueue(timeout) + callAfter = utils.Everything() + callBefore = utils.Nothing() + def __cmp__(self, other): + return 1 # We should always be the last plugin. + def invalidCommand(self, irc, msg, tokens): self.log.debug('Misc.invalidCommand called (tokens %s)', tokens) # First, we check for invalidCommand floods. This is rightfully done diff --git a/src/Owner.py b/src/Owner.py index acf0a28ad..0867026f1 100644 --- a/src/Owner.py +++ b/src/Owner.py @@ -183,7 +183,6 @@ class LogProxy(object): class Owner(privmsgs.CapabilityCheckingPrivmsg): # This plugin must be first; its priority must be lowest; otherwise odd # things will happen when adding callbacks. - priority = ~sys.maxint-1 # This must be first! capability = 'owner' _srcPlugins = ircutils.IrcSet(('Admin', 'Channel', 'Config', 'Misc', 'Owner', 'User')) @@ -227,6 +226,11 @@ class Owner(privmsgs.CapabilityCheckingPrivmsg): continue registerDefaultPlugin(name, s) + callAfter = utils.Nothing() + callBefore = utils.Everything() + def __cmp__(self, other): + return -1 # We should always be the first plugin. + def _getIrc(self, network): network = network.lower() for irc in world.ircs: @@ -376,10 +380,12 @@ class Owner(privmsgs.CapabilityCheckingPrivmsg): def doPrivmsg(self, irc, msg): callbacks.Privmsg.handled = False callbacks.Privmsg.errored = False - if ircdb.checkIgnored(msg.prefix): - return + ignored = ircdb.checkIgnored(msg.prefix) s = callbacks.addressed(irc.nick, msg) if s: + if ignored: + self.log.info('Ignoring command from %s.' % msg.prefix) + return brackets = conf.supybot.reply.brackets.get(msg.args[0])() try: tokens = callbacks.tokenize(s, brackets=brackets) @@ -595,17 +601,17 @@ class Owner(privmsgs.CapabilityCheckingPrivmsg): if name.endswith('.py'): name = name[:-3] if irc.getCallback(name): - irc.error('That plugin is already loaded.') + irc.error('%s is already loaded.' % name.capitalize()) return try: module = loadPluginModule(name, ignoreDeprecation) except Deprecated: - irc.error('That plugin is deprecated. ' - 'Use --deprecated to force it to load.') + irc.error('%s is deprecated. Use --deprecated ' + 'to force it to load.' % name.capitalize()) return except ImportError, e: if name in str(e): - irc.error('No plugin %s exists.' % name) + irc.error('No plugin named %s exists.' % utils.dqrepr(name)) else: irc.error(str(e)) return diff --git a/src/callbacks.py b/src/callbacks.py index 5e452860c..c79e8dae1 100644 --- a/src/callbacks.py +++ b/src/callbacks.py @@ -570,6 +570,7 @@ class IrcObjectProxy(RichReplyMethods): def finalEval(self): assert not self.finalEvaled, 'finalEval called twice.' self.finalEvaled = True + # We can't canonicalName here because it might be a regexp method. name = self.args[0] cbs = findCallbackForCommand(self, name) if len(cbs) == 0: @@ -595,6 +596,9 @@ class IrcObjectProxy(RichReplyMethods): # Ok, no regexp-based things matched. self._callInvalidCommands() else: + # But we must canonicalName here, since we're comparing to a + # canonicalName. + name = canonicalName(name) if len(cbs) > 1: for cb in cbs: if canonicalName(cb.name()) == name: @@ -862,6 +866,8 @@ class Privmsg(irclib.IrcCallback): alwaysCall = () threaded = False noIgnore = False + callAfter = () + callBefore = () Proxy = IrcObjectProxy commandArgs = ['self', 'irc', 'msg', 'args'] # This must be class-scope, so all plugins use the same one. @@ -871,6 +877,10 @@ class Privmsg(irclib.IrcCallback): self.__parent = super(Privmsg, self) myName = self.name() self.log = log.getPluginLogger(myName) + # We can't do this because of the specialness that Owner and Misc do. + # I guess plugin authors will have to get the capitalization right. + # self.callAfter = map(str.lower, self.callAfter) + # self.callBefore = map(str.lower, self.callBefore) ### Setup the dispatcher command. canonicalname = canonicalName(myName) self._original = getattr(self, canonicalname, None) @@ -919,6 +929,37 @@ class Privmsg(irclib.IrcCallback): dispatcher.isDispatcher = True setattr(self.__class__, canonicalname, dispatcher) + # In addition to priority, plugins may specify callBefore and callAfter + # attributes which are lists of plugin names which the plugin should be + # called before and after, respectively. We may, at some future point, + # remove priority entirely. + def __cmp__(self, other, doAssert=True): + selfName = self.name() + otherName = other.name() + # We can't be certain of the order the callbacks list is in, so we + # can't be certain that our __cmp__ is the most specific one, so + # we basically run the other callback's as well. + if isinstance(other, Privmsg): + if otherName in self.callAfter or selfName in other.callBefore: + ret = 1 + elif otherName in self.callBefore or selfName in other.callAfter: + ret = -1 + else: + ret = self.__parent.__cmp__(other) + else: + ret = self.__parent.__cmp__(other) + if doAssert: + try: + otherRet = other.__cmp__(self, doAssert=False) + except TypeError, e: + if 'doAssert' in str(e): # unexpected keyword argument. + otherRet = cmp(other, self) + else: + otherRet = ret + assert ret+otherRet==0, 'callbacks\' __cmp__ disagree: %s, %s' % \ + (selfName, otherName) + return ret + def __call__(self, irc, msg): if msg.command == 'PRIVMSG': if self.noIgnore or not ircdb.checkIgnored(msg.prefix,msg.args[0]): diff --git a/src/irclib.py b/src/irclib.py index 20850c18d..d1a6f3f88 100644 --- a/src/irclib.py +++ b/src/irclib.py @@ -76,14 +76,23 @@ class IrcCallback(IrcCommandDispatcher): # numbers mean *higher* priority (like nice values in *nix). Higher # 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, + '__cmp__': lambda self: 0, 'inFilter': lambda self, irc, msg: msg, 'outFilter': lambda self, irc, msg: msg, 'name': lambda self: self.__class__.__name__,} + + def __cmp__(self, other): + ret = cmp(self.priority, other.priority) + if not ret: + ret = cmp(self.name(), other.name()) + return ret + def name(self): """Returns the name of the callback.""" return self.__class__.__name__ @@ -586,7 +595,9 @@ class Irc(IrcCommandDispatcher): def addCallback(self, callback): """Adds a callback to the callbacks list.""" self.callbacks.append(callback) - utils.sortBy(operator.attrgetter('priority'), self.callbacks) + self.callbacks.sort() + # We used to do this, then we implemented sorting in IrcCallback. + # utils.sortBy(operator.attrgetter('priority'), self.callbacks) def getCallback(self, name): """Gets a given callback by name."""