mirror of
https://github.com/Mikaela/Limnoria.git
synced 2024-11-26 20:59:27 +01:00
Moved disambiguation stuff to callbacks.IOP. Also fixed rename-persistence.
This commit is contained in:
parent
3aa8bdcf18
commit
998f61cce8
@ -143,10 +143,6 @@ def makeNewAlias(name, alias):
|
||||
return False
|
||||
everythingReplace(tokens)
|
||||
Owner = irc.getCallback('Owner')
|
||||
d = Owner.disambiguate(irc, tokens)
|
||||
if d:
|
||||
Owner.ambiguousError(irc, msg, d)
|
||||
else:
|
||||
self.Proxy(irc.irc, msg, tokens)
|
||||
doc ='<an alias, %s>\n\nAlias for %r' % \
|
||||
(utils.nItems('argument', biggestDollar), alias)
|
||||
|
@ -152,8 +152,6 @@ class Network(callbacks.Privmsg):
|
||||
raise callbacks.ArgumentError
|
||||
network = args.pop(0)
|
||||
otherIrc = self._getIrc(network)
|
||||
Owner = irc.getCallback('Owner')
|
||||
Owner.disambiguate(irc, args)
|
||||
self.Proxy(otherIrc, msg, args)
|
||||
command = privmsgs.checkCapability(command, 'admin')
|
||||
|
||||
|
@ -57,10 +57,6 @@ class Scheduler(callbacks.Privmsg):
|
||||
def _makeCommandFunction(self, irc, msg, command, remove=True):
|
||||
"""Makes a function suitable for scheduling from command."""
|
||||
tokens = callbacks.tokenize(command)
|
||||
Owner = irc.getCallback('Owner')
|
||||
ambiguous = Owner.disambiguate(irc, tokens)
|
||||
if ambiguous:
|
||||
raise callbacks.Error, callbacks.ambiguousReply(ambiguous)
|
||||
def f():
|
||||
if remove:
|
||||
del self.events[str(f.eventId)]
|
||||
@ -74,7 +70,7 @@ class Scheduler(callbacks.Privmsg):
|
||||
future. For example, 'scheduler add [seconds 30m] "echo [cpu]"' will
|
||||
schedule the command "cpu" to be sent to the channel the schedule add
|
||||
command was given in (with no prefixed nick, a consequence of using
|
||||
echo).
|
||||
echo). Do pay attention to the quotes in that example.
|
||||
"""
|
||||
(seconds, command) = privmsgs.getArgs(args, required=2)
|
||||
try:
|
||||
|
@ -160,10 +160,9 @@ class Utilities(callbacks.Privmsg):
|
||||
commands = map(callbacks.canonicalName, commands)
|
||||
tokens = callbacks.tokenize(text)
|
||||
allTokens = commands + tokens
|
||||
Owner = irc.getCallback('Owner')
|
||||
Owner.processTokens(irc, msg, allTokens)
|
||||
|
||||
self.Proxy(irc, msg, allTokens)
|
||||
|
||||
|
||||
Class = Utilities
|
||||
|
||||
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:
|
||||
|
25
src/Misc.py
25
src/Misc.py
@ -243,29 +243,14 @@ class Misc(callbacks.Privmsg):
|
||||
command = callbacks.canonicalName(name)
|
||||
# Users might expect "@help @list" to work.
|
||||
# command = command.lstrip(conf.supybot.reply.whenAddressedBy.chars())
|
||||
cbs = callbacks.findCallbackForCommand(irc, command)
|
||||
if len(cbs) > 1:
|
||||
tokens = [command]
|
||||
ambiguous = {}
|
||||
Owner = irc.getCallback('Owner')
|
||||
Owner.disambiguate(irc, tokens, ambiguous)
|
||||
if ambiguous:
|
||||
names = [cb.name() for cb in cbs]
|
||||
names.sort()
|
||||
cbs = irc.findCallbackForCommand(command)
|
||||
if not cbs:
|
||||
irc.error('There is no such command %s.' % command)
|
||||
elif len(cbs) > 1:
|
||||
names = sorted([cb.name() for cb in cbs])
|
||||
irc.error('That command exists in the %s plugins. '
|
||||
'Please specify exactly which plugin command '
|
||||
'you want help with.'% utils.commaAndify(names))
|
||||
return
|
||||
else:
|
||||
if len(tokens) == 1:
|
||||
# It's a src plugin that wasn't disambiguated.
|
||||
tokens.append(tokens[0])
|
||||
assert len(tokens) == 2, tokens
|
||||
cb = irc.getCallback(tokens[0])
|
||||
method = getattr(cb, tokens[1])
|
||||
getHelp(method)
|
||||
elif not cbs:
|
||||
irc.error('There is no such command %s.' % command)
|
||||
else:
|
||||
cb = cbs[0]
|
||||
method = getattr(cb, command)
|
||||
|
97
src/Owner.py
97
src/Owner.py
@ -113,18 +113,23 @@ def loadPluginClass(irc, module, register=None):
|
||||
'instantiated. If you didn\'t write this ' \
|
||||
'plugin, but received it with Supybot, file ' \
|
||||
'a bug with us about this error. %s.' % e
|
||||
name = cb.name()
|
||||
plugin = cb.name()
|
||||
public = True
|
||||
if hasattr(cb, 'public'):
|
||||
public = cb.public
|
||||
conf.registerPlugin(name, register, public)
|
||||
assert not irc.getCallback(name)
|
||||
conf.registerPlugin(plugin, register, public)
|
||||
assert not irc.getCallback(plugin)
|
||||
try:
|
||||
# XXX We should first register the rename plugin.
|
||||
renames = conf.supybot.commands.renames.get(name)
|
||||
for (name, v) in renames.getValues(fullNames=False):
|
||||
renames = registerRename(plugin)()
|
||||
if renames:
|
||||
for command in renames:
|
||||
v = registerRename(plugin, command)
|
||||
newName = v()
|
||||
renameCommand(cb, name, newName)
|
||||
assert newName
|
||||
renameCommand(cb, command, newName)
|
||||
else:
|
||||
conf.supybot.commands.renames.unregister(plugin)
|
||||
except registry.NonExistentRegistryEntry, e:
|
||||
pass # The plugin isn't there.
|
||||
irc.addCallback(cb)
|
||||
@ -138,10 +143,6 @@ conf.supybot.plugins.Owner.register('public', registry.Boolean(True,
|
||||
# supybot.commands.
|
||||
###
|
||||
|
||||
conf.registerGroup(conf.supybot.commands, 'defaultPlugins',
|
||||
orderAlphabetically=True, help=utils.normalizeWhitespace("""Determines
|
||||
what commands have default plugins set, and which plugins are set to
|
||||
be the default for each of those commands."""))
|
||||
conf.registerGroup(conf.supybot.commands, 'renames', orderAlphabetically=True)
|
||||
|
||||
def registerDefaultPlugin(command, plugin):
|
||||
@ -151,13 +152,20 @@ def registerDefaultPlugin(command, plugin):
|
||||
# This must be set, or the quotes won't be removed.
|
||||
conf.supybot.commands.defaultPlugins.get(command).set(plugin)
|
||||
|
||||
def registerRename(plugin, command, newName):
|
||||
def registerRename(plugin, command=None, newName=None):
|
||||
# XXX renames.plugin should have a list of the renames, so they can be
|
||||
# registered from that value.
|
||||
g = conf.registerGroup(conf.supybot.commands.renames, plugin)
|
||||
v = conf.registerGlobalValue(g, command, registry.String(newName, ''))
|
||||
g = conf.registerGlobalValue(conf.supybot.commands.renames, plugin,
|
||||
registry.SpaceSeparatedSetOfStrings([], """Determines what commands
|
||||
in this plugin are to be renamed."""))
|
||||
if command is not None:
|
||||
g().add(command)
|
||||
v = conf.registerGlobalValue(g, command, registry.String('', ''))
|
||||
if newName is not None:
|
||||
v.setValue(newName) # In case it was already registered.
|
||||
return v
|
||||
else:
|
||||
return g
|
||||
|
||||
def renameCommand(cb, name, newName):
|
||||
assert not hasattr(cb, newName), 'Cannot rename over existing attributes.'
|
||||
@ -333,63 +341,7 @@ class Owner(privmsgs.CapabilityCheckingPrivmsg):
|
||||
name, e)
|
||||
world.starting = False
|
||||
|
||||
def disambiguate(self, irc, tokens, ambiguousCommands=None):
|
||||
"""Disambiguates the given tokens based on the plugins loaded and
|
||||
commands available in the given irc. Returns a dictionary of
|
||||
ambiguous commands, mapping the command to the plugins it's
|
||||
available in."""
|
||||
if ambiguousCommands is None:
|
||||
ambiguousCommands = {}
|
||||
if tokens:
|
||||
command = callbacks.canonicalName(tokens[0])
|
||||
try:
|
||||
plugin = conf.supybot.commands.defaultPlugins.get(command)()
|
||||
if plugin and plugin != '(Unused)':
|
||||
tokens.insert(0, plugin)
|
||||
else:
|
||||
raise registry.NonExistentRegistryEntry
|
||||
except registry.NonExistentRegistryEntry:
|
||||
cbs = callbacks.findCallbackForCommand(irc, command)
|
||||
if len(cbs) > 1:
|
||||
names = [cb.name() for cb in cbs]
|
||||
srcs = [name for name in names if name in self._srcPlugins]
|
||||
if len(srcs) == 1:
|
||||
if callbacks.canonicalName(name) != command:
|
||||
# We don't insert the dispatcher name here because
|
||||
# it's handled later. Man, this stuff is a mess.
|
||||
tokens.insert(0, srcs[0])
|
||||
elif command not in map(callbacks.canonicalName, names):
|
||||
ambiguousCommands[command] = names
|
||||
for elt in tokens:
|
||||
if isinstance(elt, list):
|
||||
self.disambiguate(irc, elt, ambiguousCommands)
|
||||
return ambiguousCommands
|
||||
|
||||
def ambiguousError(self, irc, msg, ambiguousCommands):
|
||||
if len(ambiguousCommands) == 1: # Common case.
|
||||
(command, names) = ambiguousCommands.popitem()
|
||||
names.sort()
|
||||
s = 'The command %r is available in the %s plugins. ' \
|
||||
'Please specify the plugin whose command you ' \
|
||||
'wish to call by using its name as a command ' \
|
||||
'before calling it.' % \
|
||||
(command, utils.commaAndify(names))
|
||||
else:
|
||||
L = []
|
||||
for (command, names) in ambiguousCommands.iteritems():
|
||||
names.sort()
|
||||
L.append('The command %r is available in the %s '
|
||||
'plugins' %
|
||||
(command, utils.commaAndify(names)))
|
||||
s = '%s; please specify from which plugins to ' \
|
||||
'call these commands.' % '; '.join(L)
|
||||
irc.queueMsg(callbacks.error(msg, s))
|
||||
|
||||
def processTokens(self, irc, msg, tokens):
|
||||
ambiguousCommands = self.disambiguate(irc, tokens)
|
||||
if ambiguousCommands:
|
||||
self.ambiguousError(irc, msg, ambiguousCommands)
|
||||
else:
|
||||
callbacks.IrcObjectProxy(irc, msg, tokens)
|
||||
|
||||
def do376(self, irc, msg):
|
||||
@ -421,14 +373,9 @@ class Owner(privmsgs.CapabilityCheckingPrivmsg):
|
||||
if ignored:
|
||||
self.log.info('Ignoring command from %s.' % msg.prefix)
|
||||
return
|
||||
brackets = conf.supybot.reply.brackets.get(msg.args[0])()
|
||||
brackets = conf.get(conf.supybot.reply.brackets, msg.args[0])
|
||||
try:
|
||||
tokens = callbacks.tokenize(s, brackets=brackets)
|
||||
if tokens and isinstance(tokens[0], list):
|
||||
s = 'The command called may not be the result ' \
|
||||
'of a nested command.'
|
||||
irc.queueMsg(callbacks.error(msg, s))
|
||||
return
|
||||
self.processTokens(irc, msg, tokens)
|
||||
except SyntaxError, e:
|
||||
irc.queueMsg(callbacks.error(msg, str(e)))
|
||||
|
@ -246,7 +246,6 @@ class Tokenizer(object):
|
||||
|
||||
def _insideBrackets(self, lexer):
|
||||
ret = []
|
||||
firstToken = True
|
||||
while True:
|
||||
token = lexer.get_token()
|
||||
if not token:
|
||||
@ -258,11 +257,6 @@ class Tokenizer(object):
|
||||
elif token == self.right:
|
||||
return ret
|
||||
elif token == self.left:
|
||||
if firstToken:
|
||||
s = 'The command called may not be the result ' \
|
||||
'of a nested command.'
|
||||
raise SyntaxError, 'The command called may not be the ' \
|
||||
'result or a nested command.'
|
||||
ret.append(self._insideBrackets(lexer))
|
||||
else:
|
||||
ret.append(self._handleToken(token))
|
||||
@ -595,13 +589,47 @@ class IrcObjectProxy(RichReplyMethods):
|
||||
finally:
|
||||
self.commandMethod = None
|
||||
|
||||
def findCallbackForCommand(self, command):
|
||||
cbs = findCallbackForCommand(self, command)
|
||||
if len(cbs) > 1:
|
||||
command = canonicalName(command)
|
||||
# Check for whether it's the name of a callback; their dispatchers
|
||||
# need to get precedence.
|
||||
for cb in cbs:
|
||||
if canonicalName(cb.name()) == command:
|
||||
return [cb]
|
||||
try:
|
||||
# Check if there's a configured defaultPlugin -- the user gets
|
||||
# precedence in that case.
|
||||
defaultPlugins = conf.supybot.commands.defaultPlugins
|
||||
plugin = defaultPlugins.get(command)()
|
||||
if plugin and plugin != '(Unused)':
|
||||
cb = self.irc.getCallback(plugin)
|
||||
if cb is None:
|
||||
log.warning('%s is set as a default plugin '
|
||||
'for %s, but it isn\'t loaded.',
|
||||
plugin, command)
|
||||
raise registry.NonExistentRegistryEntry
|
||||
else:
|
||||
return [cb]
|
||||
except registry.NonExistentRegistryEntry, e:
|
||||
# Check for whether it's a src/ plugin; they get precedence.
|
||||
for cb in cbs:
|
||||
important = []
|
||||
importantPlugins = defaultPlugins.importantPlugins()
|
||||
if cb.name() in importantPlugins:
|
||||
# We do this to handle multiple importants matching.
|
||||
important.append(cb)
|
||||
if len(important) == 1:
|
||||
return important
|
||||
return cbs
|
||||
|
||||
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:
|
||||
command = self.args[0]
|
||||
cbs = self.findCallbackForCommand(command)
|
||||
if not cbs:
|
||||
# Normal command not found, let's go for the specialties now.
|
||||
# First, check for addressedRegexps -- they take precedence over
|
||||
# tokenizedCommands.
|
||||
@ -634,28 +662,20 @@ class IrcObjectProxy(RichReplyMethods):
|
||||
return
|
||||
# No matching regexp commands, now we do invalidCommands.
|
||||
self._callInvalidCommands()
|
||||
elif len(cbs) > 1:
|
||||
self.error('The command %s is available in the %s plugins. '
|
||||
'Please specify the plugin whose command you wish '
|
||||
'to call by using its name as a command before %s.' %
|
||||
(command, sorted([cb.name() for cb in cbs]), command))
|
||||
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:
|
||||
del self.args[0]
|
||||
break
|
||||
else:
|
||||
# This should've been caught earlier, that's why we
|
||||
# assert instead of raising a ValueError or something.
|
||||
assert False,'Non-disambiguated command: args=%r'%self.args
|
||||
else:
|
||||
del self.args[0]
|
||||
cb = cbs[0]
|
||||
del self.args[0] # Remove the command.
|
||||
if cb.threaded or conf.supybot.debug.threadAllCommands():
|
||||
t = CommandThread(target=self._callCommand,
|
||||
args=(name, cb))
|
||||
args=(command, cb))
|
||||
t.start()
|
||||
else:
|
||||
self._callCommand(name, cb)
|
||||
self._callCommand(command, cb)
|
||||
|
||||
def reply(self, s, noLengthCheck=False, prefixName=None,
|
||||
action=None, private=None, notice=None, to=None, msg=None):
|
||||
|
13
src/conf.py
13
src/conf.py
@ -513,6 +513,19 @@ registerGlobalValue(supybot, 'flush',
|
||||
# supybot.commands. For stuff relating to commands.
|
||||
###
|
||||
registerGroup(supybot, 'commands')
|
||||
registerGroup(supybot.commands, 'defaultPlugins',
|
||||
orderAlphabetically=True, help=utils.normalizeWhitespace("""Determines
|
||||
what commands have default plugins set, and which plugins are set to
|
||||
be the default for each of those commands."""))
|
||||
registerGlobalValue(supybot.commands.defaultPlugins, 'importantPlugins',
|
||||
registry.SpaceSeparatedSetOfStrings(['Admin', 'Channel', 'Config', 'Misc',
|
||||
'Owner', 'User'], """Determines what
|
||||
plugins automatically get precedence over all other plugins when selecting
|
||||
a default plugin for a command. By default, this includes the standard
|
||||
loaded plugins. You probably shouldn't change this if you don't know what
|
||||
you're doing; if you do know what you're doing, then also know that this
|
||||
set is case-sensitive."""))
|
||||
|
||||
# supybot.commands.disabled moved to callbacks for canonicalName.
|
||||
|
||||
|
||||
|
@ -92,7 +92,6 @@ class TokenizerTestCase(SupyTestCase):
|
||||
def testError(self):
|
||||
self.assertRaises(SyntaxError, tokenize, '[foo') #]
|
||||
self.assertRaises(SyntaxError, tokenize, '"foo') #"
|
||||
self.assertRaises(SyntaxError, tokenize, '[[foo]]')
|
||||
|
||||
def testPipe(self):
|
||||
try:
|
||||
|
Loading…
Reference in New Issue
Block a user