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

View File

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

View File

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

View File

@ -230,15 +230,19 @@ class Tokenizer(object):
# double-quote, left-bracket, and right-bracket. # double-quote, left-bracket, and right-bracket.
validChars = string.ascii.translate(string.ascii, '\x00\r\n \t"') validChars = string.ascii.translate(string.ascii, '\x00\r\n \t"')
quotes = '"' quotes = '"'
def __init__(self, tokens=''): def __init__(self, brackets='', pipe=False):
# Add a '|' to tokens to have the pipe syntax. if brackets:
self.validChars = self.validChars.translate(string.ascii, tokens) self.validChars = self.validChars.translate(string.ascii, brackets)
if len(tokens) >= 2: self.left = brackets[0]
self.left = tokens[0] self.right = brackets[1]
self.right = tokens[1]
else: else:
self.left = '' self.left = ''
self.right = '' self.right = ''
self.pipe = pipe
if self.pipe:
self.validChars = self.validChars.translate(string.ascii, '|')
else:
assert '|' in self.validChars
def _handleToken(self, token): def _handleToken(self, token):
if token[0] == token[-1] and token[0] in self.quotes: if token[0] == token[-1] and token[0] in self.quotes:
@ -276,7 +280,10 @@ class Tokenizer(object):
token = lexer.get_token() token = lexer.get_token()
if not token: if not token:
break 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: if not args:
raise SyntaxError, '"|" with nothing preceding. I ' \ raise SyntaxError, '"|" with nothing preceding. I ' \
'obviously can\'t do a pipe with ' \ 'obviously can\'t do a pipe with ' \
@ -303,18 +310,20 @@ class Tokenizer(object):
args[-1].append(ends.pop()) args[-1].append(ends.pop())
return args 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.""" """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() start = time.time()
try: try:
if brackets is None: ret = Tokenizer(brackets=brackets, pipe=pipe).tokenize(s)
tokens = conf.get(conf.supybot.reply.brackets, channel)
else:
tokens = brackets
if conf.get(conf.supybot.reply.pipeSyntax, channel):
tokens = '%s|' % tokens
log.stat('tokenize took %s seconds.' % (time.time() - start)) log.stat('tokenize took %s seconds.' % (time.time() - start))
return Tokenizer(tokens).tokenize(s) return ret
except ValueError, e: except ValueError, e:
raise SyntaxError, str(e) raise SyntaxError, str(e)
@ -496,11 +505,21 @@ _repr = repr
class IrcObjectProxy(RichReplyMethods): class IrcObjectProxy(RichReplyMethods):
"A proxy object to allow proper nested of commands (even threaded ones)." "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) log.debug('IrcObjectProxy.__init__: %s' % args)
self.irc = irc self.irc = irc
self.msg = msg self.msg = msg
self.nested = nested 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 # The deepcopy here is necessary for Scheduler; it re-runs already
# tokenized commands. # tokenized commands.
self.args = copy.deepcopy(args) self.args = copy.deepcopy(args)
@ -539,7 +558,7 @@ class IrcObjectProxy(RichReplyMethods):
self.counter += 1 self.counter += 1
else: else:
self.__class__(self, self.msg, self.__class__(self, self.msg,
self.args[self.counter], nested=True) self.args[self.counter], nested=self.nested+1)
return return
self.finalEval() 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 safety purposes (so the bot is less likely to flood) it will normally send
everything in a single message, using mores if necessary.""")) 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', registerChannelValue(supybot.reply, 'whenNotCommand',
registry.Boolean(True, """Determines whether the bot will reply with an 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 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. # supybot.commands. For stuff relating to commands.
### ###
registerGroup(supybot, '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', registerGroup(supybot.commands, 'defaultPlugins',
orderAlphabetically=True, help=utils.normalizeWhitespace("""Determines orderAlphabetically=True, help=utils.normalizeWhitespace("""Determines
what commands have default plugins set, and which plugins are set to 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]'), self.assertEqual(tokenize('foo bar [baz quux]'),
['foo', 'bar', ['baz', 'quux']]) ['foo', 'bar', ['baz', 'quux']])
try: try:
orig = conf.supybot.reply.brackets() orig = conf.supybot.commands.nested()
conf.supybot.reply.brackets.setValue('') conf.supybot.commands.nested.setValue(False)
self.assertEqual(tokenize('[]'), ['[]']) self.assertEqual(tokenize('[]'), ['[]'])
self.assertEqual(tokenize('[foo]'), ['[foo]']) self.assertEqual(tokenize('[foo]'), ['[foo]'])
self.assertEqual(tokenize('foo [bar]'), ['foo', '[bar]']) self.assertEqual(tokenize('foo [bar]'), ['foo', '[bar]'])
self.assertEqual(tokenize('foo bar [baz quux]'), self.assertEqual(tokenize('foo bar [baz quux]'),
['foo', 'bar', '[baz', 'quux]']) ['foo', 'bar', '[baz', 'quux]'])
finally: finally:
conf.supybot.reply.brackets.setValue(orig) conf.supybot.commands.nested.setValue(orig)
def testError(self): def testError(self):
self.assertRaises(SyntaxError, tokenize, '[foo') #] self.assertRaises(SyntaxError, tokenize, '[foo') #]
@ -95,7 +95,7 @@ class TokenizerTestCase(SupyTestCase):
def testPipe(self): def testPipe(self):
try: try:
conf.supybot.reply.pipeSyntax.set('True') conf.supybot.commands.nested.pipeSyntax.setValue(True)
self.assertRaises(SyntaxError, tokenize, '| foo') self.assertRaises(SyntaxError, tokenize, '| foo')
self.assertRaises(SyntaxError, tokenize, 'foo ||bar') self.assertRaises(SyntaxError, tokenize, 'foo ||bar')
self.assertRaises(SyntaxError, tokenize, 'bar |') self.assertRaises(SyntaxError, tokenize, 'bar |')
@ -110,7 +110,7 @@ class TokenizerTestCase(SupyTestCase):
self.assertEqual(tokenize('foo bar | baz quux'), self.assertEqual(tokenize('foo bar | baz quux'),
['baz', 'quux', ['foo', 'bar']]) ['baz', 'quux', ['foo', 'bar']])
finally: 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'), ['foo', '|', 'bar']) self.assertEqual(tokenize('foo | bar'), ['foo', '|', 'bar'])
self.assertEqual(tokenize('foo | bar | baz'), self.assertEqual(tokenize('foo | bar | baz'),