mirror of
https://github.com/Mikaela/Limnoria.git
synced 2024-12-24 11:42:52 +01:00
Finally got topological sorting working.
This commit is contained in:
parent
727145afbe
commit
9be4fd112d
@ -79,13 +79,12 @@ class Misc(callbacks.Privmsg):
|
||||
super(Misc, self).__init__()
|
||||
self.invalidCommands = ircutils.FloodQueue(60)
|
||||
|
||||
priority = sys.maxint + 1 # This is for working with IrcCallbacks.
|
||||
callAfter = utils.Everything()
|
||||
callBefore = utils.Nothing()
|
||||
def __lt__(self, other):
|
||||
return False # We should always be the last plugin.
|
||||
def callPrecedence(self, irc):
|
||||
return ([cb for cb in irc.callbacks if cb is not self], [])
|
||||
|
||||
def invalidCommand(self, irc, msg, tokens):
|
||||
assert not msg.repliedTo, 'repliedTo msg in Misc.invalidCommand.'
|
||||
assert self is irc.callbacks[-1], 'Misc isn\'t last callback.'
|
||||
self.log.debug('Misc.invalidCommand called (tokens %s)', tokens)
|
||||
# First, we check for invalidCommand floods. This is rightfully done
|
||||
# here since this will be the last invalidCommand called, and thus it
|
||||
|
18
src/Owner.py
18
src/Owner.py
@ -202,7 +202,6 @@ class LogProxy(object):
|
||||
def __getattr__(self, attr):
|
||||
return getattr(self.log, attr)
|
||||
|
||||
|
||||
class Owner(privmsgs.CapabilityCheckingPrivmsg):
|
||||
# This plugin must be first; its priority must be lowest; otherwise odd
|
||||
# things will happen when adding callbacks.
|
||||
@ -249,11 +248,8 @@ class Owner(privmsgs.CapabilityCheckingPrivmsg):
|
||||
continue
|
||||
registerDefaultPlugin(name, s)
|
||||
|
||||
priority = ~sys.maxint - 1 # For working with IrcCallbacks.
|
||||
callAfter = utils.Nothing()
|
||||
callBefore = utils.Everything()
|
||||
def __lt__(self, other):
|
||||
return True # We should always be the first plugin.
|
||||
def callPrecedence(self, irc):
|
||||
return ([], [cb for cb in irc.callbacks if cb is not self])
|
||||
|
||||
def outFilter(self, irc, msg):
|
||||
if msg.command == 'PRIVMSG' and not world.testing:
|
||||
@ -415,12 +411,14 @@ class Owner(privmsgs.CapabilityCheckingPrivmsg):
|
||||
do422 = do377 = do376
|
||||
|
||||
def doPrivmsg(self, irc, msg):
|
||||
callbacks.Privmsg.handled = False
|
||||
callbacks.Privmsg.errored = False
|
||||
assert self is irc.callbacks[0], 'Owner isn\'t first callback.'
|
||||
msg.errored = False
|
||||
msg.repliedTo = False
|
||||
ignored = ircdb.checkIgnored(msg.prefix)
|
||||
if ircmsgs.isCtcp(msg):
|
||||
return
|
||||
s = callbacks.addressed(irc.nick, msg)
|
||||
if s:
|
||||
ignored = ircdb.checkIgnored(msg.prefix)
|
||||
if ignored:
|
||||
self.log.info('Ignoring command from %s.' % msg.prefix)
|
||||
return
|
||||
@ -434,9 +432,7 @@ class Owner(privmsgs.CapabilityCheckingPrivmsg):
|
||||
return
|
||||
self.processTokens(irc, msg, tokens)
|
||||
except SyntaxError, e:
|
||||
callbacks.Privmsg.errored = True
|
||||
irc.queueMsg(callbacks.error(msg, str(e)))
|
||||
return
|
||||
|
||||
if conf.allowEval:
|
||||
_evalEnv = {'_': None,
|
||||
|
131
src/callbacks.py
131
src/callbacks.py
@ -194,6 +194,7 @@ def reply(msg, s, prefixName=None, private=None,
|
||||
def error(msg, s, **kwargs):
|
||||
"""Makes an error reply to msg with the appropriate error payload."""
|
||||
kwargs['error'] = True
|
||||
msg.errored = True
|
||||
return reply(msg, s, **kwargs)
|
||||
|
||||
def getHelp(method, name=None):
|
||||
@ -504,7 +505,6 @@ class IrcObjectProxy(RichReplyMethods):
|
||||
# tokenized commands.
|
||||
self.args = copy.deepcopy(args)
|
||||
self.counter = 0
|
||||
self.finished = False # Used in _callInvalidCommands.
|
||||
self.commandMethod = None # Used in error.
|
||||
self._resetReplyAttributes()
|
||||
if not args:
|
||||
@ -538,17 +538,34 @@ class IrcObjectProxy(RichReplyMethods):
|
||||
return
|
||||
self.finalEval()
|
||||
|
||||
|
||||
def _callTokenizedCommands(self):
|
||||
log.debug('Calling tokenizedCommands.')
|
||||
for cb in self.irc.callbacks:
|
||||
if hasattr(cb, 'tokenizedCommand'):
|
||||
log.debug('Trying to call %s.tokenizedCommand.' % cb.name())
|
||||
self._callTokenizedCommand(cb)
|
||||
if self.msg.repliedTo:
|
||||
log.debug('Done calling tokenizedCommands: %s.' % cb.name())
|
||||
return
|
||||
|
||||
def _callTokenizedCommand(self, cb):
|
||||
try:
|
||||
cb.tokenizedCommand(self, self.msg, self.args)
|
||||
except Error, e:
|
||||
self.error(str(e))
|
||||
except Exception, e:
|
||||
log.exception('Uncaught exception in %s.tokenizedCommand.' %
|
||||
cb.name())
|
||||
|
||||
def _callInvalidCommands(self):
|
||||
if ircmsgs.isCtcp(self.msg):
|
||||
log.debug('Skipping invalidCommand, msg is CTCP.')
|
||||
return
|
||||
log.debug('Calling invalidCommands.')
|
||||
for cb in self.irc.callbacks:
|
||||
log.debug('Trying to call %s.invalidCommand.' % cb.name())
|
||||
if hasattr(cb, 'invalidCommand'):
|
||||
log.debug('Trying to call %s.invalidCommand.' % cb.name())
|
||||
self._callInvalidCommand(cb)
|
||||
if self.finished:
|
||||
log.debug('Finished calling invalidCommand: %s.',cb.name())
|
||||
if self.msg.repliedTo:
|
||||
log.debug('Done calling invalidCommands: %s.',cb.name())
|
||||
return
|
||||
|
||||
def _callInvalidCommand(self, cb):
|
||||
@ -557,7 +574,8 @@ class IrcObjectProxy(RichReplyMethods):
|
||||
except Error, e:
|
||||
self.error(str(e))
|
||||
except Exception, e:
|
||||
log.exception('Uncaught exception in %s.invalidCommand'% cb.name())
|
||||
log.exception('Uncaught exception in %s.invalidCommand.'%
|
||||
cb.name())
|
||||
|
||||
def _callCommand(self, name, cb):
|
||||
try:
|
||||
@ -581,31 +599,37 @@ class IrcObjectProxy(RichReplyMethods):
|
||||
name = self.args[0]
|
||||
cbs = findCallbackForCommand(self, name)
|
||||
if len(cbs) == 0:
|
||||
# Normal command not found, let's go for the specialties now.
|
||||
# First, check for addressedRegexps -- they take precedence over
|
||||
# tokenizedCommands.
|
||||
for cb in self.irc.callbacks:
|
||||
# We used not to run invalidCommands if regexps matched, but now I'd say that
|
||||
# invalidCommands are more essential than regexps, so it is regexps that should
|
||||
# have to work around invalidCommands, not vice-versa.
|
||||
## if isinstance(cb, PrivmsgRegexp):
|
||||
## for (r, name) in cb.res:
|
||||
## if r.search(self.msg.args[1]):
|
||||
## log.debug('Skipping invalidCommand: %s.%s',
|
||||
## cb.name(), name)
|
||||
## return
|
||||
if isinstance(cb, PrivmsgCommandAndRegexp):
|
||||
## for (r, name) in cb.res:
|
||||
## if r.search(self.msg.args[1]):
|
||||
## log.debug('Skipping invalidCommand: %s.%s',
|
||||
## cb.name(), name)
|
||||
## return
|
||||
# Although we still consider addressedRegexps to be commands, so we don't call
|
||||
# invalidCommnads if an addressedRegexp matches.
|
||||
payload = addressed(self.irc.nick, self.msg)
|
||||
for (r, name) in cb.addressedRes:
|
||||
if r.search(payload):
|
||||
log.debug('Skipping invalidCommand: %s.%s',
|
||||
cb.name(), name)
|
||||
return
|
||||
# Ok, no regexp-based things matched.
|
||||
# Now we call tokenizedCommands.
|
||||
self._callTokenizedCommands()
|
||||
# Return if we've replied.
|
||||
if self.msg.repliedTo:
|
||||
return
|
||||
# Now we check for regexp commands, which override invalidCommand.
|
||||
for cb in self.irc.callbacks:
|
||||
if isinstance(cb, PrivmsgRegexp):
|
||||
for (r, name) in cb.res:
|
||||
if r.search(self.msg.args[1]):
|
||||
log.debug('Skipping invalidCommand: %s.%s',
|
||||
cb.name(), name)
|
||||
return
|
||||
elif isinstance(cb, PrivmsgCommandAndRegexp):
|
||||
for (r, name) in cb.res:
|
||||
if r.search(self.msg.args[1]):
|
||||
log.debug('Skipping invalidCommand: %s.%s',
|
||||
cb.name(), name)
|
||||
return
|
||||
# No matching regexp commands, now we do invalidCommands.
|
||||
self._callInvalidCommands()
|
||||
else:
|
||||
# But we must canonicalName here, since we're comparing to a
|
||||
@ -623,7 +647,6 @@ class IrcObjectProxy(RichReplyMethods):
|
||||
else:
|
||||
del self.args[0]
|
||||
cb = cbs[0]
|
||||
Privmsg.handled = True
|
||||
if cb.threaded or conf.supybot.debug.threadAllCommands():
|
||||
t = CommandThread(target=self._callCommand,
|
||||
args=(name, cb))
|
||||
@ -706,7 +729,6 @@ class IrcObjectProxy(RichReplyMethods):
|
||||
notice=self.notice,
|
||||
private=self.private,
|
||||
prefixName=self.prefixName))
|
||||
self.finished = True
|
||||
return
|
||||
msgs = ircutils.wrap(s, allowedLength-30) # -30 is for nick:
|
||||
msgs.reverse()
|
||||
@ -742,7 +764,6 @@ class IrcObjectProxy(RichReplyMethods):
|
||||
notice=self.notice,
|
||||
private=self.private,
|
||||
prefixName=self.prefixName))
|
||||
self.finished = True
|
||||
finally:
|
||||
self._resetReplyAttributes()
|
||||
else:
|
||||
@ -766,10 +787,9 @@ class IrcObjectProxy(RichReplyMethods):
|
||||
self.error(formatArgumentError(self.commandMethod), **kwargs)
|
||||
else:
|
||||
raise ArgumentError # We shouldn't get here, but just in case.
|
||||
self.finished = True
|
||||
|
||||
def noReply(self):
|
||||
self.finished = True
|
||||
self.msg.repliedTo = True
|
||||
|
||||
def getRealIrc(self):
|
||||
"""Returns the real irclib.Irc object underlying this proxy chain."""
|
||||
@ -868,7 +888,6 @@ class DisabledCommands(object):
|
||||
|
||||
class Privmsg(irclib.IrcCallback):
|
||||
"""Base class for all Privmsg handlers."""
|
||||
__metaclass__ = log.MetaFirewall
|
||||
# For awhile, a comment stood here to say, "Eventually callCommand." But
|
||||
# that's wrong, because we can't do generic error handling in this
|
||||
# callCommand -- plugins need to be able to override callCommand and do
|
||||
@ -876,13 +895,9 @@ class Privmsg(irclib.IrcCallback):
|
||||
__firewalled__ = {'isCommand': None,}
|
||||
# 'invalidCommand': None} # Gotta raise callbacks.Error.
|
||||
public = True
|
||||
handled = False
|
||||
errored = False
|
||||
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.
|
||||
@ -944,24 +959,6 @@ 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 __lt__(self, other):
|
||||
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 __lt__ is the most specific one, so
|
||||
# we basically run the other callback's as well.
|
||||
if isinstance(other, Privmsg):
|
||||
if other.name() in self.callBefore or \
|
||||
self.name() in other.callAfter:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
return self.__parent.__lt__(other)
|
||||
|
||||
def __call__(self, irc, msg):
|
||||
if msg.command == 'PRIVMSG':
|
||||
if self.noIgnore or not ircdb.checkIgnored(msg.prefix,msg.args[0]):
|
||||
@ -1151,9 +1148,8 @@ class PrivmsgRegexp(Privmsg):
|
||||
irc.replyError()
|
||||
|
||||
def doPrivmsg(self, irc, msg):
|
||||
if Privmsg.errored:
|
||||
self.log.info('%s not running due to Privmsg.errored.',
|
||||
self.name())
|
||||
if msg.errored:
|
||||
self.log.info('%s not running due to msg.errored.', self.name())
|
||||
return
|
||||
for (r, name) in self.res:
|
||||
spans = sets.Set()
|
||||
@ -1218,24 +1214,21 @@ class PrivmsgCommandAndRegexp(Privmsg):
|
||||
raise
|
||||
|
||||
def doPrivmsg(self, irc, msg):
|
||||
if Privmsg.errored:
|
||||
self.log.debug('%s not running due to Privmsg.errored.',
|
||||
self.name())
|
||||
if msg.errored:
|
||||
self.log.debug('%s not running due to msg.errored.', self.name())
|
||||
return
|
||||
for (r, name) in self.res:
|
||||
for m in r.finditer(msg.args[1]):
|
||||
proxy = self.Proxy(irc, msg)
|
||||
self.callCommand(name, proxy, msg, m, catchErrors=True)
|
||||
if not Privmsg.handled:
|
||||
s = addressed(irc.nick, msg)
|
||||
if s:
|
||||
for (r, name) in self.addressedRes:
|
||||
if Privmsg.handled and name not in self.alwaysCall:
|
||||
continue
|
||||
for m in r.finditer(s):
|
||||
proxy = self.Proxy(irc, msg)
|
||||
self.callCommand(name, proxy, msg, m, catchErrors=True)
|
||||
Privmsg.handled = True
|
||||
s = addressed(irc.nick, msg)
|
||||
if s:
|
||||
for (r, name) in self.addressedRes:
|
||||
if msg.repliedTo and name not in self.alwaysCall:
|
||||
continue
|
||||
for m in r.finditer(s):
|
||||
proxy = self.Proxy(irc, msg)
|
||||
self.callCommand(name, proxy, msg, m, catchErrors=True)
|
||||
|
||||
|
||||
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:
|
||||
|
@ -72,12 +72,8 @@ class IrcCallback(IrcCommandDispatcher):
|
||||
"doCommand" -- doPrivmsg, doNick, do433, etc. These will be called
|
||||
on matching messages.
|
||||
"""
|
||||
# priority determines the order in which callbacks are called. Lower
|
||||
# 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
|
||||
callAfter = ()
|
||||
callBefore = ()
|
||||
__metaclass__ = log.MetaFirewall
|
||||
__firewalled__ = {'die': None,
|
||||
'reset': None,
|
||||
@ -87,19 +83,30 @@ class IrcCallback(IrcCommandDispatcher):
|
||||
'outFilter': lambda self, irc, msg: msg,
|
||||
'name': lambda self: self.__class__.__name__,}
|
||||
|
||||
def __lt__(self, other):
|
||||
if isinstance(other, IrcCallback):
|
||||
ret = self.priority < other.priority
|
||||
if not ret:
|
||||
ret = self.name() < other.name()
|
||||
return ret
|
||||
else:
|
||||
return super(IrcCallback, self).__lt__(other)
|
||||
def __repr__(self):
|
||||
return '<%s %s>' % (self.__class__.__name__, self.name())
|
||||
|
||||
def name(self):
|
||||
"""Returns the name of the callback."""
|
||||
return self.__class__.__name__
|
||||
|
||||
def callPrecedence(self, irc):
|
||||
"""Returns a pair of (callbacks to call before me,
|
||||
callbacks to call after me)"""
|
||||
after = []
|
||||
before = []
|
||||
for name in self.callAfter:
|
||||
cb = irc.getCallback(name)
|
||||
if cb is not None:
|
||||
after.append(cb)
|
||||
for name in self.callBefore:
|
||||
cb = irc.getCallback(name)
|
||||
if cb is not None:
|
||||
before.append(cb)
|
||||
assert self not in after, '%s was in its own after.' % self.name()
|
||||
assert self not in before, '%s was in its own before.' % self.name()
|
||||
return (before, after)
|
||||
|
||||
def inFilter(self, irc, msg):
|
||||
"""Used for filtering/modifying messages as they're entering.
|
||||
|
||||
@ -595,12 +602,43 @@ class Irc(IrcCommandDispatcher):
|
||||
def __repr__(self):
|
||||
return '<irclib.Irc object for %s>' % self.network
|
||||
|
||||
# This *isn't* threadsafe!
|
||||
def addCallback(self, callback):
|
||||
"""Adds a callback to the callbacks list."""
|
||||
self.callbacks.append(callback)
|
||||
self.callbacks.sort()
|
||||
# We used to do this, then we implemented sorting in IrcCallback.
|
||||
# utils.sortBy(operator.attrgetter('priority'), self.callbacks)
|
||||
# This is the new list we're building, which will be tsorted.
|
||||
cbs = []
|
||||
# The vertices are self.callbacks itself. Now we make the edges.
|
||||
edges = sets.Set()
|
||||
for cb in self.callbacks:
|
||||
(before, after) = cb.callPrecedence(self)
|
||||
assert cb not in after, 'cb was in its own after.'
|
||||
assert cb not in before, 'cb was in its own before.'
|
||||
for otherCb in before:
|
||||
edges.add((otherCb, cb))
|
||||
for otherCb in after:
|
||||
edges.add((cb, otherCb))
|
||||
def getFirsts():
|
||||
firsts = sets.Set(self.callbacks) - sets.Set(cbs)
|
||||
for (before, after) in edges:
|
||||
firsts.discard(after)
|
||||
return firsts
|
||||
firsts = getFirsts()
|
||||
while firsts:
|
||||
# Then we add these to our list of cbs, and remove all edges that
|
||||
# originate with these cbs.
|
||||
for cb in firsts:
|
||||
cbs.append(cb)
|
||||
edgesToRemove = []
|
||||
for edge in edges:
|
||||
if edge[0] is cb:
|
||||
edgesToRemove.append(edge)
|
||||
for edge in edgesToRemove:
|
||||
edges.remove(edge)
|
||||
firsts = getFirsts()
|
||||
assert len(cbs) == len(self.callbacks), \
|
||||
'cbs: %s, self.callbacks: %s' % (cbs, self.callbacks)
|
||||
self.callbacks[:] = cbs
|
||||
|
||||
def getCallback(self, name):
|
||||
"""Gets a given callback by name."""
|
||||
|
Loading…
Reference in New Issue
Block a user