Various minor refactorings, moved supybot.reply.{brackets,pipeSyntax} to supybot.commands.nested.

This commit is contained in:
Jeremy Fincher 2004-09-23 23:15:27 +00:00
parent ab21fc54cf
commit 3e58419338
6 changed files with 72 additions and 47 deletions

View File

@ -120,7 +120,6 @@ class Observer(callbacks.Privmsg):
self.commandCalled = False
return
channel = msg.args[0]
Owner = irc.getCallback('Owner')
observers = self.registryValue('observers')
active = self.registryValue('observers.active', channel)
for name in active:
@ -141,7 +140,7 @@ class Observer(callbacks.Privmsg):
for (i, group) in enumerate(groups):
command = command.replace('$%s' % i, group)
tokens = callbacks.tokenize(command, channel=channel)
Owner.processTokens(irc, msg, tokens)
self.Proxy(irc, msg, tokens)
def list(self, irc, msg, args):
"""[<channel>]

View File

@ -115,7 +115,8 @@ class Misc(callbacks.Privmsg):
# to log this in that case anyway, it being a nested command.
self.log.info('Not replying to %s, not a command.' % tokens[0])
if not isinstance(irc.irc, irclib.Irc):
brackets = conf.get(conf.supybot.reply.brackets, channel)
bracketConfig = conf.supybot.commands.nested.brackets
brackets = conf.get(bracketConfig, channel)
if brackets:
(left, right) = brackets
irc.reply(left + ' '.join(tokens) + right)

View File

@ -338,9 +338,6 @@ class Owner(privmsgs.CapabilityCheckingPrivmsg):
name, e)
world.starting = False
def processTokens(self, irc, msg, tokens):
callbacks.IrcObjectProxy(irc, msg, tokens)
def do376(self, irc, msg):
channels = ircutils.IrcSet(conf.supybot.channels())
channels |= conf.supybot.networks.get(irc.network).channels()
@ -370,10 +367,9 @@ class Owner(privmsgs.CapabilityCheckingPrivmsg):
if ignored:
self.log.info('Ignoring command from %s.' % msg.prefix)
return
brackets = conf.get(conf.supybot.reply.brackets, msg.args[0])
try:
tokens = callbacks.tokenize(s, brackets=brackets)
self.processTokens(irc, msg, tokens)
tokens = callbacks.tokenize(s, channel=msg.args[0])
self.Proxy(irc, msg, tokens)
except SyntaxError, e:
irc.queueMsg(callbacks.error(msg, str(e)))

View File

@ -230,15 +230,19 @@ class Tokenizer(object):
# double-quote, left-bracket, and right-bracket.
validChars = string.ascii.translate(string.ascii, '\x00\r\n \t"')
quotes = '"'
def __init__(self, tokens=''):
# Add a '|' to tokens to have the pipe syntax.
self.validChars = self.validChars.translate(string.ascii, tokens)
if len(tokens) >= 2:
self.left = tokens[0]
self.right = tokens[1]
def __init__(self, brackets='', pipe=False):
if brackets:
self.validChars = self.validChars.translate(string.ascii, brackets)
self.left = brackets[0]
self.right = brackets[1]
else:
self.left = ''
self.right = ''
self.pipe = pipe
if self.pipe:
self.validChars = self.validChars.translate(string.ascii, '|')
else:
assert '|' in self.validChars
def _handleToken(self, token):
if token[0] == token[-1] and token[0] in self.quotes:
@ -276,7 +280,10 @@ class Tokenizer(object):
token = lexer.get_token()
if not token:
break
elif token == '|' and conf.supybot.reply.pipeSyntax():
elif token == '|' and self.pipe:
# The "and self.pipe" might seem redundant here, but it's there
# for strings like 'foo | bar', where a pipe stands alone as a
# token, but shouldn't be treated specially.
if not args:
raise SyntaxError, '"|" with nothing preceding. I ' \
'obviously can\'t do a pipe with ' \
@ -303,18 +310,20 @@ class Tokenizer(object):
args[-1].append(ends.pop())
return args
def tokenize(s, brackets=None, channel=None):
def tokenize(s, channel=None):
"""A utility function to create a Tokenizer and tokenize a string."""
pipe = False
brackets = ''
nested = conf.supybot.commands.nested
if nested():
brackets = conf.get(nested.brackets, channel)
if conf.get(nested.pipeSyntax, channel): # No nesting, no pipe.
pipe = True
start = time.time()
try:
if brackets is None:
tokens = conf.get(conf.supybot.reply.brackets, channel)
else:
tokens = brackets
if conf.get(conf.supybot.reply.pipeSyntax, channel):
tokens = '%s|' % tokens
ret = Tokenizer(brackets=brackets, pipe=pipe).tokenize(s)
log.stat('tokenize took %s seconds.' % (time.time() - start))
return Tokenizer(tokens).tokenize(s)
return ret
except ValueError, e:
raise SyntaxError, str(e)
@ -496,11 +505,21 @@ _repr = repr
class IrcObjectProxy(RichReplyMethods):
"A proxy object to allow proper nested of commands (even threaded ones)."
def __init__(self, irc, msg, args, nested=False):
def __init__(self, irc, msg, args, nested=0):
log.debug('IrcObjectProxy.__init__: %s' % args)
self.irc = irc
self.msg = msg
self.nested = nested
if not self.nested and isinstance(irc, self.__class__):
# This is for plugins that indirectly spawn a Proxy, like Alias.
self.nested += irc.nested
maxNesting = conf.supybot.commands.nested.maximum()
if maxNesting and self.nested > maxNesting:
log.warning('%s attempted more than %s levels of nesting.',
self.msg.prefix, maxNesting)
self.error('You\'ve attempted more nesting than is currently '
'allowed on this bot.')
return
# The deepcopy here is necessary for Scheduler; it re-runs already
# tokenized commands.
self.args = copy.deepcopy(args)
@ -539,7 +558,7 @@ class IrcObjectProxy(RichReplyMethods):
self.counter += 1
else:
self.__class__(self, self.msg,
self.args[self.counter], nested=True)
self.args[self.counter], nested=self.nested+1)
return
self.finalEval()

View File

@ -288,22 +288,6 @@ registerGlobalValue(supybot.reply, 'oneToOne',
safety purposes (so the bot is less likely to flood) it will normally send
everything in a single message, using mores if necessary."""))
class ValidBrackets(registry.OnlySomeStrings):
validStrings = ('', '[]', '<>', '{}', '()')
registerChannelValue(supybot.reply, 'brackets',
ValidBrackets('[]', """Supybot allows you to specify what brackets are used
for your nested commands. Valid sets of brackets include [], <>, and {}
(). [] has strong historical motivation, as well as being the brackets
that don't require shift. <> or () might be slightly superior because they
cannot occur in a nick. If this value is set to the empty string, no
nesting will be allowed."""))
registerChannelValue(supybot.reply, 'pipeSyntax',
registry.Boolean(False, """Supybot allows nested commands. Enabling this
option will allow nested commands with a syntax similar to UNIX pipes, for
example: 'bot: foo | bar'."""))
registerChannelValue(supybot.reply, 'whenNotCommand',
registry.Boolean(True, """Determines whether the bot will reply with an
error message when it is addressed but not given a valid command. If this
@ -518,6 +502,32 @@ registerGlobalValue(supybot, 'flush',
# supybot.commands. For stuff relating to commands.
###
registerGroup(supybot, 'commands')
# This is a GlobalValue because bot owners should be able to say, "There will
# be no nesting at all on this bot." Individual channels can just set their
# brackets to the empty string.
registerGlobalValue(supybot.commands, 'nested',
registry.Boolean(True, """Determines whether the bot will allow nested
commands, which rule. You definitely should keep this on."""))
registerGlobalValue(supybot.commands.nested, 'maximum',
registry.PositiveInteger(10, """Determines what the maximum number of
nested commands will be; users will receive an error if they attempt
commands more nested than this."""))
class ValidBrackets(registry.OnlySomeStrings):
validStrings = ('', '[]', '<>', '{}', '()')
registerChannelValue(supybot.commands.nested, 'brackets',
ValidBrackets('[]', """Supybot allows you to specify what brackets are used
for your nested commands. Valid sets of brackets include [], <>, and {}
(). [] has strong historical motivation, as well as being the brackets
that don't require shift. <> or () might be slightly superior because they
cannot occur in a nick. If this string is empty, nested commands will
not be allowed in this channel."""))
registerChannelValue(supybot.commands.nested, 'pipeSyntax',
registry.Boolean(False, """Supybot allows nested commands. Enabling this
option will allow nested commands with a syntax similar to UNIX pipes, for
example: 'bot: foo | bar'."""))
registerGroup(supybot.commands, 'defaultPlugins',
orderAlphabetically=True, help=utils.normalizeWhitespace("""Determines
what commands have default plugins set, and which plugins are set to

View File

@ -79,15 +79,15 @@ class TokenizerTestCase(SupyTestCase):
self.assertEqual(tokenize('foo bar [baz quux]'),
['foo', 'bar', ['baz', 'quux']])
try:
orig = conf.supybot.reply.brackets()
conf.supybot.reply.brackets.setValue('')
orig = conf.supybot.commands.nested()
conf.supybot.commands.nested.setValue(False)
self.assertEqual(tokenize('[]'), ['[]'])
self.assertEqual(tokenize('[foo]'), ['[foo]'])
self.assertEqual(tokenize('foo [bar]'), ['foo', '[bar]'])
self.assertEqual(tokenize('foo bar [baz quux]'),
['foo', 'bar', '[baz', 'quux]'])
finally:
conf.supybot.reply.brackets.setValue(orig)
conf.supybot.commands.nested.setValue(orig)
def testError(self):
self.assertRaises(SyntaxError, tokenize, '[foo') #]
@ -95,7 +95,7 @@ class TokenizerTestCase(SupyTestCase):
def testPipe(self):
try:
conf.supybot.reply.pipeSyntax.set('True')
conf.supybot.commands.nested.pipeSyntax.setValue(True)
self.assertRaises(SyntaxError, tokenize, '| foo')
self.assertRaises(SyntaxError, tokenize, 'foo ||bar')
self.assertRaises(SyntaxError, tokenize, 'bar |')
@ -110,7 +110,7 @@ class TokenizerTestCase(SupyTestCase):
self.assertEqual(tokenize('foo bar | baz quux'),
['baz', 'quux', ['foo', 'bar']])
finally:
conf.supybot.reply.pipeSyntax.set('False')
conf.supybot.commands.nested.pipeSyntax.setValue(False)
self.assertEqual(tokenize('foo|bar'), ['foo|bar'])
self.assertEqual(tokenize('foo | bar'), ['foo', '|', 'bar'])
self.assertEqual(tokenize('foo | bar | baz'),