move recursion stuff out of Module.py and into modules/Alias.py

this comes with a recursion rewrite and simplification; it works
a bit more intuitively, now, but i still haven't figured out what
caused the bug that led me down this rabbit hole.

in any event, Alias now rules the roost when it comes to recursion,
which means it's a bit poorly named, but also means that there
won't (shouldn't) be multiple accidental runs for one output, caused
by multiple modules doing the recursion (despite only one really
having a legitimate reason to) --- a classic case of the intended
use of recursion (including the comment to only recurse when you
*know* the input is for you) being lost and forgotten with time

this also obsoletes meta.skip_recursion_scan. between this commit
and the last, people would be wise to revise their config files
This commit is contained in:
Brian S. Stephan 2010-12-16 23:43:16 -06:00
parent d18b2e49ff
commit 1fe9575502
2 changed files with 49 additions and 65 deletions

View File

@ -151,15 +151,6 @@ class Module(object):
elif strip_bot_name_from_input:
what = addressed_re.sub('', what)
skip_recursion_scan = False
try:
skip_recursion_scan = self.config.getboolean(self.__class__.__name__, 'meta.skip_recursion_scan')
except NoOptionError: pass
except NoSectionError: pass
if replypath is not None and skip_recursion_scan is False:
what = self.try_recursion(connection, event, nick, userhost, replypath, what, admin_unlocked)
self.do(connection, event, nick, userhost, replypath, what, admin_unlocked)
def on_privmsg(self, connection, event):
@ -192,9 +183,6 @@ class Module(object):
if internal_only and replypath is not None:
return
if replypath is not None:
what = self.try_recursion(connection, event, nick, userhost, replypath, what, admin_unlocked)
self.do(connection, event, nick, userhost, replypath, what, admin_unlocked)
def reload(self):
@ -230,52 +218,6 @@ class Module(object):
else:
connection.privmsg(replypath, replystr)
def try_recursion(self, connection, event, nick, userhost, replypath, what, admin_unlocked):
"""
Scan message for subcommands to execute and use as part of this command.
Upon seeing a line intended for this module, see if there are subcommands
that we should do what is basically a text replacement on. The intent is to
allow things like the following:
command arg1 [anothercommand arg1 arg2]
where the output of anothercommand is command's arg2..n. It's mostly for
amusement purposes, but maybe there are legitimate uses. This is intended to
be attempted after you've determined the line should be handled by your module.
"""
start_idx = what.find('[')
subcmd = what[start_idx+1:]
end_idx = subcmd.rfind(']')
subcmd = subcmd[:end_idx]
attempt = what
if start_idx == -1 or end_idx == -1 or len(subcmd) == 0:
# no nested commands at all if replypath is a real value, so don't do a damn thing
if replypath is not None:
return attempt
# no more replacements found, see if what we had is workable
else:
for module in self.modlist:
ret = module.do(connection, event, nick, userhost, None, attempt, admin_unlocked)
if ret is not None:
return ret
# if we got here, it's not workable. just return what we got
return attempt
else:
# we have a subcmd, see if there's another one nested
ret = self.try_recursion(connection, event, nick, userhost, None, subcmd, admin_unlocked)
if ret is not None:
blarg = attempt.replace('['+subcmd+']', ret)
if replypath is not None:
return blarg
else:
return self.try_recursion(connection, event, nick, userhost, None, blarg, admin_unlocked)
else:
return attempt
def remove_metaoptions(self, list):
"""Remove metaoptions from provided list, which was probably from a config file."""
@ -293,10 +235,6 @@ class Module(object):
self.config.get(self.__class__.__name__, 'meta.strip_bot_name_from_input')
list.remove('meta.strip_bot_name_from_input')
except NoOptionError: pass
try:
self.config.get(self.__class__.__name__, 'meta.skip_recursion_scan')
list.remove('meta.skip_recursion_scan')
except NoOptionError: pass
def retransmit_event(self, event):
"""

View File

@ -34,6 +34,13 @@ class Alias(Module):
an internal bot command and run it.
"""
# search for recursions, which will search for recursions, which ...
what = self.try_recursion(connection, event, nick, userhost, None, what, admin_unlocked)
# done searching for recursions in this level. we will now operate on
# whatever recursion-satisfied string we have, checking for alias and
# running module commands
# first see if the aliases are being directly manipulated via add/remove
whats = what.split(' ')
try:
@ -71,12 +78,51 @@ class Alias(Module):
alias_re = re.compile(alias)
if alias_re.search(what):
command = re.sub(alias, self.config.get(self.__class__.__name__, alias), what)
reply = self.try_recursion(connection, event, nick, userhost, None, command, admin_unlocked)
if not reply == command:
return self.reply(connection, replypath, reply)
# we found an alias for our given string, doing a replace and running it against
# each module
for module in self.modlist:
ret = module.do(connection, event, nick, userhost, replypath, command, admin_unlocked)
if ret is not None:
# a module had a result for us, post-alias, so return it
# TODO: scan all modules with compounding results
return self.reply(connection, replypath, ret)
except NoOptionError: pass
except NoSectionError: pass
def try_recursion(self, connection, event, nick, userhost, replypath, what, admin_unlocked):
"""
Scan message for subcommands to execute and use as part of this command.
Upon seeing a line intended for this module, see if there are subcommands
that we should do what is basically a text replacement on. The intent is to
allow things like the following:
command arg1 [anothercommand arg1 arg2]
where the output of anothercommand is command's arg2..n.
"""
start_idx = what.find('[')
subcmd = what[start_idx+1:]
end_idx = subcmd.rfind(']')
subcmd = subcmd[:end_idx]
attempt = what
if start_idx == -1 or end_idx == -1 or len(subcmd) == 0:
# no alias, just returning what we got
return attempt
else:
# we have a subcmd, see if there's another one nested
# this will include more calls to Alias, which will try recursing again
for module in self.modlist:
ret = module.do(connection, event, nick, userhost, None, subcmd, admin_unlocked)
if ret is not None:
# some module had a change, so we replace [subcmd] with ret and return it
return attempt.replace('['+subcmd+']', ret)
# we got here, no one had a replacement. return what we had
return attempt
# vi:tabstop=4:expandtab:autoindent
# kate: indent-mode python;indent-width 4;replace-tabs on;